Files
2026-03-05 11:39:06 +08:00

470 lines
14 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}
}