470 lines
14 KiB
C#
470 lines
14 KiB
C#
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);
|
||
}
|
||
} |