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 { 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 { 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 items) { if (!self.ItemsByConfigId.TryGetValue(configId, out var itemList)) { return; } items.AddRange(itemList); } public static void GetItemsByType(this Container self, ItemType itemType, List 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.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().Send(message); } }