Update ui icon (placeholder) placed data
This commit is contained in:
@@ -11,6 +11,11 @@ namespace PerfectWorld.Scripts.Managers
|
||||
private static readonly Dictionary<byte, Dictionary<int, InventoryItemData>> _itemsByPackage = new Dictionary<byte, Dictionary<int, InventoryItemData>>();
|
||||
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<int, InventoryItemData> EnsureSlots(byte byPackage)
|
||||
{
|
||||
if (!_itemsByPackage.TryGetValue(byPackage, out var slots) || slots == null)
|
||||
{
|
||||
slots = new Dictionary<int, InventoryItemData>();
|
||||
_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<InventoryItemData> 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<int, InventoryItemData> slots)
|
||||
{
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace PerfectWorld.Scripts.Managers
|
||||
public static class EC_IvtrItem
|
||||
{
|
||||
private static readonly Dictionary<int, string> _tidNameCache = new Dictionary<int, string>();
|
||||
private static readonly Dictionary<int, int> _pileLimitCache = new Dictionary<int, int>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<EC_InventoryUI>();
|
||||
if (ui != null)
|
||||
{
|
||||
ui.RefreshAll();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
public void OnMsgHstOwnItemInfo(ECMSG Msg)
|
||||
{
|
||||
int cmd = Convert.ToInt32(Msg.dwParam2);
|
||||
|
||||
Reference in New Issue
Block a user