提交示例代码

This commit is contained in:
Bob.Song
2026-03-05 11:39:06 +08:00
commit 25958f58c3
2534 changed files with 209593 additions and 0 deletions

View File

@@ -0,0 +1,82 @@
using Fantasy.Entitas.Interface;
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
namespace Fantasy;
public sealed class ContainerComponentDestroySystem : DestroySystem<ContainerComponent>
{
protected override void Destroy(ContainerComponent self)
{
foreach (var (_, container) in self.Containers)
{
container.Dispose();
}
self.Containers.Clear();
}
}
public sealed class ContainerComponentDeserializeSystem : DeserializeSystem<ContainerComponent>
{
protected override void Deserialize(ContainerComponent self)
{
var account = self.GetParent<Account>();
foreach (var (key, container) in self.Containers)
{
container.Deserialize(self.Scene);
container.Account = account;
}
}
}
public sealed class ContainerComponentAwakeSystem : AwakeSystem<ContainerComponent>
{
protected override void Awake(ContainerComponent self)
{
var account = self.GetParent<Account>();
var unitConfig = account.UnitConfig;
// 根据角色的配置文件创建容器。
foreach (var containerConfigId in unitConfig.MountContainer)
{
if (!ContainerConfigData.Instance.TryGet(containerConfigId, out var containerConfig))
{
Log.Error($"containerConfigId : {containerConfigId} not found");
return;
}
ContainerHelper.Create(account, (ContainerType)containerConfig.Type);
}
// 创建初始化物品到容器中。
var unitConfigItems = unitConfig.Items;
for (var itemArgs = 0; itemArgs < unitConfigItems.Length; itemArgs += 3)
{
try
{
var containerType = unitConfigItems[itemArgs];
var itemConfigId = unitConfigItems[itemArgs + 1];
var itemCount = (int)unitConfigItems[itemArgs + 2];
var item = ItemFactory.Create(self.Scene, itemConfigId, itemCount, true);
if (item == null)
{
// 这里创建物品失败,可以选择跳过这个物品,继续下一个创建。
// 也可以直接停止全部的物品创建。
continue;
}
// 添加物品到容器中。
var addItemErrorCode = ContainerHelper.AddItem(account, (ContainerType)containerType, item, ItemReason.ContainerAdd, false);
if (addItemErrorCode != 0)
{
Log.Error($"cant add item to container errorCode:{addItemErrorCode}");
continue;
}
}
catch
{
Log.Error($"unitConfig :{unitConfig.Id} items参数不正确要求是3个一组。");
// 你可以选择如果有一个出错了,跳过、继续下一个创建。
// 你还可以选择如果出错了,直接终止跳出。
break;
}
}
}
}

View File

