1760 lines
70 KiB
C#
1760 lines
70 KiB
C#
using BrewMonster.Managers;
|
|
using BrewMonster.Network;
|
|
using BrewMonster.Scripts;
|
|
using BrewMonster.Scripts.Managers;
|
|
using BrewMonster.Scripts.Skills;
|
|
using CSNetwork;
|
|
using CSNetwork.GPDataType;
|
|
using CSNetwork.S2CCommand;
|
|
using System;
|
|
using BrewMonster.UI;
|
|
using PerfectWorld.Scripts.Managers;
|
|
using UnityEngine;
|
|
using static BrewMonster.Scripts.CECHPWork;
|
|
using static BrewMonster.Scripts.EC_Inventory;
|
|
|
|
namespace BrewMonster
|
|
{
|
|
public partial class CECHostPlayer
|
|
{
|
|
public void OnMsgHstIvtrInfo(ECMSG Msg)
|
|
{
|
|
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:
|
|
{
|
|
// EC_Inventory.LogInventoryPacket("OWN_IVTR_DETAIL_DATA", data, hostId);
|
|
// Parse and store
|
|
if (data != null && data.Length >= 6)
|
|
{
|
|
byte byPackage = data[0];
|
|
byte ivtrSize = data[1];
|
|
if (EC_IvtrItemUtils.Instance.TryParseInventoryDetail(data, out var pkg,
|
|
out var size, out var items))
|
|
{
|
|
var inv = GetInventory(pkg);
|
|
if (inv != null)
|
|
{
|
|
inv.Resize(size);
|
|
inv.RemoveAllItems();
|
|
|
|
if (items != null)
|
|
{
|
|
foreach (var it in items)
|
|
{
|
|
if (it != null && it.Slot >= 0 && it.Slot < size)
|
|
{
|
|
if (it.Content != null && it.Content.Length > 0)
|
|
it.SetItemInfo(it.Content, it.Content.Length);
|
|
inv.SetItem(it.Slot, it);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// check if we got the item from the Equipment Pack. If so, we have to load the equipment items
|
|
if (byPackage == InventoryConst.IVTRTYPE_EQUIPPACK)
|
|
{
|
|
UpdateEquipSkins();
|
|
}
|
|
}
|
|
|
|
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)
|
|
//{
|
|
// try
|
|
// {
|
|
// var money = GPDataTypeHelper.FromBytes<CSNetwork.GPDataType.cmd_get_own_money>(data);
|
|
|
|
// SetMoneyAmount(money.amount);
|
|
|
|
// var ui = GameObject.FindFirstObjectByType<BrewMonster.Scripts.Managers.EC_InventoryUI>();
|
|
// if (ui == null)
|
|
// {
|
|
// var all = Resources.FindObjectsOfTypeAll<BrewMonster.Scripts.Managers.EC_InventoryUI>();
|
|
// if (all != null)
|
|
// {
|
|
// for (int i = 0; i < all.Length; i++)
|
|
// {
|
|
// var candidate = all[i];
|
|
// if (candidate != null && candidate.gameObject.scene.IsValid())
|
|
// {
|
|
// ui = candidate;
|
|
// break;
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// if (ui != null)
|
|
// {
|
|
// ui.UpdateMoney(money.amount, money.max_amount);
|
|
// }
|
|
// else
|
|
// {
|
|
// BrewMonster.Scripts.Managers.EC_InventoryUI.CacheMoney(money.amount, money.max_amount);
|
|
// }
|
|
// }
|
|
// catch (Exception ex)
|
|
// {
|
|
// Debug.LogWarning($"[Inventory] Failed to parse GET_OWN_MONEY: {ex.Message}");
|
|
// }
|
|
//}
|
|
if (data != null)
|
|
{
|
|
try
|
|
{
|
|
var money = GPDataTypeHelper.FromBytes<CSNetwork.GPDataType.cmd_get_own_money>(data);
|
|
SetMoneyAmount(money.amount);
|
|
m_iMaxMoney = (int)money.max_amount;
|
|
|
|
EC_InventoryUI ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
|
|
if (ui != null && ui.gameObject.activeInHierarchy)
|
|
{
|
|
ui.UpdateMoney(money.amount, money.max_amount);
|
|
}
|
|
else
|
|
{
|
|
EC_InventoryUI.CacheMoney(money.amount, money.max_amount);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogWarning($"[Inventory] Failed to parse GET_OWN_MONEY: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case CommandID.PLAYER_CASH:
|
|
{
|
|
if (data != null)
|
|
{
|
|
try
|
|
{
|
|
var cash = GPDataTypeHelper.FromBytes<CSNetwork.GPDataType.player_cash>(data);
|
|
var ui = GameObject.FindFirstObjectByType<BrewMonster.Scripts.Managers.EC_InventoryUI>();
|
|
if (ui == null)
|
|
{
|
|
var all = Resources.FindObjectsOfTypeAll<BrewMonster.Scripts.Managers.EC_InventoryUI>();
|
|
if (all != null)
|
|
{
|
|
for (int i = 0; i < all.Length; i++)
|
|
{
|
|
var candidate = all[i];
|
|
if (candidate != null && candidate.gameObject.scene.IsValid())
|
|
{
|
|
ui = candidate;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogWarning($"[Inventory] Failed to parse PLAYER_CASH: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnMsgHstOwnItemInfo(ECMSG Msg)
|
|
{
|
|
int cmd = Convert.ToInt32(Msg.dwParam2);
|
|
switch (cmd)
|
|
{
|
|
case CommandID.OWN_ITEM_INFO:
|
|
{
|
|
// if (Application.isEditor || Debug.isDebugBuild)
|
|
// Debug.Log($"[INVNET] HST_OWNITEMINFO cmd=OWN_ITEM_INFO bytes={(Msg.dwParam1 as byte[])?.Length ?? 0}");
|
|
|
|
// 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 < 22)
|
|
return;
|
|
|
|
byte byPackage = data[0];
|
|
byte bySlot = data[1];
|
|
int type = BitConverter.ToInt32(data, 2);
|
|
int expire_date = BitConverter.ToInt32(data, 6);
|
|
int state = BitConverter.ToInt32(data, 10);
|
|
uint count = BitConverter.ToUInt32(data, 14);
|
|
ushort content_length = BitConverter.ToUInt16(data, 20);
|
|
|
|
byte[] content = null;
|
|
if (content_length > 0 && 22 + content_length <= data.Length)
|
|
{
|
|
content = new byte[content_length];
|
|
Buffer.BlockCopy(data, 22, content, 0, content_length);
|
|
}
|
|
|
|
EC_Inventory pInventory = GetInventory(byPackage);
|
|
if (pInventory == null)
|
|
return;
|
|
|
|
if (bySlot >= pInventory.GetSize())
|
|
pInventory.Resize(bySlot + 1);
|
|
|
|
var pItem = pInventory.GetItem(bySlot, false);
|
|
if (pItem == null)
|
|
{
|
|
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 (pItem != null && pItem.IsEquipment())
|
|
{
|
|
// TODO
|
|
}
|
|
}
|
|
|
|
var ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
|
|
|
|
if (ui != null)
|
|
{
|
|
ui.RefreshAll();
|
|
//Debug.Log($"[OWN_ITEM_INFO] Refreshed inventory UI after updating item at package={byPackage}, slot={bySlot}");
|
|
}
|
|
|
|
break;
|
|
}
|
|
case CommandID.EMPTY_ITEM_SLOT:
|
|
{
|
|
var data = Msg.dwParam1 as byte[];
|
|
|
|
cmd_empty_item_slot pCmd = GPDataTypeHelper.FromBytes<cmd_empty_item_slot>(data);
|
|
EC_Inventory pInventory = GetPack(pCmd.byPackage);
|
|
if (pInventory == null)
|
|
return;
|
|
|
|
EC_IvtrItem pItem = pInventory.GetItem(pCmd.bySlot);
|
|
if (pItem != null)
|
|
{
|
|
//UpdateRemovedItemSC(pItem->GetTemplateID(), pCmd->byPackage, pCmd->bySlot);
|
|
}
|
|
|
|
pInventory.SetItem(pCmd.bySlot, null);
|
|
|
|
var ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
|
|
if (ui != null)
|
|
{
|
|
ui.RefreshAll();
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnMsgHstItemOperation(ECMSG Msg)
|
|
{
|
|
var data = Msg.dwParam1 as byte[];
|
|
int cmd = Convert.ToInt32(Msg.dwParam2);
|
|
switch (cmd)
|
|
{
|
|
case CommandID.EXG_IVTR_ITEM:
|
|
{
|
|
// S2C::cmd_exg_ivtr_item { byte index1; byte index2; }
|
|
if (data == null || data.Length < 2)
|
|
{
|
|
Debug.LogWarning("[Inventory] EXG_IVTR_ITEM: Invalid data length");
|
|
break;
|
|
}
|
|
|
|
byte index1 = data[0];
|
|
byte index2 = data[1];
|
|
|
|
if (m_pPack != null)
|
|
{
|
|
m_pPack.ExchangeItem(index1, index2);
|
|
|
|
var pItem1 = m_pPack.GetItem(index1, false);
|
|
if (pItem1 != null)
|
|
{
|
|
pItem1.Package = InventoryConst.IVTRTYPE_PACK;
|
|
pItem1.Slot = index1;
|
|
}
|
|
|
|
var pItem2 = m_pPack.GetItem(index2, false);
|
|
if (pItem2 != null)
|
|
{
|
|
pItem2.Package = InventoryConst.IVTRTYPE_PACK;
|
|
pItem2.Slot = index2;
|
|
}
|
|
}
|
|
|
|
var ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
|
|
ui?.RefreshAll();
|
|
break;
|
|
}
|
|
|
|
case CommandID.MOVE_IVTR_ITEM:
|
|
{
|
|
// S2C::cmd_move_ivtr_item { byte src; byte dest; uint count; }
|
|
if (data == null || data.Length < 6)
|
|
{
|
|
Debug.LogWarning("[Inventory] MOVE_IVTR_ITEM: Invalid data length");
|
|
break;
|
|
}
|
|
|
|
byte src = data[0];
|
|
byte dest = data[1];
|
|
uint count = BitConverter.ToUInt32(data, 2);
|
|
|
|
if (m_pPack != null)
|
|
{
|
|
// Client-side mirror of server operation.
|
|
// When dest is empty and count < stack, this becomes "separate/split stack".
|
|
m_pPack.MoveItem(src, dest, (int)count);
|
|
|
|
var pItemSrc = m_pPack.GetItem(src, false);
|
|
if (pItemSrc != null)
|
|
{
|
|
pItemSrc.Package = InventoryConst.IVTRTYPE_PACK;
|
|
pItemSrc.Slot = src;
|
|
}
|
|
|
|
var pItemDest = m_pPack.GetItem(dest, false);
|
|
if (pItemDest != null)
|
|
{
|
|
pItemDest.Package = InventoryConst.IVTRTYPE_PACK;
|
|
pItemDest.Slot = dest;
|
|
}
|
|
}
|
|
|
|
var ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
|
|
ui?.RefreshAll();
|
|
break;
|
|
}
|
|
|
|
case CommandID.PLAYER_DROP_ITEM:
|
|
{
|
|
// Parse the drop item data from the server response
|
|
if (data != null && data.Length >= 6)
|
|
{
|
|
byte byPackage = data[0];
|
|
byte bySlot = data[1];
|
|
int count = BitConverter.ToInt32(data, 2);
|
|
int tid = BitConverter.ToInt32(data, 6);
|
|
byte reason = data[10];
|
|
|
|
Debug.Log(
|
|
$"[Inventory] PLAYER_DROP_ITEM: package={byPackage}, slot={bySlot}, count={count}, tid={tid}, reason={reason}");
|
|
|
|
// Update the inventory by removing the item
|
|
var inv = GetInventory(byPackage);
|
|
bool success = inv != null && inv.RemoveItem(bySlot, count);
|
|
|
|
if (success)
|
|
{
|
|
Debug.Log(
|
|
$"[Inventory] Successfully removed {count} items from package {byPackage}, slot {bySlot}");
|
|
|
|
// 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] Failed to remove items from package {byPackage}, slot {bySlot}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning("[Inventory] PLAYER_DROP_ITEM: Invalid data length");
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
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 packInv = GetInventory(InventoryConst.IVTRTYPE_PACK);
|
|
var equipInv = GetInventory(InventoryConst.IVTRTYPE_EQUIPPACK);
|
|
var invItem = packInv?.GetItem(index_inv, true);
|
|
var equipItem = equipInv?.GetItem(index_equip, true);
|
|
UpdateEquipSkins();
|
|
if (invItem != null)
|
|
{
|
|
invItem.Package = InventoryConst.IVTRTYPE_EQUIPPACK;
|
|
invItem.Slot = index_equip;
|
|
equipInv?.SetItem(index_equip, invItem);
|
|
}
|
|
|
|
if (equipItem != null)
|
|
{
|
|
equipItem.Package = InventoryConst.IVTRTYPE_PACK;
|
|
equipItem.Slot = index_inv;
|
|
packInv?.SetItem(index_inv, equipItem);
|
|
}
|
|
|
|
// Trigger UI refresh if an EC_InventoryUI is present in scene
|
|
var ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
|
|
if (ui != null)
|
|
{
|
|
ui.RefreshAll();
|
|
ui.RefreshCharacterModelPreview();
|
|
}
|
|
|
|
UpdateEquipSkins();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <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 = 0, iExpireDate = 0, iAmount = 0, iCmdLastSlot = 0, iCmdSlotAmount = 0, iPack = 0, iMsg = -1;
|
|
|
|
switch (cmd)
|
|
{
|
|
case CommandID.PICKUP_ITEM:
|
|
{
|
|
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:
|
|
{
|
|
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;
|
|
break;
|
|
}
|
|
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)
|
|
{
|
|
#if UNITY_EDITOR
|
|
Debug.LogWarning($"[Inventory] PICKUP_FLOW desync: placed={placed} lastSlot={iLastSlot} slotNum={iSlotNum} expectedSlot={iCmdLastSlot} expectedSlotAmt={iCmdSlotAmount}");
|
|
#endif
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (cmd == CommandID.HOST_OBTAIN_ITEM && iPack == Inventory_type.IVTRTYPE_PACK)
|
|
{
|
|
// C++: CECShoppingManager::Instance().OnObtainItem(iPack, idItem, iAmount);
|
|
}
|
|
|
|
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());
|
|
}
|
|
}
|
|
|
|
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: S2C PURCHASE_ITEM (<c>cmd_purchase_item</c>).
|
|
/// Port of <c>CECHostPlayer::OnMsgHstPurchaseItems</c> (EC_HostMsg.cpp): prefer <c>MergeItem</c> like the C++ client.
|
|
/// If merge lands on a different slot than <c>inv_index</c> (common when stacking into an earlier pile), we still accept
|
|
/// the merged slot—the strict C++ <c>iLastSlot == inv_index</c> check would skip updates and break buy/stack UI here.
|
|
/// Fallback: <c>PutItemInSlot(inv_index)</c> then <c>MergeItem</c> when merge alone fails.
|
|
/// </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>();
|
|
bool purchaseSlotMismatch = false;
|
|
|
|
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;
|
|
byte booth_slot = data[index];
|
|
index += 1;
|
|
|
|
if (inv_index >= pPack.GetSize())
|
|
pPack.Resize(inv_index + 1);
|
|
|
|
int iLastSlot = 0;
|
|
int iSlotNum = 0;
|
|
bool placed = pPack.MergeItem(item_id, expire_date, count, out iLastSlot, out iSlotNum);
|
|
if (!placed)
|
|
placed = pPack.PutItemInSlot(inv_index, item_id, expire_date, count, out iLastSlot, out iSlotNum);
|
|
if (!placed)
|
|
placed = pPack.MergeItem(item_id, expire_date, count, out iLastSlot, out iSlotNum);
|
|
if (!placed)
|
|
{
|
|
#if UNITY_EDITOR
|
|
UnityEngine.Debug.LogWarning(
|
|
$"[OnMsgHstPurchaseItems] Could not place purchase tid={item_id} count={count} inv_index={inv_index}");
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
if (iLastSlot != inv_index)
|
|
purchaseSlotMismatch = true;
|
|
|
|
#if UNITY_EDITOR
|
|
if (iLastSlot != inv_index)
|
|
{
|
|
UnityEngine.Debug.Log(
|
|
$"[OnMsgHstPurchaseItems] Using merge slot {iLastSlot} (packet inv_index={inv_index}) tid={item_id}");
|
|
}
|
|
#endif
|
|
|
|
var pItem = pPack.GetItem(iLastSlot, false);
|
|
if (pItem != null)
|
|
{
|
|
pItem.Package = (byte)Inventory_type.IVTRTYPE_PACK;
|
|
pItem.Slot = iLastSlot;
|
|
// Keep stack size in sync with MergeItem/PutItemInSlot totals (authoritative for this slot).
|
|
if (iSlotNum > 0)
|
|
pItem.SetCount(iSlotNum);
|
|
if (pItem.IsEquipment())
|
|
slotsNeedingDetail.Add((byte)iLastSlot);
|
|
}
|
|
|
|
if (header.flag != 0 && GetBoothState() == 2)
|
|
{
|
|
var boothBPack = GetBoothBuyPack();
|
|
if (boothBPack != null)
|
|
{
|
|
// C++: CDlgInfo FIXMSG_BOOTHBUY — no matching in-game UI hook here; update booth pack only.
|
|
boothBPack.RemoveItemByFlag(booth_slot, count);
|
|
}
|
|
}
|
|
}
|
|
|
|
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();
|
|
if (purchaseSlotMismatch)
|
|
{
|
|
UnityGameSession.RequestInventoryAsync(0, () =>
|
|
{
|
|
var ui2 = GameObject.FindFirstObjectByType<EC_InventoryUI>();
|
|
ui2?.RefreshAll();
|
|
});
|
|
}
|
|
}
|
|
UpdateEquipSkins();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sell to NPC: server sends ITEM_TO_MONEY. This removes sold items from pack and adds gained money.
|
|
/// Payload layout differs across server builds, so we parse defensively.
|
|
/// </summary>
|
|
// NOTE: Kept as a helper implementation; the message dispatcher calls OnMsgHstItemToMoney in CECHostPlayer.cs.
|
|
private void OnMsgHstItemToMoney_Inventory(ECMSG Msg)
|
|
{
|
|
var data = Msg.dwParam1 as byte[];
|
|
if (data == null || data.Length < 4)
|
|
return;
|
|
|
|
// Two observed/expected formats:
|
|
// A) [u32 itemCount] + itemCount * npc_sell_item(16 bytes)
|
|
// B) [u32 moneyGain] [u32 itemCount] + itemCount * npc_sell_item(16 bytes)
|
|
uint a = BitConverter.ToUInt32(data, 0);
|
|
|
|
int offset = 0;
|
|
uint moneyGain = 0;
|
|
uint itemCount = 0;
|
|
|
|
const int itemSize = 16; // npc_sell_item: tid(4) index(4) count(4) price(4)
|
|
|
|
bool formatA = (data.Length - 4) >= 0 && ((data.Length - 4) % itemSize == 0) && (a == (uint)((data.Length - 4) / itemSize));
|
|
if (formatA)
|
|
{
|
|
itemCount = a;
|
|
offset = 4;
|
|
}
|
|
else if (data.Length >= 8)
|
|
{
|
|
uint b = BitConverter.ToUInt32(data, 4);
|
|
bool formatB = (data.Length - 8) >= 0 && ((data.Length - 8) % itemSize == 0) && (b == (uint)((data.Length - 8) / itemSize));
|
|
if (formatB)
|
|
{
|
|
moneyGain = a;
|
|
itemCount = b;
|
|
offset = 8;
|
|
}
|
|
}
|
|
|
|
if (itemCount == 0 || offset == 0)
|
|
{
|
|
// Fallback: if we cannot parse, request a full refresh so UI doesn't get stuck.
|
|
UnityGameSession.RequestInventoryAsync(0, () =>
|
|
{
|
|
var ui = UnityEngine.Object.FindFirstObjectByType<EC_InventoryUI>();
|
|
ui?.RefreshAll();
|
|
});
|
|
return;
|
|
}
|
|
|
|
long computedGain = moneyGain;
|
|
var pPack = GetPack(Inventory_type.IVTRTYPE_PACK);
|
|
if (pPack == null)
|
|
return;
|
|
|
|
for (int i = 0; i < itemCount; i++)
|
|
{
|
|
int baseIndex = offset + i * itemSize;
|
|
if (baseIndex + itemSize > data.Length)
|
|
break;
|
|
|
|
int tid = BitConverter.ToInt32(data, baseIndex);
|
|
uint invIndex = BitConverter.ToUInt32(data, baseIndex + 4);
|
|
uint count = BitConverter.ToUInt32(data, baseIndex + 8);
|
|
int price = BitConverter.ToInt32(data, baseIndex + 12);
|
|
|
|
if (invIndex >= (uint)pPack.GetSize())
|
|
continue;
|
|
|
|
if (count > 0)
|
|
pPack.RemoveItem((int)invIndex, (int)count);
|
|
|
|
// If server didn't include total moneyGain, sum per-item prices.
|
|
if (moneyGain == 0 && price > 0)
|
|
computedGain += price;
|
|
|
|
// Optional: request item info if something remains in slot but template changed.
|
|
var remain = pPack.GetItem((int)invIndex, false);
|
|
if (remain != null && remain.m_tid != tid)
|
|
UnityGameSession.c2s_CmdGetItemInfo((byte)Inventory_type.IVTRTYPE_PACK, (byte)invIndex);
|
|
}
|
|
|
|
if (computedGain > 0)
|
|
AddMoneyAmount((int)Mathf.Min(int.MaxValue, computedGain));
|
|
|
|
var invUi = UnityEngine.Object.FindFirstObjectByType<EC_InventoryUI>();
|
|
invUi?.RefreshAll();
|
|
UpdateEquipSkins();
|
|
}
|
|
|
|
private void OnMsgHstUseItem(ECMSG Msg)
|
|
{
|
|
cmd_host_use_item pCmd = GPDataTypeHelper.FromBytes<cmd_host_use_item>((byte[])Msg.dwParam1);
|
|
EC_Inventory pPack = GetPack(pCmd.byPackage);
|
|
if (pPack == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
EC_IvtrItem pItem = pPack.GetItem(pCmd.bySlot, false);
|
|
if (pItem == null || pItem.GetTemplateID() != pCmd.item_id)
|
|
{
|
|
return;
|
|
}
|
|
|
|
pItem.Use();
|
|
|
|
if (pCmd.use_count > 0)
|
|
{
|
|
bool removed = pPack.RemoveItem(pCmd.bySlot, pCmd.use_count);
|
|
if (removed)
|
|
{
|
|
if (pPack.GetItem(pCmd.bySlot, false) == null)
|
|
{
|
|
//Debug.Log($"[OnMsgHstUseItem] Item {pCmd.item_id} removed from slot {pCmd.bySlot}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Debug.LogError($"[OnMsgHstUseItem] Failed to remove item {pCmd.item_id} from slot {pCmd.bySlot}");
|
|
}
|
|
|
|
var ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
|
|
if (ui != null)
|
|
{
|
|
ui.RefreshAll();
|
|
ui.UpdateCooldownOverlays();
|
|
}
|
|
else
|
|
{
|
|
//Debug.LogError("[OnMsgHstUseItem] EC_InventoryUI not found, UI may not update");
|
|
}
|
|
}
|
|
|
|
if (m_pWorkMan != null)
|
|
{
|
|
CECHPWork pWork = m_pWorkMan.GetRunningWork(Host_work_ID.WORK_USEITEM);
|
|
if (pWork is CECHPWorkUse useWork)
|
|
{
|
|
if (useWork.GetItem() == pCmd.item_id)
|
|
{
|
|
m_pWorkMan.FinishRunningWork(Host_work_ID.WORK_USEITEM);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnMsgHstProduceItem(in ECMSG Msg)
|
|
{
|
|
var data = Msg.dwParam1 as byte[];
|
|
int cmd = Convert.ToInt32(Msg.dwParam2);
|
|
|
|
// Get DlgProduce to notify
|
|
var dlgProduce = GameObject.FindFirstObjectByType<DlgProduce>();
|
|
if (dlgProduce == null)
|
|
{
|
|
Debug.LogWarning("[OnMsgHstProduceItem] DlgProduce not found");
|
|
return;
|
|
}
|
|
|
|
switch (cmd)
|
|
{
|
|
case CommandID.PRODUCE_START:
|
|
{
|
|
cmd_produce_start pCmd = GPDataTypeHelper.FromBytes<cmd_produce_start>(data);
|
|
Debug.Log($"[PRODUCE_START] type={pCmd.type}, use_time={pCmd.use_time}, count={pCmd.count}");
|
|
dlgProduce.OnProduceStart(pCmd);
|
|
}
|
|
break;
|
|
case CommandID.PRODUCE_END:
|
|
{
|
|
Debug.Log("[PRODUCE_END] Production ended");
|
|
dlgProduce.OnProduceEnd();
|
|
}
|
|
break;
|
|
case CommandID.PRODUCE_NULL:
|
|
{
|
|
cmd_produce_null pCmd = GPDataTypeHelper.FromBytes<cmd_produce_null>(data);
|
|
Debug.Log($"[PRODUCE_NULL] type={pCmd.type}");
|
|
dlgProduce.OnProduceNull(pCmd);
|
|
}
|
|
break;
|
|
default:
|
|
Debug.LogWarning($"[OnMsgHstProduceItem] Unknown command: {cmd}");
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void OnMsgHstEmbedItem(ECMSG Msg)
|
|
{
|
|
cmd_embed_item pCmd = GPDataTypeHelper.FromBytes<cmd_embed_item>((byte[])Msg.dwParam1);
|
|
EC_IvtrItem pEquip = m_pPack.GetItem(pCmd.equip_idx);
|
|
EC_IvtrItem pTessera = m_pPack.GetItem(pCmd.chip_idx);
|
|
|
|
if (pEquip == null || pTessera == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_pPack.RemoveItem(pCmd.chip_idx, 1);
|
|
if (pTessera is EC_IvtrStone stone)
|
|
{
|
|
AddMoneyAmount(-(int)stone.GetDBEssence().install_price);
|
|
}
|
|
// Refresh equip's data
|
|
// todo make receive request
|
|
UnityGameSession.c2s_CmdGetItemInfo(Inventory_type.IVTRTYPE_PACK, pCmd.equip_idx);
|
|
}
|
|
|
|
|
|
|
|
public void OnMsgHstClearTessera(ECMSG Msg)
|
|
{
|
|
cmd_clear_tessera pCmd = GPDataTypeHelper.FromBytes<cmd_clear_tessera>((byte[])Msg.dwParam1);
|
|
AddMoneyAmount(-(int)pCmd.cost);
|
|
// Refresh equip's data
|
|
UnityGameSession.c2s_CmdGetItemInfo(Inventory_type.IVTRTYPE_PACK, (byte)pCmd.equip_idx);
|
|
}
|
|
// Add money amount
|
|
private int AddMoneyAmount(int iAmount)
|
|
{
|
|
long next = (long)m_iMoneyCnt + iAmount;
|
|
if (next < 0)
|
|
{
|
|
next = 0;
|
|
}
|
|
|
|
if(m_iMaxMoney > 0 && next > m_iMaxMoney)
|
|
{
|
|
next = m_iMaxMoney;
|
|
}
|
|
|
|
m_iMoneyCnt = (uint)next;
|
|
|
|
ulong amount = m_iMoneyCnt;
|
|
ulong maxAmount = m_iMaxMoney > 0 ? (ulong)m_iMaxMoney : amount;
|
|
|
|
EC_InventoryUI ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
|
|
if(ui != null && ui.gameObject.activeInHierarchy)
|
|
{
|
|
ui.UpdateMoney(amount, maxAmount);
|
|
}
|
|
else
|
|
{
|
|
EC_InventoryUI.CacheMoney(amount, maxAmount);
|
|
}
|
|
|
|
//m_iMoneyCnt += (uint)iAmount;
|
|
return (int)m_iMoneyCnt;
|
|
}
|
|
|
|
public bool HaveHealthStones()
|
|
{
|
|
var pPack = GetPack();
|
|
var items = new int[] { 36764, 36765, 36766, 36767 };
|
|
for (int i = 0; i < items.Length; ++i)
|
|
{
|
|
if (pPack.FindItem(items[i]) >= 0)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public bool UseItemInPack(int iPack, int iSlot, bool showMsg = true)
|
|
{
|
|
if (!CanDo(ActionCanDo.CANDO_USEITEM))
|
|
return false;
|
|
|
|
EC_Inventory pPack = GetPack(iPack);
|
|
if (pPack == null)
|
|
return false;
|
|
|
|
EC_IvtrItem pItem = pPack.GetItem(iSlot);
|
|
if (pItem == null || pItem.IsFrozen())
|
|
return false;
|
|
|
|
if (pItem.Use_Persist() && (IsJumping() || IsFalling()))
|
|
return false;
|
|
|
|
CECGameRun pGameRun = EC_Game.GetGameRun();
|
|
//CECGameSession pSession = g_pGame.GetGameSession();
|
|
|
|
if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_FIREWORK)
|
|
{
|
|
if (GetProfession() == (int)PROFESSION.PROF_GHOST && IsInvisible())
|
|
{
|
|
if (showMsg)
|
|
pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_CANNOT_USE_WHEN_INVISIBLE);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_INCSKILLABILITY)
|
|
{
|
|
EC_IvtrIncSkillAbility pIncSkill = pItem as EC_IvtrIncSkillAbility;
|
|
//if (pIncSkill != null)
|
|
//{
|
|
// var pDBEssence = pIncSkill.GetDBEssence();
|
|
// CECSkill pSkill = GetNormalSkill(pDBEssence.id_skill);
|
|
// if (pSkill != null)
|
|
// {
|
|
// if (pSkill.GetSkillLevel() != pDBEssence.level_required)
|
|
// {
|
|
// if (showMsg)
|
|
// pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_PRODUCE_LEVEL_INVALID);
|
|
// return false;
|
|
// }
|
|
// if (GetSkillAbilityPercent(pDBEssence.id_skill) >= 100)
|
|
// {
|
|
// if (showMsg)
|
|
// pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_PRODUCE_ABILITY_FULL);
|
|
// return false;
|
|
// }
|
|
// }
|
|
//}
|
|
}
|
|
|
|
if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_TRANSMITSCROLL)
|
|
{
|
|
CECGameUIMan pGameUI = pGameRun.GetUIManager().GetInGameUIMan();
|
|
if (pGameUI != null && !IsFighting())
|
|
{
|
|
// TODO: Implement travel map dialog
|
|
//CDlgWorldMap* pMap = (CDlgWorldMap*)pGameUI->GetDialog("Win_WorldMapTravel");
|
|
//pMap->BuildTravelMap(DT_TRANSMITSCROLL_ESSENCE, (void*)iSlot);
|
|
//pMap->Show(true);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_SHOPTOKEN)
|
|
{
|
|
CECGameUIMan pGameUI = pGameRun.GetUIManager().GetInGameUIMan();
|
|
if (pGameUI != null && !IsFighting())
|
|
{
|
|
//CDlgTokenShop* pDlg = dynamic_cast<CDlgTokenShop*>(pGameUI->GetDialog("Win_TokenShop"));
|
|
//if (pDlg)
|
|
//{
|
|
// pDlg->InitTokenShopItem(pItem->GetTemplateID());
|
|
// pDlg->Show(!pDlg->IsShow());
|
|
//}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_UNIVERSAL_TOKEN)
|
|
{
|
|
// TODO: Implement universal token when available
|
|
EC_IvtrUniversalToken pUniversalToken = pItem as EC_IvtrUniversalToken;
|
|
//if (pUniversalToken != null && pUniversalToken.HasAnyUsage())
|
|
//{
|
|
// CECUseUniversalTokenCommandManager.Instance.Use(pUniversalToken, pUniversalToken.UsageIndexAt(0));
|
|
// return true;
|
|
//}
|
|
}
|
|
|
|
if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_TASKDICE)
|
|
{
|
|
EC_IvtrTaskDice pTaskDice = pItem as EC_IvtrTaskDice;
|
|
if (pTaskDice != null)
|
|
{
|
|
if (pTaskDice != null)
|
|
{
|
|
if (IsFlying() && pTaskDice.GetDBEssence().no_use_in_combat == 1)
|
|
{
|
|
if (showMsg)
|
|
pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_CANNOT_USE_IN_BATTLE);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_TARGETITEM)
|
|
{
|
|
EC_IvtrTargetItem pTargetItem = pItem as EC_IvtrTargetItem;
|
|
if (pTargetItem == null)
|
|
return false;
|
|
|
|
var essence = pTargetItem.GetDBEssence();
|
|
|
|
if (!pTargetItem.IsEssenceLoaded())
|
|
return false;
|
|
|
|
if (IsFighting() && essence.use_in_combat == 0)
|
|
{
|
|
if (showMsg)
|
|
if (showMsg)
|
|
pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_CANNOT_USE_IN_BATTLE);
|
|
return false;
|
|
}
|
|
|
|
if (essence.use_in_sanctuary_only != 0 && !IsInSanctuary())
|
|
{
|
|
if (showMsg)
|
|
pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_USE_IN_SANCTUARY_ONLY);
|
|
return false;
|
|
}
|
|
|
|
int iCurrMap = pGameRun.GetWorld().GetInstanceID();
|
|
if (pTargetItem.GetDBEssence().num_area != 0)
|
|
{
|
|
bool found = false;
|
|
for (int i = 0; i < pTargetItem.GetDBEssence().num_area; i++)
|
|
{
|
|
if (pTargetItem.GetDBEssence().area_id[i] == iCurrMap)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
if (showMsg)
|
|
pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_CANNOT_USE_IN_CURR_MAP);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!CanDo(ActionCanDo.CANDO_SPELLMAGIC))
|
|
return false;
|
|
|
|
if (InSlidingState())
|
|
return false;
|
|
|
|
if (m_idSelTarget == 0)
|
|
return false;
|
|
|
|
CECSkill pSkill = pTargetItem.GetTargetSkill();
|
|
if (pSkill == null)
|
|
return false;
|
|
|
|
if (IsSpellingMagic() && m_pCurSkill != null && m_pCurSkill.IsCharging() &&
|
|
m_pCurSkill.GetSkillID() == pSkill.GetSkillID())
|
|
{
|
|
m_pCurSkill.EndCharging();
|
|
UnityGameSession.c2s_SendCmdContinueAction();
|
|
return true;
|
|
}
|
|
|
|
int iCon = CheckSkillCastCondition(pSkill);
|
|
if (iCon != 0)
|
|
{
|
|
if (showMsg)
|
|
ProcessSkillCondition(iCon);
|
|
return false;
|
|
}
|
|
|
|
bool bForceAttack = glb_GetForceAttackFlag(0);
|
|
if (pSkill.GetType() == (int)CECSkill.SkillType.TYPE_ATTACK ||
|
|
pSkill.GetType() == (int)CECSkill.SkillType.TYPE_CURSE)
|
|
{
|
|
if (m_idSelTarget == m_PlayerInfo.cid)
|
|
{
|
|
if (showMsg)
|
|
pGameRun.AddFixedChannelMsg((int)FixedMsg.FIXMSG_TARGETWRONG,
|
|
(int)ChatChannel.GP_CHAT_FIGHT);
|
|
return false;
|
|
}
|
|
else if (m_idSelTarget != 0)
|
|
{
|
|
if (AttackableJudge(m_idSelTarget, bForceAttack) != 1)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int idCastTarget = m_idSelTarget;
|
|
int iTargetType = pSkill.GetTargetType();
|
|
|
|
if (pSkill.GetType() == (int)CECSkill.SkillType.TYPE_BLESS ||
|
|
pSkill.GetType() == (int)CECSkill.SkillType.TYPE_NEUTRALBLESS)
|
|
{
|
|
if (iTargetType == 0 || !GPDataTypeHelper.ISPLAYERID(m_idSelTarget))
|
|
idCastTarget = m_PlayerInfo.cid;
|
|
|
|
if (GPDataTypeHelper.ISPLAYERID(idCastTarget) && idCastTarget != m_PlayerInfo.cid)
|
|
{
|
|
byte byBLSMask = EC_Utility.glb_BuildBLSMask();
|
|
|
|
if (pSkill.GetRangeType() == (int)CECSkill.RangeType.RANGE_POINT)
|
|
{
|
|
if (!IsTeamMember(idCastTarget))
|
|
{
|
|
if ((byBLSMask & (byte)PVPMask.GP_BLSMASK_SELF) != 0)
|
|
{
|
|
idCastTarget = m_PlayerInfo.cid;
|
|
}
|
|
else
|
|
{
|
|
EC_ElsePlayer pPlayer =
|
|
EC_ManMessageMono.Instance.GetECManPlayer.GetElsePlayer(idCastTarget) as
|
|
EC_ElsePlayer;
|
|
if (pPlayer == null)
|
|
return false;
|
|
|
|
if (pPlayer.IsInvader() || pPlayer.IsPariah())
|
|
{
|
|
if ((byBLSMask & (byte)PVPMask.GP_BLSMASK_NORED) != 0)
|
|
idCastTarget = m_PlayerInfo.cid;
|
|
}
|
|
|
|
if (!IsFactionMember(pPlayer.GetFactionID()))
|
|
{
|
|
if ((byBLSMask & (byte)PVPMask.GP_BLSMASK_NOMAFIA) != 0)
|
|
idCastTarget = m_PlayerInfo.cid;
|
|
}
|
|
|
|
if (!IsFactionAllianceMember(pPlayer.GetFactionID()))
|
|
{
|
|
if ((byBLSMask & (byte)PVPMask.GP_BLSMASK_NOALLIANCE) != 0)
|
|
idCastTarget = m_PlayerInfo.cid;
|
|
}
|
|
|
|
if (GetForce() != pPlayer.GetForce())
|
|
{
|
|
if ((byBLSMask & (byte)PVPMask.GP_BLSMASK_NOFORCE) != 0)
|
|
idCastTarget = m_PlayerInfo.cid;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If host is in dule
|
|
if (IsInDuel() && m_idSelTarget == m_pvp.idDuelOpp)
|
|
idCastTarget = m_PlayerInfo.cid;
|
|
|
|
// If host is in battle
|
|
if (IsInBattle())
|
|
{
|
|
EC_ElsePlayer pPlayer =
|
|
EC_ManMessageMono.Instance.GetECManPlayer.GetElsePlayer(idCastTarget);
|
|
if (!InSameBattleCamp(pPlayer))
|
|
idCastTarget = m_PlayerInfo.cid;
|
|
}
|
|
}
|
|
}
|
|
else if (pSkill.GetType() == (int)CECSkill.SkillType.TYPE_BLESS)
|
|
{
|
|
// TODO: Implement pet blessing when petsystem is available
|
|
//CECSCPet pPet = EC_Game.GetGameRun().GetWorld().GetPetByID(m_idSelTarget);
|
|
//if (pPet == null || pPet.GetMasterID() == GetCharacterID())
|
|
//{
|
|
// CECPetData pPetData = m_pPetCorral.GetActivePet();
|
|
// if (pPetData == null ||
|
|
// pPetData.GetClass() != GP_PET_TYPE.GP_PET_CLASS_COMBAT &&
|
|
// pPetData.GetClass() != GP_PET_TYPE.GP_PET_CLASS_SUMMON &&
|
|
// pPetData.GetClass() != GP_PET_TYPE.GP_PET_CLASS_EVOLUTION)
|
|
// return false;
|
|
|
|
// idCastTarget = m_pPetCorral.GetActivePetNPCID();
|
|
//}
|
|
//if(iTargetType != 0 && idCastTarget == 0)
|
|
// return false;
|
|
}
|
|
|
|
if (iTargetType != 0)
|
|
{
|
|
int iAliveFlag = 0;
|
|
if (iTargetType == 1)
|
|
iTargetType = 1;
|
|
else if (iTargetType == 2)
|
|
iTargetType = 2;
|
|
|
|
CECObject pObject = EC_ManMessageMono.Instance.GetObject(idCastTarget, iAliveFlag);
|
|
if (pObject == null)
|
|
return false;
|
|
}
|
|
|
|
if (!IsMeleeing() && !IsSpellingMagic() &&
|
|
(iTargetType == 0 || idCastTarget == m_PlayerInfo.cid))
|
|
{
|
|
if (!pSkill.ReadyToCast())
|
|
return false;
|
|
|
|
if (!pSkill.IsInstant() && pSkill.GetType() != (int)CECSkill.SkillType.TYPE_FLASHMOVE)
|
|
{
|
|
if (!NaturallyStopMoving())
|
|
return false;
|
|
}
|
|
else if (pSkill.GetType() == (int)CECSkill.SkillType.TYPE_FLASHMOVE)
|
|
{
|
|
if (!CanDo(ActionCanDo.CANDO_FLASHMOVE))
|
|
return false;
|
|
}
|
|
|
|
m_pPrepSkill = pSkill;
|
|
}
|
|
else if (IsSpellingMagic() && m_pCurSkill == pSkill && !pSkill.ReadyToCast())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (pItem.IsEquipment())
|
|
{
|
|
if (iPack == Inventory_type.IVTRTYPE_EQUIPPACK)
|
|
{
|
|
// Take off equipment
|
|
int iEmpty = m_pPack.SearchEmpty();
|
|
if (iEmpty < 0)
|
|
return false;
|
|
|
|
UnityGameSession.RequestEquipItemAsync((byte)iEmpty, (byte)iSlot, null);
|
|
return true;
|
|
}
|
|
|
|
EC_IvtrEquip pEquip = pItem as EC_IvtrEquip;
|
|
if (pEquip == null)
|
|
return false;
|
|
|
|
int iReason = 0;
|
|
if (!CanUseEquipment(pEquip, ref iReason))
|
|
return false;
|
|
|
|
int iFirstFree = -1, iFirstCan = -1;
|
|
for (int i = 0; i < InventoryConst.SIZE_ALL_EQUIPIVTR; i++)
|
|
{
|
|
if (pItem.CanEquippedTo(i))
|
|
{
|
|
if (iFirstCan < 0)
|
|
iFirstCan = i;
|
|
|
|
if (m_pEquipPack.GetItem(i) == null && iFirstFree < 0)
|
|
{
|
|
iFirstFree = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int iDst;
|
|
if (iFirstFree >= 0)
|
|
iDst = iFirstFree;
|
|
else if (iFirstCan >= 0)
|
|
iDst = iFirstCan;
|
|
else
|
|
{
|
|
Debug.Assert(false);
|
|
return false;
|
|
}
|
|
|
|
if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_DYNSKILLEQUIP)
|
|
{
|
|
int iSameIDPos = m_pEquipPack.FindItem(pItem.GetTemplateID());
|
|
if (iSameIDPos >= 0)
|
|
{
|
|
iDst = iSameIDPos;
|
|
}
|
|
}
|
|
|
|
if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_ARROW ||
|
|
pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_DYNSKILLEQUIP)
|
|
{
|
|
EC_IvtrItem pDstItem = m_pEquipPack.GetItem(iDst);
|
|
if (pDstItem == null || pItem.GetTemplateID() != pDstItem.GetTemplateID())
|
|
UnityGameSession.RequestEquipItemAsync((byte)iSlot, (byte)iDst, null);
|
|
else
|
|
{
|
|
// TODO: Implement c2s_CmdMoveItemToEquip when available
|
|
//UnityGameSession.c2s_CmdMoveItemToEquip((byte)iSlot, (byte)iDst);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_GENERALCARD)
|
|
{
|
|
//TODO: Add general card equip request
|
|
EC_IvtrGeneralCard pCard = pItem as EC_IvtrGeneralCard;
|
|
if (pCard != null)
|
|
{
|
|
iDst = InventoryConst.EQUIPIVTR_GENERALCARD1 + pCard.GetEssence().type;
|
|
}
|
|
}
|
|
|
|
UnityGameSession.RequestEquipItemAsync((byte)iSlot, (byte)iDst, null);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
if (iPack != Inventory_type.IVTRTYPE_PACK)
|
|
return false;
|
|
|
|
if (!pItem.CheckUseCondition())
|
|
{
|
|
if (showMsg)
|
|
pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_ITEM_CANNOTUSE);
|
|
return false;
|
|
}
|
|
|
|
int piMax = -1;
|
|
if (pItem.GetCoolTime(out piMax) > 0)
|
|
{
|
|
if (showMsg)
|
|
pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_ITEM_INCOOLTIME);
|
|
return false;
|
|
}
|
|
|
|
if (pItem.Use_AtkTarget() || pItem.Use_Target())
|
|
{
|
|
if (pItem.Use_AtkTarget() && CannotAttack())
|
|
return false;
|
|
|
|
if (m_idSelTarget == 0 || m_idSelTarget == m_PlayerInfo.cid)
|
|
{
|
|
if (showMsg)
|
|
{
|
|
CECStringTab pStrTab = EC_Game.GetFixedMsgs();
|
|
pGameRun.AddChatMessage(pStrTab.GetWideString((int)FixedMsg.FIXMSG_NOTARGET),
|
|
(int)ChatChannel.GP_CHAT_SYSTEM);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
float fAattackRange = 10000.0f;
|
|
if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_TOSSMAT)
|
|
{
|
|
EC_IvtrTossMat pTossMat = pItem as EC_IvtrTossMat;
|
|
if (pTossMat != null)
|
|
fAattackRange = pTossMat.GetDBEssence().attack_range;
|
|
}
|
|
else if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_TANKCALLIN)
|
|
{
|
|
fAattackRange = 5.0f;
|
|
}
|
|
else if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_TARGETITEM)
|
|
{
|
|
EC_IvtrTargetItem pTargetItem = pItem as EC_IvtrTargetItem;
|
|
if (pTargetItem != null && pTargetItem.GetTargetSkill() != null)
|
|
{
|
|
fAattackRange = pTargetItem.GetTargetSkill()
|
|
.GetCastRange(m_ExtProps.ak.AttackRange, GetPrayDistancePlus());
|
|
}
|
|
}
|
|
|
|
float fDist = 0, fTargetRag = 0;
|
|
CECObject pObject = null;
|
|
if (CalcDist(m_idSelTarget, out fDist, out pObject))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (GPDataTypeHelper.ISNPCID(m_idSelTarget))
|
|
{
|
|
pObject = EC_ManMessageMono.Instance.CECNPCMan.GetNPC(m_idSelTarget);
|
|
CECNPC pNPC = pObject as CECNPC;
|
|
if (pNPC != null)
|
|
fTargetRag = pNPC.GetTouchRadius();
|
|
}
|
|
else if (GPDataTypeHelper.ISPLAYERID(m_idSelTarget))
|
|
{
|
|
pObject = EC_ManMessageMono.Instance.GetECManPlayer.GetElsePlayer(m_idSelTarget);
|
|
EC_ElsePlayer pPlayer = pObject as EC_ElsePlayer;
|
|
if (pPlayer != null)
|
|
fTargetRag = pPlayer.GetTouchRadius();
|
|
}
|
|
|
|
if (fDist - fTargetRag > fAattackRange * 0.8f)
|
|
{
|
|
if (showMsg)
|
|
{
|
|
CECStringTab pStrTab = EC_Game.GetFixedMsgs();
|
|
pGameRun.AddChatMessage(pStrTab.GetWideString((int)FixedMsg.FIXMSG_TARGETISFAR),
|
|
(int)ChatChannel.GP_CHAT_SYSTEM);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
byte byPVPMask = glb_BuildPVPMask(glb_GetForceAttackFlag(0));
|
|
UnityGameSession.c2s_SendCmdUseItemWithTarget((byte)iPack, (byte)iSlot, pItem.GetTemplateID(),
|
|
byPVPMask);
|
|
}
|
|
else
|
|
{
|
|
if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_DOUBLEEXP)
|
|
{
|
|
EC_IvtrDoubleExp pDoubleExp = pItem as EC_IvtrDoubleExp;
|
|
if (pDoubleExp != null)
|
|
{
|
|
if (pDoubleExp.GetDBEssence().double_exp_time + pGameRun.GetRemainDblExpTime() > 3600 * 4)
|
|
{
|
|
if (showMsg)
|
|
{
|
|
CECGameUIMan pGameUI = pGameRun.GetUIManager().GetInGameUIMan();
|
|
//pGameUI.MessageBox("", pGameUI.GetStringFromTable(828), MB_OK, new Color32(1, 1, 1, 0.6));
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_SHARPENER)
|
|
{
|
|
if (showMsg)
|
|
pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_SHARPEN_ON_DRAG);
|
|
return false;
|
|
}
|
|
|
|
UnityGameSession.c2s_SendCmdUseItem((byte)iPack, (byte)iSlot, pItem.GetTemplateID(), 1);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public EC_Inventory GetPack(int iPack)
|
|
{
|
|
EC_Inventory pInventory = null;
|
|
switch (iPack)
|
|
{
|
|
case Inventory_type.IVTRTYPE_PACK: pInventory = m_pPack; break;
|
|
case Inventory_type.IVTRTYPE_EQUIPPACK: pInventory = m_pEquipPack; break;
|
|
case Inventory_type.IVTRTYPE_TASKPACK: pInventory = m_pTaskPack; break;
|
|
//case Inventory_type.IVTRTYPE_TRASHBOX: pInventory = m_pTrashBoxPack; break;
|
|
//case Inventory_type.IVTRTYPE_TRASHBOX2: pInventory = m_pTrashBoxPack2; break;
|
|
//case Inventory_type.IVTRTYPE_TRASHBOX3: pInventory = m_pTrashBoxPack3; break;
|
|
//case Inventory_type.IVTRTYPE_ACCOUNT_BOX: pInventory = m_pAccountBoxPack; break;
|
|
//case Inventory_type.IVTRTYPE_GENERALCARD_BOX: pInventory = m_pGeneralCardPack; break;
|
|
//case IVTRTYPE_PACK_CLIENT_GENERALCAR.IVTRTYPE_CLIENT_GENERALCARD_PACK: pInventory = m_pClientGenCardPack; break;
|
|
default:
|
|
return null;
|
|
}
|
|
|
|
return pInventory;
|
|
}
|
|
|
|
public int GetEquippedSuiteItem(int idSuite, ref int[] aItems)
|
|
{
|
|
int i, iItemCnt = 0;
|
|
for (i = 0; i < m_pEquipPack.GetSize(); i++)
|
|
{
|
|
var pItem = m_pEquipPack.GetItem(i);
|
|
if (pItem == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
EC_IvtrEquip pEquip = (EC_IvtrEquip)pItem;
|
|
if (pEquip == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (pEquip.GetSuiteID() != idSuite)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int iReason = 0;
|
|
if (!CanUseEquipment(pEquip, ref iReason))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (pEquip.CID == (int)EC_IvtrEquip.EQUIP_CLASS_ID.ICID_GENERALCARD)
|
|
{
|
|
//TODO: Add general card Suite
|
|
}
|
|
|
|
if (aItems.Length > 0)
|
|
{
|
|
aItems[iItemCnt] = pEquip.GetTemplateID();
|
|
}
|
|
|
|
iItemCnt++;
|
|
}
|
|
|
|
return iItemCnt;
|
|
}
|
|
|
|
public bool CanUseEquipment(EC_IvtrEquip pEquip, ref int piReason)
|
|
{
|
|
int iReason = 0;
|
|
if (pEquip == null)
|
|
{
|
|
iReason = 1;
|
|
goto End;
|
|
}
|
|
|
|
if (GetMaxLevelSofar() < pEquip.LevelReq ||
|
|
m_ExtProps.bs.strength < pEquip.StrengthReq ||
|
|
m_ExtProps.bs.agility < pEquip.AgilityReq ||
|
|
m_ExtProps.bs.vitality < pEquip.VitalityReq ||
|
|
m_ExtProps.bs.energy < pEquip.EnergyReq /*||
|
|
Reputation < pEquip.ReputationReq*/) //todo Add reputation check
|
|
{
|
|
iReason = 2;
|
|
goto End;
|
|
}
|
|
|
|
switch (pEquip.CID) //class id
|
|
{
|
|
case (int)EC_IvtrEquip.EQUIP_CLASS_ID.ICID_ARROW:
|
|
break;
|
|
case (int)EC_IvtrEquip.EQUIP_CLASS_ID.ICID_WING:
|
|
if (m_iProfession != (int)PROFESSION.PROF_ARCHOR && m_iProfession != (int)PROFESSION.PROF_ANGEL)
|
|
iReason = 3;
|
|
break;
|
|
case (int)EC_IvtrEquip.EQUIP_CLASS_ID.ICID_FLYSWORD:
|
|
//TODO: Add flysword check
|
|
break;
|
|
case (int)EC_IvtrEquip.EQUIP_CLASS_ID.ICID_FASHION:
|
|
//TODO: Add fashion check
|
|
break;
|
|
case (int)EC_IvtrEquip.EQUIP_CLASS_ID.ICID_ARMOR:
|
|
case (int)EC_IvtrEquip.EQUIP_CLASS_ID.ICID_DECORATION:
|
|
if ((pEquip.ProfReq & (1 << m_iProfession)) == 0)
|
|
iReason = 3;
|
|
break;
|
|
case (int)EC_IvtrEquip.EQUIP_CLASS_ID.ICID_WEAPON:
|
|
if ((pEquip.ProfReq & (1 << m_iProfession)) == 0)
|
|
iReason = 3;
|
|
else
|
|
{
|
|
//TODO: check range weapon arrow
|
|
}
|
|
|
|
break;
|
|
case (int)EC_IvtrEquip.EQUIP_CLASS_ID.ICID_GENERALCARD:
|
|
// TODO: Add general card check
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
End:
|
|
if (piReason > 0)
|
|
{
|
|
piReason = iReason;
|
|
}
|
|
|
|
return iReason == 0 ? true : false;
|
|
}
|
|
|
|
public bool CanTakeItem(int idItem, int iAmount)
|
|
{
|
|
bool bCanPick = false;
|
|
|
|
if (GPDataTypeHelper.ISMONEYTID(idItem))
|
|
{
|
|
if (GetMoneyAmount() < GetMaxMoneyAmount())
|
|
bCanPick = true;
|
|
}
|
|
else
|
|
{
|
|
if (IvtrPack.CanAddItem(idItem, iAmount, false) >= 0)
|
|
bCanPick = true;
|
|
}
|
|
|
|
return bCanPick;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Consume money + SP for learning skill on client side after player confirms.
|
|
/// Returns false if resources are not enough at commit time.
|
|
/// </summary>
|
|
public bool TryConsumeSkillLearnCost(int moneyCost, int spCost)
|
|
{
|
|
if (moneyCost < 0)
|
|
moneyCost = 0;
|
|
if (spCost < 0)
|
|
spCost = 0;
|
|
|
|
if ((long)GetMoneyAmount() < moneyCost)
|
|
return false;
|
|
|
|
if (m_BasicProps.iSP < spCost)
|
|
return false;
|
|
|
|
AddMoneyAmount(-moneyCost);
|
|
m_BasicProps.iSP = Math.Max(0, m_BasicProps.iSP - spCost);
|
|
return true;
|
|
}
|
|
}
|
|
}
|