From 960fd880b62584f4af90d2d21b41ce7d6b0c0625 Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Wed, 24 Sep 2025 09:28:40 +0700 Subject: [PATCH] Update ui icon (placeholder) placed data --- .../Scripts/Managers/EC_Inventory.cs | 260 ++++++++++++++++++ .../Scripts/Managers/EC_IvtrItem.cs | 59 ++++ .../Scripts/Managers/EC_IvtrType.cs | 64 +++-- Assets/Scripts/CECHostPlayer.cs | 40 ++- 4 files changed, 392 insertions(+), 31 deletions(-) diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_Inventory.cs b/Assets/PerfectWorld/Scripts/Managers/EC_Inventory.cs index 1597f99bbb..4f8844f226 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_Inventory.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_Inventory.cs @@ -11,6 +11,11 @@ namespace PerfectWorld.Scripts.Managers private static readonly Dictionary> _itemsByPackage = new Dictionary>(); private const int MaxContentHexToLog = 64; + // Package constants to mirror legacy C++ names + public const byte PACK_INVENTORY = 0; + public const byte PACK_EQUIPMENT = 1; + public const byte PACK_TASKINVENTORY = 2; + private static string GetPackageName(byte pkg) { switch (pkg) @@ -31,8 +36,244 @@ namespace PerfectWorld.Scripts.Managers return hex; } + public static InventoryItemData GetItem(int iSlot, bool bRemove) + { + // Backward-compatible overload defaulting to inventory package + return GetItem(PACK_INVENTORY, iSlot, bRemove); + } + public static InventoryItemData GetItem(byte byPackage, int slot, bool remove) + { + if (!_itemsByPackage.TryGetValue(byPackage, out var slots) || slot < 0) + return null; + if (!slots.TryGetValue(slot, out var item)) + return null; + if (remove) + slots.Remove(slot); + return item; + } + private static Dictionary EnsureSlots(byte byPackage) + { + if (!_itemsByPackage.TryGetValue(byPackage, out var slots) || slots == null) + { + slots = new Dictionary(); + _itemsByPackage[byPackage] = slots; + } + return slots; + } + + public static void Resize(byte byPackage, int newSize) + { + _packSizeByPackage[byPackage] = Math.Max(0, newSize); + EnsureSlots(byPackage); + } + + public static InventoryItemData PutItem(byte byPackage, int slot, InventoryItemData item) + { + var slots = EnsureSlots(byPackage); + if (slot < 0) return null; + slots.TryGetValue(slot, out var oldItem); + slots[slot] = item; + return oldItem; + } + + public static void SetItem(byte byPackage, int slot, InventoryItemData item) + { + var slots = EnsureSlots(byPackage); + if (slot < 0) return; + slots[slot] = item; + } + + public static void ExchangeItem(byte byPackage, int slot1, int slot2) + { + if (slot1 == slot2) return; + var slots = EnsureSlots(byPackage); + slots.TryGetValue(slot1, out var i1); + slots.TryGetValue(slot2, out var i2); + if (i1 != null) slots[slot2] = i1; else slots.Remove(slot2); + if (i2 != null) slots[slot1] = i2; else slots.Remove(slot1); + } + + public static bool RemoveItem(byte byPackage, int slot, int amount) + { + var slots = EnsureSlots(byPackage); + if (!slots.TryGetValue(slot, out var item) || item == null) + return true; + int newCount = Math.Max(0, item.Count - Math.Max(0, amount)); + item.Count = newCount; + if (newCount <= 0) + slots.Remove(slot); + else + slots[slot] = item; + return true; + } + + public static void RemoveAllItems(byte byPackage) + { + var slots = EnsureSlots(byPackage); + slots.Clear(); + } + + public static int SearchEmpty(byte byPackage) + { + int size = _packSizeByPackage.TryGetValue(byPackage, out var s) ? s : 0; + var slots = EnsureSlots(byPackage); + for (int i = 0; i < size; i++) + if (!slots.ContainsKey(i)) return i; + return -1; + } + + public static int GetEmptySlotNum(byte byPackage) + { + int size = _packSizeByPackage.TryGetValue(byPackage, out var s) ? s : 0; + int occupied = EnsureSlots(byPackage).Count; + int empty = Math.Max(0, size - occupied); + return empty; + } + + public static int FindItem(byte byPackage, int templateId, int baseIdx = 0) + { + var slots = EnsureSlots(byPackage); + int size = _packSizeByPackage.TryGetValue(byPackage, out var s) ? s : 0; + if (baseIdx < 0) baseIdx = 0; + for (int i = baseIdx; i < size; i++) + { + if (slots.TryGetValue(i, out var it) && it != null && it.TemplateId == templateId) + return i; + } + return -1; + } + + public static int GetItemTotalNum(byte byPackage, int templateId) + { + int total = 0; + foreach (var kv in EnsureSlots(byPackage)) + { + var it = kv.Value; + if (it != null && it.TemplateId == templateId) + total += Math.Max(0, it.Count); + } + return total; + } + + public static int GetItemCanPileCount(byte byPackage, int templateId) + { + int ret = 0; + foreach (var kv in EnsureSlots(byPackage)) + { + var it = kv.Value; + if (it != null && it.TemplateId == templateId) + { + int pileLimit = Math.Max(1, EC_IvtrItem.GetPileLimit(templateId)); + ret += Math.Max(0, pileLimit - Math.Max(0, it.Count)); + } + } + return ret; + } + + public static int CanAddItem(byte byPackage, int templateId, int amount, bool tryPile) + { + int firstEmpty = -1; + int pileLimit = Math.Max(1, EC_IvtrItem.GetPileLimit(templateId)); + int size = _packSizeByPackage.TryGetValue(byPackage, out var s) ? s : 0; + var slots = EnsureSlots(byPackage); + for (int i = 0; i < size; i++) + { + if (!slots.TryGetValue(i, out var it) || it == null) + { + if (!tryPile) return i; + if (firstEmpty < 0) firstEmpty = i; + } + else if (it.TemplateId == templateId && it.Count + amount <= pileLimit) + { + return i; + } + } + return firstEmpty; + } + + public static bool MergeItem(byte byPackage, int templateId, int expireDate, int amount, out int lastSlot, out int slotAmount) + { + lastSlot = -1; + slotAmount = 0; + var slots = EnsureSlots(byPackage); + int pileLimit = Math.Max(1, EC_IvtrItem.GetPileLimit(templateId)); + int firstEmpty = -1; + int size = _packSizeByPackage.TryGetValue(byPackage, out var s) ? s : 0; + for (int i = 0; i < size && amount > 0; i++) + { + slots.TryGetValue(i, out var slotItem); + if (slotItem == null) + { + if (firstEmpty < 0) firstEmpty = i; + continue; + } + if (slotItem.TemplateId != templateId) continue; + int canAdd = Math.Max(0, pileLimit - Math.Max(0, slotItem.Count)); + if (canAdd <= 0) continue; + int add = Math.Min(canAdd, amount); + slotItem.Count += add; + amount -= add; + lastSlot = i; + slotAmount = slotItem.Count; + } + if (amount <= 0) return true; + if (firstEmpty < 0) return false; + var newItem = new InventoryItemData + { + Package = byPackage, + Slot = firstEmpty, + TemplateId = templateId, + ExpireDate = expireDate, + State = 0, + Count = amount, + Crc = 0, + Content = null + }; + slots[firstEmpty] = newItem; + lastSlot = firstEmpty; + slotAmount = amount; + return true; + } + + public static bool MoveItem(byte byPackage, int src, int dest, int amount) + { + if (src == dest) return false; + if (amount <= 0) return false; + int size = _packSizeByPackage.TryGetValue(byPackage, out var s) ? s : 0; + if (src < 0 || src >= size || dest < 0 || dest >= size) return false; + var slots = EnsureSlots(byPackage); + if (!slots.TryGetValue(src, out var srcItem) || srcItem == null) return false; + slots.TryGetValue(dest, out var dstItem); + if (dstItem == null) + { + var clone = new InventoryItemData + { + Package = byPackage, + Slot = dest, + TemplateId = srcItem.TemplateId, + ExpireDate = srcItem.ExpireDate, + State = srcItem.State, + Count = amount, + Crc = srcItem.Crc, + Content = srcItem.Content != null ? (byte[])srcItem.Content.Clone() : null + }; + slots[dest] = clone; + } + else + { + if (dstItem.TemplateId != srcItem.TemplateId) return false; + int pileLimit = Math.Max(1, EC_IvtrItem.GetPileLimit(dstItem.TemplateId)); + int canAdd = Math.Max(0, pileLimit - Math.Max(0, dstItem.Count)); + int add = Math.Min(canAdd, amount); + if (add <= 0) return false; + dstItem.Count += add; + amount = add; + } + RemoveItem(byPackage, src, amount); + return true; + } public static void UpdatePack(byte byPackage, int ivtrSize, IEnumerable items) { _packSizeByPackage[byPackage] = ivtrSize; @@ -56,6 +297,25 @@ namespace PerfectWorld.Scripts.Managers LogPackInternal(byPackage, ivtrSize, slots); } + public static bool ResetWithDetailData(byte byPackage, int ivtrSize, byte[] data) + { + // Uses EC_IvtrItem.TryParseInventoryDetail format + if (data == null) + { + Resize(byPackage, ivtrSize); + RemoveAllItems(byPackage); + return true; + } + if (!EC_IvtrItem.TryParseInventoryDetail(data, out var pkg, out var size, out var items)) + return false; + // Prefer header values when valid + byte finalPkg = byPackage; + if (pkg == byPackage) finalPkg = pkg; + int finalSize = ivtrSize > 0 ? ivtrSize : size; + UpdatePack(finalPkg, finalSize, items); + return true; + } + private static void LogPackInternal(byte byPackage, int ivtrSize, IReadOnlyDictionary slots) { diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem.cs b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem.cs index 7029fbe58a..354557608c 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem.cs @@ -25,6 +25,7 @@ namespace PerfectWorld.Scripts.Managers public static class EC_IvtrItem { private static readonly Dictionary _tidNameCache = new Dictionary(); + private static readonly Dictionary _pileLimitCache = new Dictionary(); private const int MaxContentHexToLog = 64; public static string ResolveItemName(int templateId) @@ -265,5 +266,63 @@ namespace PerfectWorld.Scripts.Managers if (bytes.Length > len) hex += "-..."; return hex; } + + public static int GetPileLimit(int templateId) + { + if (templateId <= 0) return 1; + if (_pileLimitCache.TryGetValue(templateId, out var cached)) return cached; + int limit = 1; + try + { + var edm = ElementDataManProvider.GetElementDataMan(); + if (edm != null) + { + uint id = unchecked((uint)templateId); + object data = edm.get_data_ptr(id, ID_SPACE.ID_SPACE_ESSENCE); + limit = ExtractPileLimitFromElement(data); + } + } + catch { } + if (limit <= 0) limit = 1; + _pileLimitCache[templateId] = limit; + return limit; + } + + private static int ExtractPileLimitFromElement(object data) + { + if (data == null) return 1; + var t = data.GetType(); + // Common field/property names across item essences + string[] names = new[] + { + "pilelimit", "pile_limit", "pileLimit", "stack", "stack_max", "stackMax", "max_stack", "maxStack" + }; + foreach (var name in names) + { + var f = t.GetField(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); + if (f != null && (f.FieldType == typeof(int) || f.FieldType == typeof(uint) || f.FieldType == typeof(short) || f.FieldType == typeof(ushort) || f.FieldType == typeof(byte))) + { + try + { + var val = f.GetValue(data); + int limit = Convert.ToInt32(val); + if (limit > 0) return limit; + } + catch { } + } + var p = t.GetProperty(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); + if (p != null && (p.PropertyType == typeof(int) || p.PropertyType == typeof(uint) || p.PropertyType == typeof(short) || p.PropertyType == typeof(ushort) || p.PropertyType == typeof(byte))) + { + try + { + var val = p.GetValue(data, null); + int limit = Convert.ToInt32(val); + if (limit > 0) return limit; + } + catch { } + } + } + return 1; + } } } diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrType.cs b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrType.cs index ffc42447a3..3b60ba17d5 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrType.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrType.cs @@ -10,36 +10,36 @@ namespace PerfectWorld.Scripts.Managers public enum IndexOfIteminEquipmentInventory : byte { EQUIPIVTR_WEAPON = 0, - EQUIPIVTR_HEAD, - EQUIPIVTR_NECK, - EQUIPIVTR_SHOULDER, - EQUIPIVTR_BODY, - EQUIPIVTR_WAIST, - EQUIPIVTR_LEG, - EQUIPIVTR_FOOT, - EQUIPIVTR_WRIST, - EQUIPIVTR_FINGER1, - EQUIPIVTR_FINGER2, - EQUIPIVTR_PROJECTILE, - EQUIPIVTR_FLYSWORD, - EQUIPIVTR_FASHION_BODY, - EQUIPIVTR_FASHION_LEG, - EQUIPIVTR_FASHION_FOOT, - EQUIPIVTR_FASHION_WRIST, - EQUIPIVTR_RUNE, - EQUIPIVTR_BIBLE, - EQUIPIVTR_SPEAKER, - EQUIPIVTR_AUTOHP, - EQUIPIVTR_AUTOMP, - EQUIPIVTR_POCKET, - EQUIPIVTR_GOBLIN, - EQUIPIVTR_CERTIFICATE, - EQUIPIVTR_FASHION_HEAD, - EQUIPIVTR_FORCE_TOKEN, - EQUIPIVTR_DYNSKILLEQUIP1, - EQUIPIVTR_DYNSKILLEQUIP2, - EQUIPIVTR_FASHION_WEAPON, - SIZE_EQUIPIVTR, + EQUIPIVTR_HEAD = 1, + EQUIPIVTR_NECK = 2, + EQUIPIVTR_SHOULDER = 3, + EQUIPIVTR_BODY = 4, + EQUIPIVTR_WAIST = 5, + EQUIPIVTR_LEG = 6, + EQUIPIVTR_FOOT = 7, + EQUIPIVTR_WRIST = 8, + EQUIPIVTR_FINGER1 = 9, + EQUIPIVTR_FINGER2 = 10, + EQUIPIVTR_PROJECTILE = 11, + EQUIPIVTR_FLYSWORD = 12, + EQUIPIVTR_FASHION_BODY = 13, + EQUIPIVTR_FASHION_LEG = 14, + EQUIPIVTR_FASHION_FOOT = 15, + EQUIPIVTR_FASHION_WRIST = 16, + EQUIPIVTR_RUNE = 17, + EQUIPIVTR_BIBLE = 18, + EQUIPIVTR_SPEAKER = 19, + EQUIPIVTR_AUTOHP = 20, + EQUIPIVTR_AUTOMP = 21, + EQUIPIVTR_POCKET = 22, + EQUIPIVTR_GOBLIN = 23, + EQUIPIVTR_CERTIFICATE = 24, + EQUIPIVTR_FASHION_HEAD = 25, + EQUIPIVTR_FORCE_TOKEN = 26, + EQUIPIVTR_DYNSKILLEQUIP1 = 27, + EQUIPIVTR_DYNSKILLEQUIP2 = 28, + EQUIPIVTR_FASHION_WEAPON = 29, + SIZE_EQUIPIVTR = 30, } public static byte GetEquipLocationForItem(int templateId) @@ -128,6 +128,10 @@ namespace PerfectWorld.Scripts.Managers { if (it.id == id) return (byte)IndexOfIteminEquipmentInventory.EQUIPIVTR_AUTOHP; } + foreach (var it in edm.goblin_essence_array) + { + if (it.id == id) return (byte)IndexOfIteminEquipmentInventory.EQUIPIVTR_GOBLIN; + } foreach (var it in edm.automp_essence_array) { if (it.id == id) return (byte)IndexOfIteminEquipmentInventory.EQUIPIVTR_AUTOMP; diff --git a/Assets/Scripts/CECHostPlayer.cs b/Assets/Scripts/CECHostPlayer.cs index 1751ce9be4..1475550b16 100644 --- a/Assets/Scripts/CECHostPlayer.cs +++ b/Assets/Scripts/CECHostPlayer.cs @@ -212,10 +212,48 @@ public class CECHostPlayer : MonoBehaviour break; } case int value when value == EC_MsgDef.MSG_HST_ITEMOPERATION: - Debug.Log("HungDev : MSG_HST_ITEMOPERATION"); + OnMsgHstItemOperation(Msg); break; } } + + public void OnMsgHstItemOperation(ECMSG Msg) + { + var data = Msg.dwParam1 as byte[]; + int cmd = Convert.ToInt32(Msg.dwParam2); + switch (cmd) + { + case CommandID.EQUIP_ITEM: + { + byte index_inv = data[0]; + byte index_equip = data[1]; + // Update client-side data: move item between PACK_INVENTORY and PACK_EQUIPMENT + var invItem = EC_Inventory.GetItem(EC_Inventory.PACK_INVENTORY, index_inv, true); + var equipItem = EC_Inventory.GetItem(EC_Inventory.PACK_EQUIPMENT, index_equip, true); + if (invItem != null) + { + invItem.Package = EC_Inventory.PACK_EQUIPMENT; + invItem.Slot = index_equip; + EC_Inventory.SetItem(EC_Inventory.PACK_EQUIPMENT, index_equip, invItem); + } + if (equipItem != null) + { + equipItem.Package = EC_Inventory.PACK_INVENTORY; + equipItem.Slot = index_inv; + EC_Inventory.SetItem(EC_Inventory.PACK_INVENTORY, index_inv, equipItem); + } + + // Trigger UI refresh if an EC_InventoryUI is present in scene + var ui = GameObject.FindObjectOfType(); + if (ui != null) + { + ui.RefreshAll(); + } + + break; + } + } + } public void OnMsgHstOwnItemInfo(ECMSG Msg) { int cmd = Convert.ToInt32(Msg.dwParam2);