@@ -0,0 +1,291 @@
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
using Fantasy.Entitas;
using Fantasy.Network;
#pragma warning disable CS8601 // Possible null reference assignment.
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
#pragma warning disable CS8603 // Possible null reference return.
namespace Fantasy;
public static class ContainerHelper
{
#region Container
public static Container Create(Account account, ContainerType containerType)
{
if (!ContainerConfigData.Instance.TryGetConfig(containerType, out var containerConfig))
{
return null;
}
var containerComponent = account.GetComponent<ContainerComponent>();
if (containerComponent == null)
{
Log.Error($"account {account.Id} not found ContainerComponent!");
return null;
}
var containerTypeInt = (int)containerType;
if (containerComponent.Containers.ContainsKey(containerTypeInt))
{
Log.Error($"account {account.Id} containerType {containerType} already exists!");
return null;
}
var container = Entity.Create<Container>(account.Scene, true, true);
container.ConfigId = containerConfig.Id;
container.Account = account;
container.CurrentCellCount = 0;
containerComponent.Containers.Add(containerTypeInt, container);
return container;
}
public static uint TryGetContainer(Account account, ContainerType containerType, out Container container)
{
container = null;
var containerComponent = account.GetComponent<ContainerComponent>();
if (containerComponent == null)
{
// 没有找到容器组件的错误码。
return 1;
}
if (!containerComponent.Containers.TryGetValue((int)containerType, out container))
{
// 在容器组件中没有找到该容器的错误码。
return 2;
}
return 0;
}
#endregion
#region GetItem
public static bool GetItemById(Account account, ContainerType containerType, long id, out Item item)
{
if (TryGetContainer(account, containerType, out var container) != 0)
{
item = null;
return false;
}
return container.GetItemById(id, out item);
}
public static bool GetItemByCell(Account account, ContainerType containerType, long cell, out Item item)
{
if (TryGetContainer(account, containerType, out var container) != 0)
{
item = null;
return false;
}
return container.GetItemByCell(cell, out item);
}
public static void GetItemsByConfigId(Account account, ContainerType containerType, uint configId, List<Item> items)
{
if (TryGetContainer(account, containerType, out var container) != 0)
{
return;
}
container.GetItemsByConfigId(configId, items);
}
public static void GetItemsByType(Account account, ContainerType containerType, ItemType itemType, List<Item> items)
{
if (TryGetContainer(account, containerType, out var container) != 0)
{
return;
}
container.GetItemsByType(itemType, items);
}
public static bool IsFull(Account account, ContainerType containerType)
{
if (TryGetContainer(account, containerType, out var container) != 0)
{
// 这里的可以返回False因为容器组件中可能这个容器。
// 但是也可以返回true是因为这个函数的语义是判断容器已满如果找不到容器也可以认为是满的。
return true;
}
return container.IsFull();
}
public static int GetItemCount(Account account, ContainerType containerType, uint configId)
{
if (TryGetContainer(account, containerType, out var container) != 0)
{
return 0;
}
return container.GetItemCount(configId);
}
#endregion
#region AddItem
public static uint AddItem(Account account, ContainerType containerType, Item item, ItemReason itemReason, bool isSendClient)
{
var tryGetContainer = TryGetContainer(account, containerType, out var container);
if (tryGetContainer != 0)
{
return tryGetContainer;
}
return container.AddItem(item, itemReason, isSendClient);
}
public static uint AddItem(Account account, ContainerType containerType, Item item, long cellId, ItemReason itemReason, bool isSendClient)
{
var tryGetContainer = TryGetContainer(account, containerType, out var container);
if (tryGetContainer != 0)
{
return tryGetContainer;
}
return container.AddItem(item, cellId, itemReason, isSendClient);
}
#endregion
#region SplitItem
public static uint SplitItem(Account account, ContainerType containerType, long itemId, int count, out Item splitItem)
{
var tryGetContainer = TryGetContainer(account, containerType, out var container);
if (tryGetContainer != 0)
{
splitItem = null;
return tryGetContainer;
}
return container.SplitItem(itemId, count, out splitItem);
}
#endregion
#region RemoveItem
public static uint RemoveItem(Account account, ContainerType containerType, long itemId, ItemReason itemReason, bool isSendClient, bool isDispose = true)
{
var tryGetContainer = TryGetContainer(account, containerType, out var container);
if (tryGetContainer != 0)
{
return tryGetContainer;
}
return container.RemoveItem(itemId, itemReason, isSendClient, isDispose);
}
public static uint RemoveItem(Account account, ContainerType containerType, long itemId, int count, ItemReason itemReason, bool isSendClient, bool isDispose = true)
{
var tryGetContainer = TryGetContainer(account, containerType, out var container);
if (tryGetContainer != 0)
{
return tryGetContainer;
}
return container.RemoveItem(itemId, count, itemReason, isSendClient, isDispose);
}
public static uint RemoveItemByCell(Account account, ContainerType containerType, long cellId, ItemReason itemReason, bool isSendClient, bool isDispose = true)
{
var tryGetContainer = TryGetContainer(account, containerType, out var container);
if (tryGetContainer != 0)
{
return tryGetContainer;
}
return container.RemoveItemByCell(cellId, itemReason, isSendClient, isDispose);
}
#endregion
#region Sort
public static void Sort(Account account, ContainerType containerType)
{
if (TryGetContainer(account, containerType, out var container) != 0)
{
return;
}
container.Sort();
}
#endregion
#region ToProto
public static bool TryGetContainerInfo(Account account, ContainerType containerType, out ContainerInfo containerInfo)
{
var tryGetContainer = TryGetContainer(account, containerType, out var container);
if (tryGetContainer != 0)
{
containerInfo = null;
return false;
}
containerInfo = container.ToContainerInfo();
return true;
}
public static void SendAllContainerInfo(Account account)
{
var containerComponent = account.GetComponent<ContainerComponent>();
if (containerComponent == null)
{
Log.Error($"account {account.Id} not found ContainerComponent!");
return;
}
var g2CPushAllContainerInfo = new G2C_PushAllContainerInfo();
foreach (var (_, container) in containerComponent.Containers)
{
g2CPushAllContainerInfo.Containers.Add(container.ToContainerInfo());
}
// 推送容器数据给客户端
account.Session.Send(g2CPushAllContainerInfo);
}
// public static void SendAllContainerInfo(Account account)
// {
// var containerComponent = account.GetComponent<ContainerComponent>();
//
// if (containerComponent == null)
// {
// Log.Error($"account {account.Id} not found ContainerComponent!");
// return;
// }
//
// foreach (var (_, container) in containerComponent.Containers)
// {
// account.Session.Send(new G2C_PushContainerInfo()
// {
// Container = container.ToContainerInfo()
// });
// }
// }
#endregion
}

