3a05b94366
# Conflicts: # Assets/PerfectWorld/Scripts/Managers/CECNPCMan.cs # Assets/PerfectWorld/Scripts/Managers/EC_HPWorkMelee.cs # Assets/PerfectWorld/Scripts/Managers/EC_HPWorkTrace.cs # Assets/PerfectWorld/Scripts/Managers/EC_HostPlayer.cs # Assets/PerfectWorld/Scripts/Managers/EC_ManMatter.cs # Assets/PerfectWorld/Scripts/Managers/EC_ManPlayer.cs # Assets/PerfectWorld/Scripts/Managers/EC_Object.cs # Assets/PerfectWorld/Scripts/Move/CECHostMove.cs # Assets/PerfectWorld/Scripts/NPC/CECMonster.cs # Assets/PerfectWorld/Scripts/NPC/CECNPC.cs # Assets/PerfectWorld/Scripts/NPC/CECNPCModelDefaultPolicy.cs # Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs # Assets/PerfectWorld/Scripts/PlayerState/PlayerIdleState.cs # Assets/PerfectWorld/Scripts/PlayerState/PlayerMoveState.cs # Assets/PerfectWorld/Scripts/Players/EC_ElsePlayer.cs # Assets/PerfectWorld/Scripts/UI/pickupItem.cs # Assets/Scripts/CECHostPlayer.cs # Assets/Scripts/EC_Utility.cs # Assets/Scripts/GameController.cs # Assets/Scripts/InitializePlayer.cs # Assets/Scripts/Move/EC_Player.cs # Assets/Scripts/PlayerVisual.cs
389 lines
11 KiB
C#
389 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace BrewMonster.Scripts.Managers
|
|
{
|
|
|
|
public static class EC_Inventory
|
|
{
|
|
private static readonly Dictionary<byte, int> _packSizeByPackage = new Dictionary<byte, int>();
|
|
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)
|
|
{
|
|
case 0: return "PACK_INVENTORY";
|
|
case 1: return "PACK_EQUIPMENT";
|
|
case 2: return "PACK_TASKINVENTORY";
|
|
default: return "PACK_UNKNOWN";
|
|
}
|
|
}
|
|
|
|
private static string BytesToHex(byte[] bytes, int max)
|
|
{
|
|
if (bytes == null || bytes.Length == 0) return "";
|
|
int len = Math.Min(bytes.Length, max);
|
|
string hex = BitConverter.ToString(bytes, 0, len);
|
|
if (bytes.Length > len) hex += "-...";
|
|
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;
|
|
if (!_itemsByPackage.TryGetValue(byPackage, out var slots))
|
|
{
|
|
slots = new Dictionary<int, InventoryItemData>();
|
|
_itemsByPackage[byPackage] = slots;
|
|
}
|
|
slots.Clear();
|
|
if (items != null)
|
|
{
|
|
foreach (var it in items)
|
|
{
|
|
if (it != null && it.Slot >= 0)
|
|
{
|
|
slots[it.Slot] = it;
|
|
}
|
|
}
|
|
}
|
|
// Log this pack's items
|
|
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)
|
|
{
|
|
//Debug.Log($"[Inventory] === Pack {GetPackageName(byPackage)}({byPackage}) size={ivtrSize}, items={(slots?.Count ?? 0)} ===");
|
|
if (slots == null || slots.Count == 0)
|
|
{
|
|
//Debug.Log("[Inventory] (empty)");
|
|
return;
|
|
}
|
|
foreach (var kv in slots)
|
|
{
|
|
var it = kv.Value;
|
|
string itemName = EC_IvtrItem.ResolveItemName(it.TemplateId);
|
|
string extraHex = it.Content != null && it.Content.Length > 0 ? EC_IvtrItem.BytesToHex(it.Content, MaxContentHexToLog) : "";
|
|
//int extraLen = it.Content?.Length ?? 0;
|
|
//Debug.Log(
|
|
// $"[Inventory] pkg={GetPackageName(it.Package)}({it.Package}) slot={it.Slot} tid={it.TemplateId}{(string.IsNullOrEmpty(itemName) ? "" : " \"" + itemName + "\"")} count={it.Count} state={it.State} expire={it.ExpireDate} crc={it.Crc} content_len={extraLen}{(extraLen > 0 ? ", content_hex=" + extraHex : "")}"
|
|
//);
|
|
}
|
|
}
|
|
|
|
public static void LogInventoryPacket(string tag, byte[] buffer, int hostId)
|
|
{
|
|
if (buffer == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int index = 0;
|
|
if (buffer.Length < 6)
|
|
{
|
|
//LogInventoryRaw(tag, buffer);
|
|
return;
|
|
}
|
|
|
|
byte byPackage = buffer[index++];
|
|
byte ivtrSize = buffer[index++];
|
|
uint contentLength = BitConverter.ToUInt32(buffer, index); index += 4;
|
|
|
|
int remaining = buffer.Length - index;
|
|
int contentBytes = remaining;
|
|
if (contentLength < (uint)remaining)
|
|
{
|
|
contentBytes = (int)contentLength;
|
|
}
|
|
|
|
|
|
if (contentBytes > 0)
|
|
{
|
|
byte[] content = new byte[contentBytes];
|
|
Buffer.BlockCopy(buffer, index, content, 0, contentBytes);
|
|
}
|
|
|
|
int trailing = buffer.Length - (index + contentBytes);
|
|
if (trailing > 0)
|
|
{
|
|
byte[] tail = new byte[trailing];
|
|
Buffer.BlockCopy(buffer, index + contentBytes, tail, 0, trailing);
|
|
}
|
|
}
|
|
|
|
public static void LogInventoryRaw(string tag, byte[] buffer)
|
|
{
|
|
Debug.Log($"[Inventory] {tag}: RAW HEX (len={buffer?.Length ?? 0})=\n{(buffer == null ? "<null>" : BitConverter.ToString(buffer))}");
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|