Merge pull request 'Update dlg pet rec and fix bug restore egg from NPC.' (#182) from feature/hp_pet into develop

Reviewed-on: https://git.pthub.vn/Unity/perfect-world-unity/pulls/182
This commit is contained in:
tungdv
2026-02-28 11:36:10 +00:00
23 changed files with 2456 additions and 2300 deletions
@@ -9,7 +9,6 @@ using System.Buffers.Binary;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using BrewMonster.Network;
using Unity.VisualScripting;
using UnityEngine;
public class CECNPCMan : IMsgHandler
@@ -1,5 +1,4 @@
using CSNetwork.GPDataType;
using Unity.VisualScripting;
using UnityEngine;
using static BrewMonster.Scripts.CECHPWorkSpell.Spell_magic_state;
namespace BrewMonster.Scripts
@@ -6,7 +6,6 @@ using CSNetwork.GPDataType;
using System;
using System.Runtime.ConstrainedExecution;
using PerfectWorld.Scripts;
using Unity.VisualScripting;
using UnityEngine;
///////////////////////////////////////////////////////////////////////////
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
namespace BrewMonster.Scripts.Managers
@@ -129,6 +128,42 @@ namespace BrewMonster.Scripts.Managers
m_aItems[iSlot2] = tmp;
}
/// <summary>
/// Place or stack item in a specific slot (server-specified slot). Matches C++ expectation that client uses same slot as server.
/// </summary>
public bool PutItemInSlot(int iSlot, int tid, int iExpireDate, int iAmount, out int piLastSlot, out int piLastAmount)
{
piLastSlot = -1;
piLastAmount = 0;
if (iSlot < 0 || iSlot >= m_aItems.Length || iAmount <= 0)
return false;
var slotItem = m_aItems[iSlot];
if (slotItem == null)
{
var newItem = EC_IvtrItem.CreateItem(tid, iExpireDate, iAmount);
if (newItem == null)
return false;
newItem.Slot = iSlot;
newItem.SetCount(iAmount);
m_aItems[iSlot] = newItem;
piLastSlot = iSlot;
piLastAmount = iAmount;
return true;
}
if (slotItem.GetTemplateID() != tid)
return false;
int pileLimit = Math.Max(1, EC_IvtrItem.GetPileLimit(tid));
int canAdd = Math.Max(0, pileLimit - Math.Max(0, slotItem.GetCount()));
if (canAdd <= 0)
return false;
int add = Math.Min(canAdd, iAmount);
slotItem.AddAmount(add);
piLastSlot = iSlot;
piLastAmount = slotItem.GetCount();
return true;
}
public bool MergeItem(int tid, int iExpireDate, int iAmount, out int piLastSlot, out int piLastAmount)
{
piLastSlot = -1;
@@ -170,6 +205,9 @@ namespace BrewMonster.Scripts.Managers
return false;
}
var newItem = EC_IvtrItem.CreateItem(tid, iExpireDate, iAmount);
if (newItem == null)
return false;
newItem.Slot = firstEmpty;
newItem.SetCount(iAmount);
m_aItems[firstEmpty] = newItem;
@@ -14,9 +14,6 @@ using System.Text.RegularExpressions;
using System.Reflection;
using BrewMonster.Scripts.Managers;
using BrewMonster.Scripts;
using UnityEngine.AddressableAssets;
using CSNetwork.Protocols;
using Unity.VisualScripting;
namespace PerfectWorld.Scripts.Managers
{
@@ -1,4 +1,3 @@
using Unity.VisualScripting;
using UnityEngine;
namespace BrewMonster
@@ -1,26 +1,17 @@
using Animancer;
using BrewMonster;
using BrewMonster.Managers;
using BrewMonster.Network;
using BrewMonster.PerfectWorld.Scripts.Vfx;
using BrewMonster.Scripts;
using BrewMonster.Scripts;
using BrewMonster.Scripts.Managers;
using BrewMonster.Scripts.Skills;
using CSNetwork.GPDataType;
using ModelRenderer.Scripts.GameData;
using PerfectWorld.Scripts.Managers;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Xml.Linq;
using TMPro;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UIElements;
using static BrewMonster.CECPlayer;
using BrewMonster.Network;
using System.Runtime.InteropServices;
using PerfectWorld.Scripts.Managers.BrewMonster.Managers;
using CSNetwork;
@@ -4,9 +4,6 @@ using ModelRenderer.Scripts.GameData;
using System;
using System.Runtime.InteropServices;
using System.Text;
using Unity.VisualScripting;
using UnityEngine;
using static CECNPC;
public class CECMonster : CECNPC
{
@@ -1315,6 +1315,26 @@ namespace CSNetwork.GPDataType
public byte bySlot;
}
/// <summary>One item in cmd_purchase_item (buy from NPC/booth). Wire: item_id, expire_date, count, inv_index, booth_slot = 15 bytes.</summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_purchase_item_ITEM
{
public int item_id;
public int expire_date;
public uint count;
public ushort inv_index;
public byte booth_slot;
}
/// <summary>Fixed header of cmd_purchase_item. Rest of packet is item_count x cmd_purchase_item_ITEM.</summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_purchase_item_header
{
public uint cost;
public uint yinpiao;
public byte flag;
public ushort item_count;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_get_own_money
@@ -1394,7 +1414,7 @@ namespace CSNetwork.GPDataType
public byte index;
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct cmd_pickup_item
public struct cmd_pickup_item
{
public int tid;
public int expire_date;
File diff suppressed because it is too large Load Diff
@@ -22,7 +22,6 @@ using CSNetwork.GPDataType;
using CSNetwork.S2CCommand;
using System;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using static BrewMonster.EC_Resource;
using static BrewMonster.IconResourceType;
@@ -5,7 +5,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Unity.VisualScripting;
using static BrewMonster.SkillArrayWrapper;
namespace BrewMonster
@@ -4,7 +4,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Unity.VisualScripting;
namespace BrewMonster.Scripts.Skills
{
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Serialization;
@@ -17,7 +17,6 @@ using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using TMPro;
using Unity.VisualScripting;
namespace BrewMonster.Scripts.Task.UI
{
@@ -5,7 +5,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;
@@ -10,9 +10,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Unity.VisualScripting;
using UnityEngine;
using static UnityEngine.Rendering.DebugUI;
namespace BrewMonster.UI
{
@@ -24,6 +24,7 @@ public class NPCShopDetailPanel : MonoBehaviour
private GShopItem currentItem;
private NPCShopUIManager shopManager;
private int shopItemIndex;
void Start()
{
@@ -39,10 +40,11 @@ public class NPCShopDetailPanel : MonoBehaviour
buyButton.onClick.AddListener(OnBuyButtonClicked);
}
public void SetupDetailPanel(GShopItem item, NPCShopUIManager manager)
public void SetupDetailPanel(GShopItem item, NPCShopUIManager manager, int index)
{
currentItem = item;
shopManager = manager;
shopItemIndex = index;
UpdateDisplay();
}
@@ -189,17 +191,19 @@ public class NPCShopDetailPanel : MonoBehaviour
}
}
// Create npc_trade_item array for buying from NPC
// The tid is the item template ID, index is shop item index (0 for now), count is quantity to buy
// Server requires SEVNPC_HELLO with NPC id before buy, and the correct shop slot index
if (shopManager != null && shopManager.CurrentNPCID != 0)
UnityGameSession.c2s_CmdNPCSevHello((int)shopManager.CurrentNPCID);
// Create npc_trade_item: tid = template ID, index = shop slot (server validates this), count = quantity
npc_trade_item[] items = new npc_trade_item[1];
items[0] = new npc_trade_item
{
tid = (int)currentItem.id,
index = 0, // Shop item index - may need to be determined from shop item position
count = 1 // Quantity to buy
index = (uint)shopItemIndex,
count = 1
};
// Send the buy command
UnityGameSession.c2s_CmdNPCSevBuy(1, items);
Debug.Log($"[NPCShopDetailPanel] Sent buy command for item {currentItem.id}, price {price}");
@@ -19,6 +19,7 @@ public class NPCShopItemPanel : MonoBehaviour
private GShopItem itemData;
private Coroutine iconLoadCoroutine;
private NPCShopUIManager shopManager;
private int shopItemIndex;
void Start()
{
@@ -54,10 +55,11 @@ public class NPCShopItemPanel : MonoBehaviour
}
}
public void SetupItem(GShopItem item, NPCShopUIManager manager)
public void SetupItem(GShopItem item, NPCShopUIManager manager, int index)
{
itemData = item;
shopManager = manager;
shopItemIndex = index;
UpdateDisplay();
}
@@ -65,7 +67,7 @@ public class NPCShopItemPanel : MonoBehaviour
{
if (shopManager != null && itemData.id != 0)
{
shopManager.ShowItemDetail(itemData);
shopManager.ShowItemDetail(itemData, shopItemIndex);
}
}
@@ -35,6 +35,9 @@ public class NPCShopUIManager : MonoBehaviour
private int currentTabIndex = 0;
private uint currentNPCID = 0;
private NPC_SELL_SERVICE? cachedSellService = null;
/// <summary>Current NPC id for this shop session. Send SEVNPC_HELLO with this before buy.</summary>
public uint CurrentNPCID => currentNPCID;
private NPCShopDetailPanel detailPanelScript;
void Start()
@@ -278,8 +281,9 @@ public class NPCShopUIManager : MonoBehaviour
if (elementDataMan == null)
return;
foreach (var good in page.goods)
for (int i = 0; i < page.goods.Length; i++)
{
var good = page.goods[i];
if (good.id == 0)
continue;
@@ -293,8 +297,8 @@ public class NPCShopUIManager : MonoBehaviour
// Create GShopItem
GShopItem shopItem = CreateShopItemFromGood(good, itemData, itemDataType);
// Create panel
CreateItemPanel(shopItem);
// Create panel with shop slot index (server expects this in npc_trade_item.index)
CreateItemPanel(shopItem, i);
}
}
}
@@ -372,7 +376,7 @@ public class NPCShopUIManager : MonoBehaviour
return shopItem;
}
void CreateItemPanel(GShopItem item)
void CreateItemPanel(GShopItem item, int shopItemIndex)
{
if (itemPanelPrefab == null || itemContainer == null)
return;
@@ -386,7 +390,7 @@ public class NPCShopUIManager : MonoBehaviour
if (itemPanelScript != null)
{
itemPanelScript.SetupItem(item, this);
itemPanelScript.SetupItem(item, this, shopItemIndex);
}
else
{
@@ -406,7 +410,7 @@ public class NPCShopUIManager : MonoBehaviour
currentItemPanels.Clear();
}
public void ShowItemDetail(GShopItem item)
public void ShowItemDetail(GShopItem item, int shopItemIndex)
{
if (item.id == 0)
return;
@@ -428,7 +432,7 @@ public class NPCShopUIManager : MonoBehaviour
if (detailPanelScript != null)
{
npcShopDetailPanel.SetActive(true);
detailPanelScript.SetupDetailPanel(item, this);
detailPanelScript.SetupDetailPanel(item, this, shopItemIndex);
}
else
{
@@ -3,7 +3,6 @@ using NUnit.Framework;
using System;
using System.Collections.Generic;
using TMPro;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;
+295 -206
View File
@@ -1,4 +1,4 @@
using BrewMonster.Managers;
using BrewMonster.Managers;
using BrewMonster.Network;
using BrewMonster.Scripts;
using BrewMonster.Scripts.Managers;
@@ -22,12 +22,76 @@ namespace BrewMonster
var data = Msg.dwParam1 as byte[];
int cmd = Convert.ToInt32(Msg.dwParam2);
int hostId = Convert.ToInt32(Msg.dwParam3);
switch (cmd)
{
case CommandID.OWN_IVTR_DATA:
{
LogInventoryPacket("OWN_IVTR_DATA", data, hostId);
// C++: pPack->ResetItems(*pCmd) where cmd_own_ivtr_info.content is a compact stream:
// for each slot [0..ivtr_size): tid (int); if tid>=0 then expire_date (int) and amount (int).
if (data != null && data.Length >= 6)
{
byte byPackage = data[0];
byte ivtrSize = data[1];
uint contentLength = BitConverter.ToUInt32(data, 2);
int index = 6;
int remaining = data.Length - index;
int contentBytes = remaining;
if (contentLength < (uint)remaining)
contentBytes = (int)contentLength;
var inv = GetInventory(byPackage);
if (inv != null)
{
inv.Resize(ivtrSize);
inv.RemoveAllItems();
int end = index + Math.Max(0, contentBytes);
for (int slot = 0; slot < ivtrSize; slot++)
{
if (index + 4 > end)
break;
int tid = BitConverter.ToInt32(data, index);
index += 4;
if (tid < 0)
{
inv.SetItem(slot, null);
continue;
}
if (index + 8 > end)
break;
int expireDate = BitConverter.ToInt32(data, index);
index += 4;
int amount = BitConverter.ToInt32(data, index);
index += 4;
if (amount > 0)
{
var item = EC_IvtrItem.CreateItem(tid, expireDate, amount);
if (item != null)
{
item.Package = byPackage;
item.Slot = slot;
item.SetCount(amount);
inv.SetItem(slot, item);
}
}
else
{
inv.SetItem(slot, null);
}
}
}
if (byPackage == InventoryConst.IVTRTYPE_EQUIPPACK)
{
UpdateEquipSkins();
}
var ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
ui?.RefreshAll();
}
break;
}
case CommandID.OWN_IVTR_DETAIL_DATA:
@@ -71,6 +135,19 @@ namespace BrewMonster
break;
}
case CommandID.CHANGE_IVTR_SIZE:
{
// C++: resize pack (normal inventory)
if (data != null && data.Length >= 4)
{
int newSize = BitConverter.ToInt32(data, 0);
if (m_pPack != null)
m_pPack.Resize(newSize);
var ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
ui?.RefreshAll();
}
break;
}
case CommandID.GET_OWN_MONEY:
{
if (data != null)
@@ -168,14 +245,13 @@ namespace BrewMonster
{
case CommandID.OWN_ITEM_INFO:
{
//Debug.Log("[Inventory] OWN_ITEM_INFO received");
//var data = Msg.dwParam1 as byte[];
//int hostId = Convert.ToInt32(Msg.dwParam3);
//LogInventoryPacket("OWN_ITEM_INFO", data, hostId);
if (Application.isEditor || Debug.isDebugBuild)
Debug.Log($"[INVNET] HST_OWNITEMINFO cmd=OWN_ITEM_INFO bytes={(Msg.dwParam1 as byte[])?.Length ?? 0}");
//Handmade
// Match C++ cmd_own_item_info layout and behavior: update an existing item in place.
// If the slot is missing (can happen if client missed OWN_IVTR_DATA), create it to match server state.
var data = Msg.dwParam1 as byte[];
if (data == null || data.Length == 0)
if (data == null || data.Length < 22)
return;
byte byPackage = data[0];
@@ -184,7 +260,6 @@ namespace BrewMonster
int expire_date = BitConverter.ToInt32(data, 6);
int state = BitConverter.ToInt32(data, 10);
uint count = BitConverter.ToUInt32(data, 14);
ushort crc = BitConverter.ToUInt16(data, 18);
ushort content_length = BitConverter.ToUInt16(data, 20);
byte[] content = null;
@@ -192,38 +267,45 @@ namespace BrewMonster
{
content = new byte[content_length];
Buffer.BlockCopy(data, 22, content, 0, content_length);
string hexDebug = BitConverter.ToString(content);
//Debug.Log($"[OWN_ITEM_INFO] Full Content Hex ({content_length} bytes): {hexDebug}");
}
//Debug.Log($"[OWN_ITEM_INFO] Parsed: package={byPackage}, slot={bySlot}, tid={type}, count={count}, content_len={content_length}");
EC_Inventory pInventory = GetInventory(byPackage);
EC_IvtrItem newItem = EC_IvtrItem.CreateItem(type, expire_date, (int)count);
if (pInventory == null)
return;
if (newItem != null)
if (bySlot >= pInventory.GetSize())
pInventory.Resize(bySlot + 1);
var pItem = pInventory.GetItem(bySlot, false);
if (pItem == null)
{
newItem.SetProcType(state);
newItem.GetDetailDataFromLocal();
if (content != null && content.Length > 0)
{
newItem.SetItemInfo(content, content_length);
}
pInventory.SetItem(bySlot, newItem);
//Debug.Log($"[OWN_ITEM_INFO] Fixed Update: Pack {byPackage} Slot {bySlot} - Type {type}");
pItem = EC_IvtrItem.CreateItem(type, expire_date, (int)count);
if (pItem == null)
return;
pItem.Package = byPackage;
pItem.Slot = bySlot;
pInventory.SetItem(bySlot, pItem);
}
pItem.SetExpireDate(expire_date);
pItem.SetProcType(state);
pItem.SetAmount((int)count);
if (content != null && content.Length > 0)
pItem.SetItemInfo(content, content.Length);
else
pItem.SetItemInfo(null, 0);
#if UNITY_EDITOR
Debug.Log($"[Inventory] OWN_ITEM_INFO pkg={byPackage} slot={bySlot} tid={type} count={count} contentLen={content_length}");
#endif
if (byPackage == InventoryConst.IVTRTYPE_EQUIPPACK)
{
UpdateEquipSkins();
}
else if (byPackage == InventoryConst.IVTRTYPE_PACK)
{
if (newItem.IsEquipment())
if (pItem != null && pItem.IsEquipment())
{
// TODO
}
@@ -342,7 +424,7 @@ namespace BrewMonster
}
// Trigger UI refresh if an EC_InventoryUI is present in scene
var ui = GameObject.FindObjectOfType<EC_InventoryUI>();
var ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
if (ui != null)
{
ui.RefreshAll();
@@ -354,208 +436,215 @@ namespace BrewMonster
}
}
/// <summary>
/// Message MSG_HST_PICKUPITEM handler. Matches C++ flow: switch only fills idItem/iExpireDate/iAmount/iCmdLastSlot/iCmdSlotAmount/iPack/iMsg;
/// then single common path: MergeItem (or PutItemInSlot to match server slot), GetItemInfo request for equipment, notifications, RefreshAll.
/// </summary>
public void OnMsgHstPickupItem(in ECMSG Msg)
{
var data = Msg.dwParam1 as byte[];
int cmd = Convert.ToInt32(Msg.dwParam2);
if (data == null)
return;
if (Application.isEditor || Debug.isDebugBuild)
Debug.Log($"[INVNET] HST_PICKUPITEM cmd={cmd} bytes={data.Length}");
bool bDoOther = false;
int idItem, iExpireDate = 0, iAmount, iCmdLastSlot, iCmdSlotAmount, iPack, iMsg = -1;
int idItem = 0, iExpireDate = 0, iAmount = 0, iCmdLastSlot = 0, iCmdSlotAmount = 0, iPack = 0, iMsg = -1;
switch (cmd)
{
case CommandID.HOST_OBTAIN_ITEM:
{
// Parse cmd_host_obtain_item struct data
int type = BitConverter.ToInt32(data, 0);
int expire_date = BitConverter.ToInt32(data, 4);
uint amount = BitConverter.ToUInt32(data, 8);
uint slot_amount = BitConverter.ToUInt32(data, 12);
byte where = data[16]; // Package index
byte index = data[17]; // Slot index in that package
var newItem = EC_IvtrItem.CreateItem(type, expire_date, (int)amount);
// Add item to inventory
var ivt = GetInventory(where);
if (newItem.Content != null && newItem.Content.Length > 0)
{
newItem.SetItemInfo(newItem.Content, newItem.Content.Length);
}
ivt.SetItem(index, newItem);
Debug.Log(
$"[HOST_OBTAIN_ITEM] Successfully added item {type} to package {where}, slot {index} with count {amount}");
// Trigger UI refresh if an EC_InventoryUI is present in scene
var ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
if (ui != null)
{
ui.RefreshAll();
}
UpdateEquipSkins();
}
break;
case CommandID.PICKUP_ITEM:
{
int tid = BitConverter.ToInt32(data, 0);
int expire_date = BitConverter.ToInt32(data, 4);
iAmount = (int)BitConverter.ToUInt32(data, 8);
uint iSlotAmount = BitConverter.ToUInt32(data, 12);
byte byPackage = data[16];
byte bySlot = data[17];
//Debug.Log($"[Inventory] PICKUP_ITEM: tid={tid}, expire_date={expire_date}, iAmount={iAmount}, iSlotAmount={iSlotAmount}, byPackage={byPackage}, bySlot={bySlot}");
// Notify pickupItem script about successful pickup
pickupItem pickupScript = pickupItem.Instance;
if (pickupScript != null)
{
//Debug.Log($"[Inventory] PICKUP_ITEM: tid={tid}, expire_date={expire_date}, iAmount={iAmount}, iSlotAmount={iSlotAmount}, byPackage={byPackage}, bySlot={bySlot}");
// Notify pickupItem script about successful pickup
pickupScript = UnityEngine.Object.FindFirstObjectByType<pickupItem>();
if (pickupScript != null)
{
pickupScript.OnPickupSuccess(tid);
}
// Create new inventory item data
var newItem = EC_IvtrItem.CreateItem(tid, expire_date, (int)iAmount);
// Add item to inventory
var ivt = GetInventory(byPackage);
ivt.SetItem(bySlot, newItem);
//Debug.Log($"[Inventory] Successfully added item {tid} to package {byPackage}, slot {bySlot} with count {iAmount}");
// Trigger UI refresh if an EC_InventoryUI is present in scene
var ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
if (ui != null)
{
ui.RefreshAll();
}
}
else
{
Debug.LogWarning("[Inventory] PICKUP_ITEM: Invalid data length");
}
var pCmdPickup = GPDataTypeHelper.FromBytes<cmd_pickup_item>(data);
idItem = pCmdPickup.tid;
iExpireDate = pCmdPickup.expire_date;
iAmount = (int)pCmdPickup.iAmount;
iCmdLastSlot = pCmdPickup.bySlot;
iCmdSlotAmount = (int)pCmdPickup.iSlotAmount;
iPack = pCmdPickup.byPackage;
iMsg = (int)FixedMsg.FIXMSG_PICKUPITEM;
break;
}
case CommandID.HOST_OBTAIN_ITEM:
{
var pCmdObtain = GPDataTypeHelper.FromBytes<cmd_host_obtain_item>(data);
idItem = pCmdObtain.type;
iExpireDate = pCmdObtain.expire_date;
iAmount = (int)pCmdObtain.amount;
iCmdLastSlot = pCmdObtain.index;
iCmdSlotAmount = (int)pCmdObtain.slot_amount;
iPack = pCmdObtain.where;
iMsg = (int)FixedMsg.FIXMSG_GAINITEM;
break;
}
case CommandID.PRODUCE_ONCE:
{
var pCmdProduce = GPDataTypeHelper.FromBytes<cmd_produce_once>(data);
idItem = pCmdProduce.type;
iExpireDate = 0;
iAmount = (int)pCmdProduce.amount;
iCmdLastSlot = pCmdProduce.index;
iCmdSlotAmount = (int)pCmdProduce.slot_amount;
iPack = pCmdProduce.where;
iMsg = (int)FixedMsg.FIXMSG_PRODUCEITEM;
var dlgProduce = UnityEngine.Object.FindFirstObjectByType<DlgProduce>();
dlgProduce?.OnProduceOnce(pCmdProduce);
break;
}
case CommandID.TASK_DELIVER_ITEM:
cmd_task_deliver_item pCmd = GPDataTypeHelper.FromBytes<cmd_task_deliver_item>(data);
// ASSERT(pCmd);
idItem = pCmd.type;
iExpireDate = pCmd.expire_date;
iAmount = (int)pCmd.amount;
iCmdLastSlot = pCmd.index;
iCmdSlotAmount = (int)pCmd.slot_amount;
iPack = pCmd.where;
{
var pCmdTask = GPDataTypeHelper.FromBytes<cmd_task_deliver_item>(data);
idItem = pCmdTask.type;
iExpireDate = pCmdTask.expire_date;
iAmount = (int)pCmdTask.amount;
iCmdLastSlot = pCmdTask.index;
iCmdSlotAmount = (int)pCmdTask.slot_amount;
iPack = pCmdTask.where;
iMsg = (int)FixedMsg.FIXMSG_GAINITEM;
bDoOther = true;
// Create new inventory item data
var taskNewItem = EC_IvtrItem.CreateItem(idItem, iExpireDate, (int)iAmount);
// Add item to inventory
var task_ivt = GetInventory((byte)iPack);
if (!task_ivt.MergeItem(idItem, iExpireDate, iAmount, out var iLastSlot, out var iSlotNum) ||
iLastSlot != iCmdLastSlot || iSlotNum != iCmdSlotAmount)
{
return;
}
task_ivt.SetItem(iCmdLastSlot, taskNewItem);
//Debug.Log($"[Inventory] Successfully added item {tid} to package {byPackage}, slot {bySlot} with count {iAmount}");
// Trigger UI refresh if an EC_InventoryUI is present in scene
var task_ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
if (task_ui != null)
{
task_ui.RefreshAll();
}
break;
case CommandID.PRODUCE_ONCE:
}
default:
return;
}
#if UNITY_EDITOR
Debug.Log($"[Inventory] PICKUP_FLOW cmd={cmd} pack={iPack} slot={iCmdLastSlot} tid={idItem} amt={iAmount} slotAmt={iCmdSlotAmount}");
#endif
EC_Inventory pInventory = GetPack(iPack);
if (pInventory == null)
return;
if (iCmdLastSlot >= pInventory.GetSize())
pInventory.Resize(iCmdLastSlot + 1);
bool placed = pInventory.PutItemInSlot(iCmdLastSlot, idItem, iExpireDate, iAmount, out int iLastSlot, out int iSlotNum);
if (!placed)
{
placed = pInventory.MergeItem(idItem, iExpireDate, iAmount, out iLastSlot, out iSlotNum);
if (!placed || iLastSlot != iCmdLastSlot || iSlotNum != iCmdSlotAmount)
{
// Parse cmd_produce_once struct data
cmd_produce_once produceCmd = GPDataTypeHelper.FromBytes<cmd_produce_once>(data);
#if UNITY_EDITOR
Debug.LogWarning($"[Inventory] PICKUP_FLOW desync: placed={placed} lastSlot={iLastSlot} slotNum={iSlotNum} expectedSlot={iCmdLastSlot} expectedSlotAmt={iCmdSlotAmount}");
#endif
return;
}
}
int produceItemId = produceCmd.type;
int produceExpireDate = 0;
uint produceAmount = produceCmd.amount;
byte producePack = produceCmd.where;
byte produceSlot = produceCmd.index;
if (cmd == CommandID.HOST_OBTAIN_ITEM && iPack == Inventory_type.IVTRTYPE_PACK)
{
// C++: CECShoppingManager::Instance().OnObtainItem(iPack, idItem, iAmount);
}
Debug.Log(
$"[PRODUCE_ONCE] Received: itemId={produceItemId}, amount={produceAmount}, pack={producePack}, slot={produceSlot}");
EC_IvtrItem pItem = pInventory.GetItem(iCmdLastSlot, false);
if (pItem != null)
{
pItem.Package = (byte)iPack;
pItem.Slot = iCmdLastSlot;
int cid = pItem.GetClassID();
if (pItem.IsEquipment() ||
cid == (int)EC_IvtrItem.InventoryClassId.ICID_TASKNMMATTER ||
cid == (int)EC_IvtrItem.InventoryClassId.ICID_TASKDICE ||
cid == (int)EC_IvtrItem.InventoryClassId.ICID_TASKITEM ||
cid == (int)EC_IvtrItem.InventoryClassId.ICID_GOBLIN_EXPPILL ||
cid == (int)EC_IvtrItem.InventoryClassId.ICID_WEDDINGBOOKCARD ||
cid == (int)EC_IvtrItem.InventoryClassId.ICID_WEDDINGINVITECARD ||
cid == (int)EC_IvtrItem.InventoryClassId.ICID_SKILLTOME ||
cid == (int)EC_IvtrItem.InventoryClassId.ICID_GOBLIN ||
cid == (int)EC_IvtrItem.InventoryClassId.ICID_PETEGG)
{
UnityGameSession.c2s_CmdGetItemInfo((byte)iPack, (byte)iCmdLastSlot);
}
if (iMsg >= 0)
{
CECGameRun pGameRun = EC_Game.GetGameRun();
pGameRun?.AddFixedMessage(iMsg, iAmount, pItem.GetName());
}
}
// Get inventory
var produce_ivt = GetInventory(producePack);
if (produce_ivt == null)
if (bDoOther)
{
// C++: m_pTaskInterface->DoAutoTeamForTask(idItem);
}
if (cmd == CommandID.PICKUP_ITEM)
{
var pickupScript = UnityEngine.Object.FindFirstObjectByType<pickupItem>();
pickupScript?.OnPickupSuccess(idItem);
}
var ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
ui?.RefreshAll();
UpdateEquipSkins();
}
/// <summary>Buy from NPC/booth: server sends PURCHASE_ITEM (cmd_purchase_item). C++ OnMsgHstPurchaseItems.</summary>
public void OnMsgHstPurchaseItems(ECMSG Msg)
{
var data = Msg.dwParam1 as byte[];
if (data == null || data.Length < 11)
return;
var header = GPDataTypeHelper.FromBytes<cmd_purchase_item_header>(data);
int index = 11;
const int itemSize = 15; // item_id(4) + expire_date(4) + count(4) + inv_index(2) + booth_slot(1)
EC_Inventory pPack = GetPack(Inventory_type.IVTRTYPE_PACK);
if (pPack == null)
return;
var slotsNeedingDetail = new System.Collections.Generic.List<byte>();
for (int i = 0; i < header.item_count && index + itemSize <= data.Length; i++)
{
int item_id = BitConverter.ToInt32(data, index); index += 4;
int expire_date = BitConverter.ToInt32(data, index); index += 4;
int count = (int)BitConverter.ToUInt32(data, index); index += 4;
ushort inv_index = BitConverter.ToUInt16(data, index); index += 2;
index += 1; // booth_slot
if (inv_index >= pPack.GetSize())
pPack.Resize(inv_index + 1);
bool placed = pPack.PutItemInSlot(inv_index, item_id, expire_date, count, out int lastSlot, out int slotNum);
if (!placed)
{
placed = pPack.MergeItem(item_id, expire_date, count, out lastSlot, out slotNum);
if (!placed || lastSlot != inv_index)
continue;
}
var pItem = pPack.GetItem(inv_index, false);
if (pItem != null)
{
pItem.Package = (byte)Inventory_type.IVTRTYPE_PACK;
pItem.Slot = inv_index;
int cid = pItem.GetClassID();
if (pItem.IsEquipment() ||
cid == (int)EC_IvtrItem.InventoryClassId.ICID_TASKNMMATTER ||
cid == (int)EC_IvtrItem.InventoryClassId.ICID_TASKDICE ||
cid == (int)EC_IvtrItem.InventoryClassId.ICID_TASKITEM ||
cid == (int)EC_IvtrItem.InventoryClassId.ICID_GOBLIN_EXPPILL ||
cid == (int)EC_IvtrItem.InventoryClassId.ICID_WEDDINGBOOKCARD ||
cid == (int)EC_IvtrItem.InventoryClassId.ICID_WEDDINGINVITECARD ||
cid == (int)EC_IvtrItem.InventoryClassId.ICID_SKILLTOME ||
cid == (int)EC_IvtrItem.InventoryClassId.ICID_GOBLIN ||
cid == (int)EC_IvtrItem.InventoryClassId.ICID_PETEGG)
{
Debug.LogWarning($"[PRODUCE_ONCE] Invalid inventory package {producePack}");
return;
}
// Check if the slot already has an item
var existingItem = produce_ivt.GetItem(produceSlot, false);
if (existingItem != null)
{
if (existingItem.m_tid == produceItemId)
{
existingItem.m_iCount = (int)produceAmount;
Debug.Log(
$"[PRODUCE_ONCE] Updated existing item count at slot {produceSlot} to {produceAmount}");
}
else
{
Debug.LogWarning(
$"[PRODUCE_ONCE] Slot {produceSlot} already has different item (tid={existingItem.m_tid}), not overwriting with {produceItemId}");
return;
}
}
else
{
var produceNewItem = new EC_IvtrItem
{
Package = producePack,
Slot = produceSlot,
m_tid = produceItemId,
m_expire_date = produceExpireDate,
State = 0,
m_iCount = (int)produceAmount,
Crc = 0,
Content = null
};
produce_ivt.SetItem(produceSlot, produceNewItem);
Debug.Log($"[PRODUCE_ONCE] Created new item at slot {produceSlot} with count {produceAmount}");
}
// Trigger UI refresh
var produce_ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
if (produce_ui != null)
{
produce_ui.RefreshAll();
}
UpdateEquipSkins();
// Notify DlgProduce
var dlgProduce = GameObject.FindFirstObjectByType<DlgProduce>();
if (dlgProduce != null)
{
dlgProduce.OnProduceOnce(produceCmd);
slotsNeedingDetail.Add((byte)inv_index);
}
}
break;
}
AddMoneyAmount(-(int)header.cost);
foreach (byte slot in slotsNeedingDetail)
UnityGameSession.c2s_CmdGetItemInfo((byte)Inventory_type.IVTRTYPE_PACK, slot);
var ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
ui?.RefreshAll();
UpdateEquipSkins();
}
private void OnMsgHstUseItem(ECMSG Msg)
+10 -7
View File
@@ -43,9 +43,9 @@ namespace BrewMonster
private CECHPWorkMan m_pWorkMan; // Host work manager
private uint m_dwLIES; // Logic-influence extend states
private FACTION_FORTRESS_ENTER m_fortressEnter; // ½øÈë»ùµØÐÅÏ¢
private PVPINFO m_pvp; // pvp information
private bool m_bInSanctuary = false; // true, player is in sanctuary
private int m_idFaction = 0; // ID of player's faction
//private PVPINFO m_pvp; // pvp information
//private bool m_bInSanctuary = false; // true, player is in sanctuary
//private int m_idFaction = 0; // ID of player's faction
public bool m_bPrepareFight = false; // true, prepare to fight
private int m_iJumpCount = 0;
private bool m_bJumpInWater = false;
@@ -565,6 +565,9 @@ namespace BrewMonster
case EC_MsgDef.MSG_HST_PICKUPITEM:
OnMsgHstPickupItem(Msg);
break;
case EC_MsgDef.MSG_HST_PURCHASEITEMS:
OnMsgHstPurchaseItems(Msg);
break;
case EC_MsgDef.MSG_HST_PRODUCEITEM:
OnMsgHstProduceItem(Msg);
break;
@@ -1467,10 +1470,10 @@ namespace BrewMonster
}
// Get faction ID
public int GetFactionID()
{
return m_idFaction;
}
//public int GetFactionID()
//{
// return m_idFaction;
//}
public void SetPrayDistancePlus(float prayDistancePlus)
{
m_fPrayDistancePlus = prayDistancePlus;