View File

@@ -0,0 +1,470 @@
using Fantasy.DataStructure.Collection;
using Fantasy.Entitas.Interface;
using Fantasy.Helper;
using Fantasy.Network;
using Fantasy.Network.Interface;
#pragma warning disable CS8602 // Dereference of a possibly null reference.
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
#pragma warning disable CS8601 // Possible null reference assignment.
namespace Fantasy;
public sealed class ContainerDestroySystem : DestroySystem<Container>
{
protected override void Destroy(Container self)
{
self.Account = null;
self.ConfigId = 0;
self.CurrentCellCount = 0;
foreach (var (_, item) in self.Items)
{
item.Dispose();
}
self.Items.Clear();
self.ItemsByCell.Clear();
self.ItemsByConfigId.Clear();
self.ItemsByType.Clear();
}
}
public sealed class ContainerDeserializeSystem : DeserializeSystem<Container>
{
protected override void Deserialize(Container self)
{
foreach (var (_, item) in self.Items)
{
item.Deserialize(self.Scene);
item.Container = self;
if (item.CellId > 0)
{
self.ItemsByCell.Add(item.CellId, item);
}
self.ItemsByConfigId.Add(item.ConfigId, item);
self.ItemsByType.Add(item.Config.Type, item);
}
}
}
public static class ContainerSystem
{
#region Get
public static bool GetItemById(this Container self, long id, out Item item)
{
return self.Items.TryGetValue(id, out item);
}
public static bool GetItemByCell(this Container self, long cell, out Item item)
{
return self.ItemsByCell.TryGetValue(cell, out item);
}
public static void GetItemsByConfigId(this Container self, uint configId, List<Item> items)
{
if (!self.ItemsByConfigId.TryGetValue(configId, out var itemList))
{
return;
}
items.AddRange(itemList);
}
public static void GetItemsByType(this Container self, ItemType itemType, List<Item> items)
{
if (!self.ItemsByType.TryGetValue((uint)itemType, out var itemList))
{
return;
}
items.AddRange(itemList);
}
public static bool IsFull(this Container self)
{
return self.CurrentCellCount >= self.Config.CellCount;
}
public static int GetItemCount(this Container self, uint configId)
{
if (!self.ItemsByConfigId.TryGetValue(configId, out var itemList))
{
return 0;
}
var count = 0;
foreach (var item in itemList)
{
count += item.Count;
}
// return itemList.Sum(x => x.Count);
return count;
}
#endregion
#region Add
public static uint AddItem(this Container self, Item item, ItemReason itemReason, bool isSendClient)
{
var containerConfig = self.Config;
if (!ContainerType.Normal.HasFlag((ContainerType)containerConfig.Type))
{
Log.Error($"{self.GetType().Name} is not normal container");
return 1;
}
var count = item.Count;
var itemConfig = item.Config;
var g2CUpdateItems = new G2C_UpdateItems();
g2CUpdateItems.ItemReason = (int)itemReason;
if (itemConfig.Superposed)
{
var superposedMax = (int)itemConfig.SuperposedMax;
if (self.ItemsByConfigId.TryGetValue(itemConfig.Id, out var itemList))
{
var availableCount = superposedMax * itemList.Count;
// 计算当前物品可以叠加的数量
foreach (var haveItem in itemList)
{
availableCount -= haveItem.Count;
}
// 检查当前容器中是否还有空位。
if (item.Count > availableCount && self.IsFull())
{
// 当前背包已满,无法添加该物品的错误码。
// 无法叠加的数量大于物品的数量,那就表示当前物品不可以叠加到其他物品中了。
return 2;
}
foreach (var haveItem in itemList)
{
var haveItemCount = superposedMax - haveItem.Count;
// 如果当前物品叠加数量已经满了那就直接找下一个同配置Id的物品。
if (haveItemCount <= 0)
{
continue;
}
if (item.Count > haveItemCount)
{
// 如果添加的物品数量大于当前物品的可叠加数量,那就把可叠加的数量减去。
item.Count -= haveItemCount;
}
else
{
// 如果添加的物品数量小于当前物品的可叠加数量,那就表示当前物品可以把添加物品的全部叠加到当前物品中了。
// 这样的话,只需要记录下添加物品的数量就可以了
haveItemCount = item.Count;
item.Count = 0;
}
haveItem.Count += haveItemCount;
g2CUpdateItems.Items.Add(haveItem.ToItemInfo());
if (item.Count <= 0)
{
break;
}
}
}
}
else
{
if (self.IsFull())
{
// 当前背包已满,无法添加该物品的错误码。
return 2;
}
}
if (item.Count <= 0)
{
item.Dispose();
}
else
{
item.Container = self;
self.CurrentCellCount++;
self.Items.Add(item.Id, item);
self.ItemsByType.Add(itemConfig.Type, item);
self.ItemsByConfigId.Add(itemConfig.Id, item);
g2CUpdateItems.Items.Add(item.ToItemInfo());
}
if (isSendClient)
{
self.SendClient(g2CUpdateItems);
}
Log.Debug($"AddItem itemReason:{itemReason} itemConfigId:{itemConfig.Id} Count:{count}");
return 0;
}
public static uint AddItem(this Container self, Item item, long cellId, ItemReason itemReason, bool isSendClient)
{
var containerConfig = self.Config;
if (!ContainerType.Cell.HasFlag((ContainerType)containerConfig.Type))
{
Log.Error($"{self.GetType().Name} is not cell container");
return 1;
}
if (self.IsFull())
{
// 当前背包已满,无法添加该物品的错误码。
return 2;
}
if (self.ItemsByCell.ContainsKey(cellId))
{
// 该Cell已经存在物品了。
return 3;
}
var g2CUpdateItems = new G2C_UpdateItems();
g2CUpdateItems.ItemReason = (int)itemReason;
var itemConfig = item.Config;
var count = item.Count;
item.CellId = cellId;
item.Container = self;
self.CurrentCellCount++;
self.Items.Add(item.Id, item);
self.ItemsByCell.Add(cellId, item);
self.ItemsByType.Add(itemConfig.Type, item);
self.ItemsByConfigId.Add(item.ConfigId, item);
g2CUpdateItems.Items.Add(item.ToItemInfo());
if (isSendClient)
{
self.SendClient(g2CUpdateItems);
}
Log.Debug($"AddItem itemReason:{itemReason} itemConfigId:{itemConfig.Id} Count:{count}");
return 0;
}
#endregion
#region Split
public static uint SplitItem(this Container self, long itemId, int count, out Item splitItem)
{
splitItem = null;
if (count <= 0)
{
// 要拆分的道具数量不可以小于等于0的错误码。
return 2;
}
if (self.Items.TryGetValue(itemId, out var item))
{
// 没有找到物品的错误码。
return 1;
}
if (item.Count == count)
{
// 要拆分的物品数量不可以和物品的数量相同的错误码。
return 3;
}
if (item.Count < count)
{
// 要拆分的物品数量不可以大于物品的数量的错误码。
return 4;
}
item.Count -= count;
splitItem = ItemFactory.Create(self.Scene, item.ConfigId, count, item.IsBind);
return 0;
}
#endregion
#region Remove
public static uint RemoveItem(this Container self, Item item, ItemReason itemReason, bool isSendClient, bool isDispose = true)
{
var count = item.Count;
var itemConfig = item.Config;
self.Items.Remove(item.Id);
self.ItemsByConfigId.RemoveValue(item.ConfigId,item);
self.ItemsByType.RemoveValue(itemConfig.Type, item);
var containerConfig = self.Config;
if (ContainerType.Cell.HasFlag((ContainerType)containerConfig.Type))
{
self.ItemsByCell.Remove(item.CellId);
}
var g2CUpdateItems = new G2C_UpdateItems();
g2CUpdateItems.ItemReason = (int)itemReason;
g2CUpdateItems.Items.Add(item.ToItemInfo());
if (isDispose)
{
item.Dispose();
}
self.CurrentCellCount--;
if (isSendClient)
{
self.SendClient(g2CUpdateItems);
}
Log.Debug($"RemoveItem itemReason:{itemReason} itemConfigId:{itemConfig.Id} Count:{count}");
return 0;
}
public static uint RemoveItem(this Container self, long itemId, ItemReason itemReason, bool isSendClient, bool isDispose = true)
{
if (!self.Items.TryGetValue(itemId, out var item))
{
// 移除的时候,容器里找不到该物品的错误码。
return 1;
}
return self.RemoveItem(item, itemReason, isSendClient, isDispose);
}
public static uint RemoveItem(this Container self, long itemId, int count, ItemReason itemReason, bool isSendClient, bool isDispose = true)
{
if (!self.Items.TryGetValue(itemId, out var item))
{
// 移除的时候,容器里找不到该物品的错误码。
return 1;
}
if (item.Count < count)
{
// 要移除的物品数量不足的错误码。
return 2;
}
if (count < 1)
{
// 要移除物品的数量不能小于1的错误码。
return 3;
}
item.Count -= count;
if (item.Count <= 0)
{
return self.RemoveItem(item, itemReason, isSendClient, isDispose);
}
else
{
if (isSendClient)
{
var g2CUpdateItems = new G2C_UpdateItems();
g2CUpdateItems.ItemReason = (int)itemReason;
g2CUpdateItems.Items.Add(item.ToItemInfo());
self.SendClient(g2CUpdateItems);
}
Log.Debug($"RemoveItem itemReason:{itemReason} itemConfigId:{item.ConfigId} Count:{count}");
}
return 0;
}
public static uint RemoveItemByCell(this Container self, long cellId, ItemReason itemReason, bool isSendClient, bool isDispose = true)
{
if (!self.ItemsByCell.TryGetValue(cellId, out var item))
{
// 移除的时候,容器里找不到该物品的错误码。
return 1;
}
return self.RemoveItem(item, itemReason, isSendClient, isDispose);
}
#endregion
#region Sort
public static void Sort(this Container self)
{
// 这里排序一般服务器不会排序,也可以说是很少用排序.
// 一般都是让客户端做好这个排序。
// 如果用服务器排序会出现如下几种情况:
// 1、排序好了后要下发给客户端很有可能会把当前容器里的所有物品全部下发给客户端。
// 2、如果频繁排序的话服务器的压力就会很大。
// 3、因为移动端很少有那种格子空一个然后再有物品的情况所以可以不用考虑空格子的情况。
// 如果是PC端需要格子有空位的这时候服务器可以考虑排序了。
using (var list = ListPool<Item>.Create())
{
list.AddRange(self.Items.Values);
list.Sort(SortCompare);
self.Items.Clear();
for (var i = 0; i < list.Count; i++)
{
var item = list[i];
self.Items.Add(item.Id,item);
}
}
}
private static int SortCompare(Item a, Item b)
{
// 升序排列(从小到大)
// 3 1 2 排序后就是1 2 3
// < 0 那就是a 在 b 的前面
// = 0 那就是a 和 b 相等,顺序不变
// > 0 那就 a 在 b 的后面
var aConfig = a.Config;
var bConfig = b.Config;
var aWeight = aConfig.Weight;
var bWeight = bConfig.Weight;
// 这里还可以增加一些自定义的逻辑
// 比如:按照物品创建时间、是否绑定、出售价格等等。
// 这里的话就按照需求自己扩展把。
if (a.IsBind != b.IsBind)
{
// 比如这里要做一个需求是绑定的排在前面.
return a.IsBind ? -1 : 1;
}
if (aWeight == bWeight)
{
return 0;
}
if (aWeight > bWeight)
{
return -1;
}
return 1;
}
#endregion
#region ToProto
public static ContainerInfo ToContainerInfo(this Container self)
{
var containerInfo = new ContainerInfo()
{
ConfigId = (int)self.ConfigId,
CurrentCellCount = self.CurrentCellCount
};
foreach (var (_, item) in self.Items)
{
containerInfo.Items.Add(item.ToItemInfo());
}
return containerInfo;
}
#endregion
private static void SendClient(this Container self, IMessage message)
{
self.Account.GetParent<Session>().Send(message);
}
}

View File

@@ -0,0 +1,162 @@
using Fantasy.Entitas;
using Fantasy.Entitas.Interface;
using Fantasy.Helper;
namespace Fantasy.Equip;
public class EquipComponentAwakeSystem : AwakeSystem<EquipComponent>
{
protected override void Awake(EquipComponent self)
{
self.Awake();
}
}
public sealed class EquipComponentDestroySystem : DestroySystem<EquipComponent>
{
protected override void Destroy(EquipComponent self)
{
foreach (var (_, value) in self.MainAttrs)
{
value.Dispose();
}
foreach (var (_, value) in self.EntryAttrs)
{
value.Dispose();
}
self.Affixs.Clear();
self.MainAttrs.Clear();
self.EntryAttrs.Clear();
self.Durable = 0;
self.DurableMax = 0;
}
}
public sealed class EquipAttrDestroySystem : DestroySystem<EquipAttr>
{
protected override void Destroy(EquipAttr self)
{
self.Key = 0;
self.Value = 0;
self.SValue = 0;
}
}
public static class EquipComponentSystem
{
public static void Awake(this EquipComponent self)
{
var item = self.GetParent<Item>();
var itemConfig = item.Config;
// 设置装备的耐久
self.Durable = itemConfig.Durable;
self.DurableMax = self.Durable;
// 获取装备品质主属性方案
if (!EquipValueConfigData.Instance.TryGetValue(itemConfig.Id, itemConfig.Quality, out var equipValueConfig))
{
// 没有找到主属性方案,需要检查代码或配置表是否正确。
// 到这里的话就不需要继续执行了。
return;
}
// 设置主属性
self.InitMainAttrs(itemConfig, equipValueConfig);
// 设置附加(副)属性
self.InitEntryAttrs(itemConfig, equipValueConfig);
// 设置词缀
self.InitAffix(itemConfig, equipValueConfig);
}
private static void InitMainAttrs(this EquipComponent self, ItemConfig itemConfig, EquipValueConfig equipValueConfig)
{
self.MainAttrs.Clear();
// 循环遍历主属性。
foreach (var (key, value) in equipValueConfig.MainAttrs.Dic)
{
var equipAttr = Entity.Create<EquipAttr>(self.Scene, true, true);
equipAttr.Key = key;
equipAttr.Value = value;
self.MainAttrs.Add(key, equipAttr);
}
// 这里还可以加一些其他的需求。
}
private static void InitEntryAttrs(this EquipComponent self, ItemConfig itemConfig, EquipValueConfig equipValueConfig)
{
var equipEntryConfigId = equipValueConfig.EquipEntryConfigId;
// 很有可能当前的装备没有附加(副)属性,所以如果为默认值的情况下就不进行下面的逻辑。
if (equipEntryConfigId == 0)
{
return;
}
// 还有一种可能是设置的副(附加)属性不存在,一般这样的情况下都是策划配置的错误,这里的话也不进行下面的逻辑了。
if (!EquipEntryConfigData.Instance.TryGet(equipEntryConfigId, out var equipEntryConfig))
{
// 也可以在这里打印出一个错误,方便排查,找到这个错误告诉策划,让策划修改下。
// 推荐做法也可以一个单独的Scene在这里Scene里把这些所有配置加载出来按个的排查如果有错误就打印出来。
return;
}
self.EntryAttrs.Clear();
// 这里可以用权重来控制随机的数量和属性,但是这里就不演示的。
// 这里就是用直接的随机来做来了。
var entryCount = RandomHelper.RandomNumber(equipEntryConfig.Min, equipEntryConfig.Max);
// 获得当前随机附加属性的列表
var configAttrs = equipEntryConfig.Attrs;
var entryIndex = 0;
for (var i = 0; i < entryCount; i++)
{
var equipAttr = Entity.Create<EquipAttr>(self.Scene, true, true);
equipAttr.Key = configAttrs[entryIndex];
equipAttr.Value = RandomHelper.RandomNumber(configAttrs[entryIndex + 1], configAttrs[entryIndex + 2]);
self.EntryAttrs.Add(equipAttr.Key, equipAttr);
}
}
private static void InitAffix(this EquipComponent self, ItemConfig itemConfig, EquipValueConfig equipValueConfig)
{
var equipEntryConfigId = equipValueConfig.EquipEntryConfigId;
if (equipEntryConfigId == 0)
{
return;
}
if (!EquipEntryConfigData.Instance.TryGet(equipEntryConfigId, out var equipEntryConfig))
{
// 也可以在这里打印出一个错误,方便排查,找到这个错误告诉策划,让策划修改下。
// 推荐做法也可以一个单独的Scene在这里Scene里把这些所有配置加载出来按个的排查如果有错误就打印出来。
return;
}
if (equipEntryConfig.Affix.Length == 0)
{
// 有可能没有词缀,所以这里就不进行下面的逻辑了。
return;
}
// 随机词缀的数量
// 这里还是只做最简单的随机,如果想加权重的随机,可以自己实现下。
var configAffix = equipEntryConfig.Affix;
var affixCount = RandomHelper.RandomNumber(equipEntryConfig.AffixMin, equipEntryConfig.AffixMax);
for (var i = 0; i < affixCount; i++)
{
self.Affixs.Add(configAffix[i]);
}
}
public static EquipInfo ToEquipInfo(this EquipComponent self)
{
var equipInfo = new EquipInfo();
equipInfo.Durable = self.Durable;
equipInfo.DurableMax = self.DurableMax;
foreach (var (key,equipAttr) in self.MainAttrs)
{
equipInfo.MainKeys.Add(key);
equipInfo.EquipAttrKeys.Add(key);
equipInfo.EquipAttrValues.Add(equipAttr.Value);
equipInfo.EquipAttrSValues.Add(equipAttr.SValue);
}
return equipInfo;
}
}

View File

@@ -0,0 +1,137 @@
namespace Fantasy;
public static class EquipHelper
{
/// <summary>
/// 装载一个装备
/// </summary>
/// <param name="account"></param>
/// <param name="mountEquipItem"></param>
/// <returns></returns>
public static uint MountEquip(Account account, Item mountEquipItem)
{
var itemConfig = mountEquipItem.Config;
if (!itemConfig.IsEquip())
{
// 这里返回的是不是装备的错误码。
return 1;
}
if (mountEquipItem.Container.Config.Type != (int)ContainerType.Bag)
{
// 只有在背包里的物品才可以装备到装备栏里的错误码。
return 2;
}
var mountEquipPosition = itemConfig.Position;
// 如果在目标装备栏上有装备,那就先卸载装备。
UnMountEquip(account, (EquipPosition)mountEquipPosition, true);
// 下面要执行把装备添加到装备栏上。
// 先在背包中移除该装备,因为已经得到了装备了,所以可以直接通过物品拿到背包的容器。
var removeItem = mountEquipItem.Container.RemoveItem(mountEquipItem, ItemReason.MountEquipRemove, true, false);
if (removeItem != 0)
{
// 移除装备的时候,发生了错误,直接返回错误码。
return removeItem;
}
// 把装备添加到对应的装备栏上。
var addItem = ContainerHelper.AddItem(account, ContainerType.Equip, mountEquipItem, mountEquipPosition,
ItemReason.MountEquipAdd, true);
if (addItem != 0)
{
// 添加装备的时候,发生了错误,直接返回错误码。
return addItem;
}
// 这里要执行添加装备后因为装备有增加属性或词缀Buff所以需要在添加的时候把这些也要给添加下。
// 因为本课程胡总没有数值系统和Buff系统所以这里就不处理了。
Log.Debug($"执行了装备装备的效果逻辑。");
Log.Debug($"装载装备完成。mountEquipItem:{mountEquipItem.Id}");
// 可以合包优化下这个协议的数量,优化成一个,能节省流量。
return 0;
}
/// <summary>
/// 卸载一个装备
/// </summary>
/// <param name="account"></param>
/// <param name="equipPosition"></param>
/// <param name="isSendClient"></param>
/// <returns></returns>
public static uint UnMountEquip(Account account, EquipPosition equipPosition, bool isSendClient)
{
if (!ContainerHelper.GetItemByCell(account, ContainerType.Equip, (long)equipPosition, out var unMountEquipItem))
{
// 没有找到该位置的装备的错误码。
return 1;
}
return UnMountEquip(account, unMountEquipItem, isSendClient);
}
/// <summary>
/// 卸载一个装备
/// </summary>
/// <param name="account"></param>
/// <param name="unMountEquipItem"></param>
/// <param name="isSendClient"></param>
/// <returns></returns>
public static uint UnMountEquip(Account account, Item unMountEquipItem, bool isSendClient)
{
// 首先在装备栏容器中移除装备。
var removeItem = unMountEquipItem.Container.RemoveItem(unMountEquipItem, ItemReason.UnMountEquipRemove, isSendClient, false);
if (removeItem != 0)
{
// 移除失败了,要返回错误码,就不要执行后面的程序了。
return removeItem;
}
// 这里要执行移除装备后因为装备有增加属性或词缀Buff所以需要在移除的时候把这些也要给移除下。
// 因为本课程胡总没有数值系统和Buff系统所以这里就不处理了。
// 把移除成功的装备再次添加到背包容器中。
Log.Debug("执行了装备卸载的效果逻辑。");
return ContainerHelper.AddItem(account, ContainerType.Bag, unMountEquipItem, ItemReason.UnMountEquipAdd, isSendClient);
}
/// <summary>
/// 根据装备的位置获取装备
/// </summary>
/// <param name="account"></param>
/// <param name="equipPosition"></param>
/// <param name="item"></param>
/// <returns></returns>
public static bool GetEquipItem(Account account, EquipPosition equipPosition, out Item item)
{
return ContainerHelper.GetItemByCell(account, ContainerType.Equip, (long)equipPosition, out item);
}
/// <summary>
/// 是否是装备
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public static bool IsEquip(this Item item)
{
return item.Config.IsEquip();
}
/// <summary>
/// 获得装备所在的装备栏位置
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public static EquipPosition GetEquipPosition(this Item item)
{
return (EquipPosition)item.GetEquipPositionInt();
}
private static int GetEquipPositionInt(this Item item)
{
return item.Config.Position;
}
private static bool IsEquip(this ItemConfig itemConfig)
{
var configType = (ItemType)itemConfig.Type;
return configType == ItemType.Equip;
}
}

View File

@@ -0,0 +1,44 @@
using Fantasy.Entitas.Interface;
using Fantasy.Equip;
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
#pragma warning disable CS8601 // Possible null reference assignment.
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
namespace Fantasy;
public sealed class ItemDestroySystem : DestroySystem<Item>
{
protected override void Destroy(Item self)
{
self.CellId = 0;
self.Count = 0;
self.ConfigId = 0;
self.Container = null;
self.IsBind = false;
}
}
public static class ItemSystem
{
public static ItemInfo ToItemInfo(this Item self)
{
// 创建基础的物品信息
var mItemInfo = new ItemInfo()
{
ItemId = self.Id,
CellId = self.CellId,
Count = self.Count,
IsBind = self.IsBind,
ConfigId = (int)self.ConfigId,
Container = self.Container.Config.Type
};
// 添加装备信息
var equipComponent = self.GetComponent<EquipComponent>();
if (equipComponent != null)
{
mItemInfo.EquipInfo = equipComponent.ToEquipInfo();
}
return mItemInfo;
}
}

View File

@@ -0,0 +1,73 @@
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
namespace Fantasy;
public static class ItemUseHelper
{
public static uint UseBagItem(Account account, long itemId, int count)
{
return UseItem(account, ContainerType.Bag, itemId, count);
}
public static uint CanUseBagItem(Account account, long itemId, int count, out Container container, out ItemConfig itemConfig, out ItemUseComponent itemUseComponent)
{
return CanUseItem(account, ContainerType.Bag, itemId, count, out container, out itemConfig, out itemUseComponent);
}
public static uint UseItem(Account account, ContainerType containerType, long itemId, int count)
{
var errorCode = CanUseItem(account, containerType, itemId, count, out var container, out var itemConfig, out var itemUseComponent);
if (errorCode != 0)
{
return errorCode;
}
errorCode = container.RemoveItem(itemId, count, ItemReason.ItemUse, true);
if (errorCode != 0)
{
return errorCode;
}
itemUseComponent.Use(account, itemConfig, ref count);
return 0;
}
public static uint CanUseItem(Account account, ContainerType containerType, long itemId, int count, out Container container, out ItemConfig itemConfig, out ItemUseComponent itemUseComponent)
{
itemConfig = null;
itemUseComponent = null;
var errorCode = ContainerHelper.TryGetContainer(account, containerType, out container);
if (errorCode != 0)
{
return errorCode;
}
if (!container.GetItemById(itemId, out var item))
{
// 这里是找不到该物品的错误码。
return 1;
}
if (item.Count < count)
{
// 这里是物品数量不足的错误码。
return 2;
}
itemConfig = item.Config;
if (itemConfig.Params.Length <= 0)
{
// 这里是物品没有配置参数的错误码。
return 3;
}
// 这里还可以增加一些其他的判定,比如物品是否过期,物品是否被锁定等。
// 甚至还有物品使用的CD。
// 使用物品效果来判定
itemUseComponent = account.Scene.GetComponent<ItemUseComponent>();
return itemUseComponent.CanUse(account, item.Config, ref count);
}
}