Merge branch 'develop' into feature/HP_jump

This commit is contained in:
Tungdv
2025-12-23 16:25:41 +07:00
14 changed files with 814 additions and 252 deletions
@@ -5,6 +5,7 @@ using BrewMonster.Scripts;
using CSNetwork.GPDataType;
using System;
using System.Runtime.ConstrainedExecution;
using PerfectWorld.Scripts;
using Unity.VisualScripting;
using UnityEngine;
@@ -132,6 +133,10 @@ namespace BrewMonster
float fMaxCut = m_bMoreClose ? -1.0f : 1.0f;
CECObject pObject = EC_ManMessageMono.Instance.GetObject(m_iObjectId, 0);
if (pObject == null)
{
return false;
}
float fTouchRadius = 0.0f;
if (GPDataTypeHelper.ISPLAYERID(m_iObjectId))
@@ -142,7 +147,13 @@ namespace BrewMonster
}
else
{
CECPlayer pPlayer = pObject.GetComponent<CECHostPlayer>();
// pObject is usually EC_ElsePlayer/CECPlayer, not CECHostPlayer.
// Using GetComponent<CECHostPlayer>() returns null and breaks touch checks.
CECPlayer pPlayer = pObject as CECPlayer;
if (pPlayer == null)
{
return false;
}
fTouchRadius = pPlayer.GetTouchRadius();
}
return m_pHost.CanTouchTarget(vHostPos, vTargetPos, fTouchRadius, iTouchReason, fMaxCut);
@@ -150,13 +161,25 @@ namespace BrewMonster
else if (GPDataTypeHelper.ISNPCID(m_iObjectId))
{
CECNPC pNPC = pObject as CECNPC;
if (pNPC == null)
{
return false;
}
fTouchRadius = pNPC.GetTouchRadius();
return m_pHost.CanTouchTarget(vHostPos, vTargetPos, fTouchRadius, iTouchReason, fMaxCut);
}
else if (GPDataTypeHelper.ISMATTERID(m_iObjectId))
{
//CECMatter pMatter = (pObject) as CECMatter;
//return pMatter.CalcDist(vHostPos, true) < pMatter.GetGatherDist();
CECMatter pMatter = (pObject) as CECMatter;
if (pMatter == null)
{
return false;
}
// For matter, use horizontal distance (ignore Y).
// Unity's touch check uses host position + capsule extent, which would otherwise
// force the player to get much closer than intended.
// C++ behavior expects a stand-off distance (default 3.0).
return pMatter.CalcDist(vHostPos, false) < pMatter.GetGatherDist();
}
break;
}
@@ -207,7 +230,11 @@ namespace BrewMonster
public override A3DVECTOR3 GetTargetPos()
{
return (GetTargetObject() as CECNPC).GetServerPos();
// 使用 Unity transform 的实时位置,而不是服务器缓存位置(可能滞后 / 未及时刷新),
// 否则追踪目的地/触碰判定会偏离导致永远触不到。
// Use live transform position (GetPos) instead of cached server position (GetServerPos),
// otherwise tracing destination and touch checks can be wrong and never trigger.
return (GetTargetObject() as CECNPC).GetPos();
}
public override bool OnTouched()
@@ -231,8 +258,16 @@ namespace BrewMonster
}
else if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_ATTACK)
{
if (m_iObjectId == m_pHost.m_idSelTarget &&
m_pHost.AttackableJudge(m_iObjectId, m_bForceAttack) == 1)
// 服务器的选中目标同步可能比追踪触碰晚到,导致这里永远不触发攻击。
// 为了保证追踪到目标后能正常攻击,这里在必要时主动选中目标。
// Server select-target ack can arrive later than the trace touch moment, which would
// make this block forever. Ensure we select the traced target before attacking.
if (m_iObjectId != m_pHost.m_idSelTarget)
{
UnityGameSession.c2s_CmdSelectTarget(m_iObjectId);
m_pHost.m_idSelTarget = m_iObjectId;
}
if (m_pHost.AttackableJudge(m_iObjectId, m_bForceAttack) == 1)
{
byte byPVPMask = EC_Utility.glb_BuildPVPMask(m_bForceAttack);
UnityGameSession.c2s_CmdNormalAttack(byPVPMask);
@@ -317,7 +352,9 @@ namespace BrewMonster
}
else
{
return (pObject as EC_ElsePlayer).GetServerPos();
// 同上:用实时位置避免 server pos 滞后导致追踪/触碰失败。
// Same rationale: use live position to avoid stale server pos breaking trace/touch.
return (pObject as EC_ElsePlayer).GetPos();
}
}
@@ -342,8 +379,14 @@ namespace BrewMonster
}
if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_ATTACK)
{
if (m_iObjectId == m_pHost.m_idSelTarget &&
m_pHost.AttackableJudge(m_iObjectId, m_bForceAttack) == 1)
// 同 NPC:追踪触碰可能早于服务器选中目标同步,需主动选中以避免攻击不触发。
// Same as NPC: trace touch can happen before server selection ack; proactively select.
if (m_iObjectId != m_pHost.m_idSelTarget)
{
UnityGameSession.c2s_CmdSelectTarget(m_iObjectId);
m_pHost.m_idSelTarget = m_iObjectId;
}
if (m_pHost.AttackableJudge(m_iObjectId, m_bForceAttack) == 1)
{
byte byPVPMask = EC_Utility.glb_BuildPVPMask(m_bForceAttack);
UnityGameSession.c2s_CmdNormalAttack(byPVPMask);
@@ -431,70 +474,78 @@ namespace BrewMonster
public override bool OnTouched()
{
bool bActionDone = false;
//if (GPDataTypeHelper.ISMATTERID(m_iObjectId))
//{
// if (m_pHost.GetProfession() == PROF_GHOST && m_pHost.IsInvisible())
// {
// g_pGame.GetGameRun().AddFixedMessage(FIXMSG_CANNOT_USE_WHEN_INVISIBLE);
// return bActionDone;
// }
// CECMatter* pMatter = (CECMatter*)GetTargetObject();
if (GPDataTypeHelper.ISMATTERID(m_iObjectId))
{
// if (m_pHost.GetProfession() == BrewMonster.RoleTypes.PROF_GHOST && m_pHost.IsInvisible())
// {
// EC_Game.GetGameRun().AddFixedMessage(FIXMSG_CANNOT_USE_WHEN_INVISIBLE);
// return bActionDone;
// }
CECMatter pMatter = (CECMatter)GetTargetObject();
// if (m_iReason == CECHPWorkTrace::TRACE_PICKUP)
// {
// // Check whether we have enougth place to hold this item or money
// a_LogOutput(1, "[NormalATK]- CECTracedMatter- OnTouched- TRACE_PICKUP");
// if (m_pHost.CanTakeItem(pMatter.GetTemplateID(), 1))
// {
// // Send pickup asking and wait response command
// g_pGame.GetGameSession().c2s_CmdPickup(m_iObjectId, pMatter.GetTemplateID());
// bActionDone = true;
// }
// else
// {
// // Print a notify message
// g_pGame.GetGameRun().AddFixedMessage(FIXMSG_PACKISFULL);
// }
// }
// else
// { // m_iReason == TRACE_GATHER
// int tidMatter = pMatter.GetTemplateID();
// a_LogOutput(1, "[NormalATK]- CECTracedMatter- OnTouched- TRACE_GATHER");
// // Check mine level requirement
// if (m_pHost.GetBasicProps().iLevel < pMatter.GetLevelReq())
// {
// g_pGame.GetGameRun().AddFixedMessage(FIXMSG_LEVELTOOLOW);
// return bActionDone;
// }
if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_PICKUP)
{
// Check whether we have enougth place to hold this item or money
int tid = pMatter.GetTemplateID();
bool isMoney = pMatter.IsMoney() || GPDataTypeHelper.ISMONEYTID(tid);
// // Check whether we have a mine tool
// int iPack, iIndex, idTool;
// if (m_pHost.FindMineTool(tidMatter, &iPack, &iIndex, &idTool))
// {
// DATA_TYPE DataType;
// const MINE_ESSENCE* pData = (const MINE_ESSENCE*)g_pGame.GetElementDataMan().get_data_ptr(pMatter.GetTemplateID(), ID_SPACE_ESSENCE, DataType);
// if (DataType != DT_MINE_ESSENCE)
// {
// ASSERT(DataType == DT_MINE_ESSENCE);
// return bActionDone;
// }
// Money pickup should not be blocked by inventory space checks.
// Let the server validate (e.g. money cap), and only gate items by bag space.
if (isMoney || m_pHost.CanTakeItem(tid, 1))
{
// Send pickup asking and wait response command
UnityGameSession.RequestPickupItem(m_iObjectId, tid);
bActionDone = true;
}
else
{
// Print a notify message
//g_pGame.GetGameRun().AddFixedMessage(FIXMSG_PACKISFULL);
Debug.Log("Khong du cho trong de nhat item");
}
}
else if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_GATHER)
{ // m_iReason == TRACE_GATHER
int tidMatter = pMatter.GetTemplateID();
// Check mine level requirement
if (m_pHost.GetBasicProps().iLevel < pMatter.GetLevelReq())
{
//g_pGame.GetGameRun().AddFixedMessage(FIXMSG_LEVELTOOLOW);
Debug.Log("Cap do cua ban khong du de khai thac tai nguyen nay");
return bActionDone;
}
// if (m_pHost.GetCoolTime(GP_CT_PLAYER_GATHER))
// {
// g_pGame.GetGameRun().AddFixedMessage(FIXMSG_CMD_INCOOLTIME);
// }
// else
// {
// // Send gather asking and wait response command
// g_pGame.GetGameSession().c2s_CmdGatherMaterial(m_iObjectId, iPack, iIndex, idTool, pData.task_in);
// }
// }
// else
// {
// g_pGame.GetGameRun().AddFixedMessage(FIXMSG_NEEDTOOL);
// }
// }
//}
// Check whether we have a mine tool
int iPack = 0;
int iIndex = 0;
int idTool = 0;
if (m_pHost.FindMineTool(tidMatter, ref iPack, ref iIndex, ref idTool))
{
DATA_TYPE DataType = DATA_TYPE.DT_INVALID;
MINE_ESSENCE pData = (MINE_ESSENCE)ElementDataManProvider.GetElementDataMan().get_data_ptr((uint)pMatter.GetTemplateID(), ID_SPACE.ID_SPACE_ESSENCE, ref DataType);
if (DataType != DATA_TYPE.DT_MINE_ESSENCE)
{
//ASSERT(DataType == DT_MINE_ESSENCE);
return bActionDone;
}
// if (m_pHost.GetCoolTime(GP_CT_PLAYER_GATHER))
// {
// g_pGame.GetGameRun().AddFixedMessage(FIXMSG_CMD_INCOOLTIME);
// }
else
{
// Send gather asking and wait response command
UnityGameSession.c2s_CmdGatherMaterial(m_iObjectId, iPack, iIndex, idTool, (int)pData.task_in);
bActionDone = true;
}
}
else
{
//g_pGame.GetGameRun().AddFixedMessage(FIXMSG_NEEDTOOL);
}
}
}
return bActionDone;
}
@@ -531,6 +582,10 @@ namespace BrewMonster
public void SetTraceTarget(CECTracedObject pTraceObj, bool bUseAutoPF = false)
{
ResetUseAutoPF(bUseAutoPF);
if (pTraceObj == null)
{
return; // Invalid trace object / Invalid trace object
}
if (!pTraceObj.GetTargetObject() || pTraceObj.GetObjectID() == m_pHost.GetCharacterID())
{
// This is special case
@@ -568,10 +623,10 @@ namespace BrewMonster
{
return new CECTracedNPC(TraceObjectType.TRACE_NPC, iTraceObjId, m_pHost, iReason, bForceAttack);
}
//else if (GPDataTypeHelper.ISMATTERID(iTraceObjId))
//{
// return new CECTracedMatter(TraceObjectType.TRACE_MATTER, iTraceObjId, m_pHost, iReason);
//}
else if (GPDataTypeHelper.ISMATTERID(iTraceObjId))
{
return new CECTracedMatter(TraceObjectType.TRACE_MATTER, iTraceObjId, m_pHost, iReason);
}
return null;
}
@@ -596,17 +651,20 @@ namespace BrewMonster
return true;
}
if (m_bCheckTouch)
// 重要:不要用 m_bCheckTouch 在这里抑制 Touch 检测。
// 之前的移植里 m_bCheckTouch 会在 GroundMove 后被置为 false,导致下一帧永远不进入 OnTouchTarget。
// Important: do not suppress touch checks with m_bCheckTouch here.
// In this port, m_bCheckTouch can be forced false after GroundMove, making OnTouchTarget never fire.
if (IsGoodTimeToTouch())
{
if (IsGoodTimeToTouch())
// 注意:这里不能加 vExtent.y(胶囊半高),否则会把主角位置抬高导致 3D 距离变大,
// 进而 CanTouchTarget 永远不满足(OnTouchTarget 永远不会触发)。
// Note: Do NOT add vExtent.y (capsule half-height) here; touch uses 3D distance and this
// artificially increases the distance, preventing OnTouchTarget() from ever triggering.
if (m_pTraceObject.CanTouchFrom(m_pHost.GetPos()))
{
//OnTouchTarget();
//return true;
if (m_pTraceObject.CanTouchFrom(m_pHost.GetPos() + new A3DVECTOR3(0f, m_pHost.m_CDRInfo.vExtent.y, 0f)))
{
OnTouchTarget();
return true;
}
OnTouchTarget();
return true;
}
}
m_bCheckTouch = true;
@@ -797,7 +855,7 @@ namespace BrewMonster
// Operations
// On first tick
protected virtual void OnFirstTick()
protected override void OnFirstTick()
{
m_pHost.m_iMoveMode = (int)MoveMode.MOVE_MOVE;
m_bHaveMoved = false;
@@ -851,7 +909,7 @@ namespace BrewMonster
else if (!m_pHost.m_GndInfo.bOnGround)
iMoveMode = (int)GPMoveMode.GP_MOVE_FALL;
RaycastHit lastGroundHit;
//RaycastHit lastGroundHit;
//m_pHost.m_GndInfo.bOnGround = m_pHost.GroundCheck(out lastGroundHit);
if (m_pHost.m_GndInfo.bOnGround)
{
@@ -886,10 +944,9 @@ namespace BrewMonster
// CECIntelligentRoute::Instance().OnPlayerPosChange(vCurPos);
//}
if (cdr.vTPNormal.IsZero())
{
m_bCheckTouch = false;
}
// 不要在这里关闭 touch 检测:Tick() 的 touch 检测发生在移动之前,
// 如果这里把 m_bCheckTouch 置 false,会导致下一帧永远跳过触发逻辑。
// Do not disable touch checking here; touch checking happens before movement in Tick().
//if (!m_vCurDirH.IsZero())
//{
@@ -972,6 +1029,33 @@ namespace BrewMonster
}
public A3DVECTOR3 GetCurMovingDest()
{
// 对于采集/拾取物体:保持一定距离,避免角色模型顶住碰撞体抖动
// For gather/pickup matters: keep a stand-off distance to avoid jittering against colliders.
if (m_pTraceObject != null && m_pTraceObject.GetTraceType() == TraceObjectType.TRACE_MATTER)
{
CECMatter matter = m_pTraceObject.GetTargetObject() as CECMatter;
if (matter != null)
{
float keepDist = matter.GetGatherDist();
if (keepDist > 0.01f)
{
A3DVECTOR3 hostPos = m_pHost.GetPos();
A3DVECTOR3 targetPos = matter.GetPos();
A3DVECTOR3 deltaH = targetPos - hostPos;
deltaH.y = 0.0f;
float distH = deltaH.MagnitudeH();
if (distH > keepDist + 0.05f && distH > 1e-4f)
{
deltaH.Normalize();
A3DVECTOR3 dest = targetPos - deltaH * keepDist;
// keep Y stable; GroundMove will resolve final Y anyway
dest.y = hostPos.y;
return dest;
}
}
}
}
return m_pTraceObject.GetTargetPos();
}
public void UpdateUseAutoPF()
@@ -2,6 +2,7 @@ using BrewMonster.Managers;
using BrewMonster.Network;
using BrewMonster.Scripts;
using CSNetwork.GPDataType;
using PerfectWorld.Scripts;
using System;
using UnityEngine;
@@ -25,23 +26,107 @@ namespace BrewMonster
public void OnMsgLBtnClick()
{
// 停止自动策略 / Stop auto policy
// Note: Auto policy check would go here if implemented
int idTraceTarget = 0, idSelTarget = 0;
bool bForceAttack = false;
int iTraceReason = CECHPWorkTrace.Trace_reason.TRACE_NONE;
bool bWikiMonster = false;
ray = mainCam.ScreenPointToRay(Input.mousePosition);
Vector3 vStart = mainCam.transform.position;
Vector3 vDest = ray.GetPoint(1.0f);
Vector3 vDelta = vDest - vStart;
// Check for NPC with pate text (hover text) first
// Note: This would require GetMouseOnPateTextNPC implementation
// For now, we'll proceed with raycast
if (Physics.Raycast(ray, out hit))
{
if (hit.collider.gameObject.TryGetComponent<CECObject>(out CECObject clickedObject))
// Check if hit terrain, building, or forest (no CECObject component)
if (!hit.collider.gameObject.TryGetComponent<CECObject>(out CECObject clickedObject))
{
int idObject = CECObject.GetObjectID(clickedObject);
if (idObject != 0)
//ENABLE LATER - CURRENT WORKING FINE
// Hit terrain / building / forest / Hit terrain
// if (m_pWorkMan.IsSitting())
// {
// UnityGameSession.c2s_CmdStandUp();
// return;
// }
// if (!CanDo(ActionCanDo.CANDO_MOVETO))
// return;
//
// // Calculate move destination / Hit terrain
// Vector3 vMoveDest = hit.point;
// A3DVECTOR3 a3dMoveDest = EC_Utility.ToA3DVECTOR3(vMoveDest);
//
// if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl))
// {
// // Ctrl pressed - direct goto / Ctrl pressed
// // Note: c2s_CmdGoto not implemented, using work system instead
// // UnityGameSession.c2s_CmdGoto(a3dMoveDest.x, a3dMoveDest.y, a3dMoveDest.z);
// // For now, use work system even with Ctrl
// }
// // else removed - always use work system
// {
// CECHPWork pWork = m_pWorkMan.GetWork(CECHPWork.Host_work_ID.WORK_MOVETOPOS);
// if (pWork != null)
// {
// CECHPWorkMove pWorkMove = pWork as CECHPWorkMove;
// if (pWorkMove != null)
// {
// pWorkMove.SetDestination(CECHPWorkMove.DestTypes.DEST_2D, a3dMoveDest);
// pWorkMove.SetUseAutoMoveDialog(false);
// // Note: PlayMoveTargetGFX would go here if implemented
// }
// }
// else if (m_pWorkMan.CanStartWork(CECHPWork.Host_work_ID.WORK_MOVETOPOS))
// {
// // If destination is too near, ignore it. / If destination is too near, ignore it.
// A3DVECTOR3 vDist = a3dMoveDest - GetPos();
// float fDistH = (float)Math.Sqrt(vDist.x * vDist.x + vDist.z * vDist.z);
// if (fDistH > 0.5f)
// {
// CECHPWorkMove pWorkMove = (CECHPWorkMove)m_pWorkMan.CreateWork(CECHPWork.Host_work_ID.WORK_MOVETOPOS);
// pWorkMove.SetDestination(CECHPWorkMove.DestTypes.DEST_2D, a3dMoveDest);
// // Note: PlayMoveTargetGFX would go here if implemented
// m_pWorkMan.StartWork_p1(pWorkMove);
// }
// }
// }
return;
}
// Hit an object / Hit a object
int idObject = CECObject.GetObjectID(clickedObject);
if (idObject != 0)
{
// Check if it's a matter object / Check if it's a matter object
if (clickedObject.IsMatter())
{
CECMatter pMatter = clickedObject as CECMatter;
if (pMatter != null)
{
idTraceTarget = pMatter.GetMatterID();
// Check if it's a mine (gather) or item (pickup) / Check if it's a mine (gather) or item (pickup)
bool bIsMine = pMatter.IsMine();
iTraceReason = bIsMine ? CECHPWorkTrace.Trace_reason.TRACE_GATHER : CECHPWorkTrace.Trace_reason.TRACE_PICKUP;
}
}
// Check if it's a dynamic object / Check if it's a dynamic object
// else if (clickedObject.gameObject.CompareTag("DynamicObject")) // ECENT_DYN_OBJ
// {
// return; // Dynamic objects are not clickable
// }
else
{
// NPC or Player / NPC or Player
CECNPC pNPC = EC_ManMessageMono.Instance.CECNPCMan.GetNPC(idObject);
if (pNPC != null)
{
// Msg.dwParam4 is double click flag / Msg.dwParam4 is double click flag
if (!pNPC.IsDead() && m_idSelTarget == idObject)
{
idTraceTarget = idObject;
@@ -53,6 +138,7 @@ namespace BrewMonster
if (idTraceTarget != 0)
{
// bForceAttack = glb_GetForceAttackFlag(&Msg.dwParam3);
if (AttackableJudge(idObject, bForceAttack) == 1)
iTraceReason = CECHPWorkTrace.Trace_reason.TRACE_ATTACK;
else if (pNPC.IsServerNPC())
@@ -64,12 +150,12 @@ namespace BrewMonster
}
else
{
// pCDS.m_RayTraceRt.iEntity == ECENT_PLAYER
// pCDS.m_RayTraceRt.iEntity == ECENT_PLAYER / pCDS.m_RayTraceRt.iEntity == ECENT_PLAYER
CECPlayer pPlayer = EC_ManMessageMono.Instance.EC_ManPlayer.GetPlayer(idObject);
// 1. Msg.dwParam4 is double click flag.
// 2. Buddy player counld't be traced
if (!pPlayer.IsDead() /*&& pPlayer.GetCharacterID() != m_iBuddyId*/ &&
// 1. Msg.dwParam4 is double click flag. / 1. Msg.dwParam4 is double click flag.
// 2. Buddy player counld't be traced / 2. Buddy player counld't be traced
if (pPlayer != null && !pPlayer.IsDead() /*&& pPlayer.GetCharacterID() != m_iBuddyId*/ &&
(m_idSelTarget == idObject /*|| (Msg.dwParam4 && m_idUCSelTarget == idObject)*/))
{
idTraceTarget = idObject;
@@ -80,31 +166,73 @@ namespace BrewMonster
else if (pPlayer.GetBoothState() != 0)
iTraceReason = CECHPWorkTrace.Trace_reason.TRACE_TALK;
}
else
else if (pPlayer != null)
{
idSelTarget = idObject;
}
}
// cancel this action if not selectable
// cancel this action if not selectable / cancel this action if not selectable
if (!CanSelectTarget(idTraceTarget))
{
idTraceTarget = 0;
//return;
return;
}
// Check for wiki monster / Check for wiki monster
// Note: CDlgAutoHelp::IsAutoHelp() check would go here if implemented
// if (CDlgAutoHelp.IsAutoHelp() && pNPC != null && pNPC.IsMonsterNPC())
// bWikiMonster = true;
}
}
}
// Tell server we select a target
if (idSelTarget != 0 && m_idSelTarget != idSelTarget)
else
{
m_idUCSelTarget = idSelTarget;
SelectTarget(m_idUCSelTarget);
// Nothing is clicked / Nothing is clicked
if (m_pWorkMan.IsSitting())
{
UnityGameSession.c2s_CmdStandUp();
return;
}
if (!CanDo(ActionCanDo.CANDO_MOVETO))
return;
// Move on the clicked direction / Move on the clicked direction
Vector3 vMoveDir = vDelta;
vMoveDir.y = 0.0f;
if (vMoveDir.sqrMagnitude < 0.0001f)
return;
vMoveDir.Normalize();
A3DVECTOR3 a3dMoveDir = EC_Utility.ToA3DVECTOR3(vMoveDir);
CECHPWork pWork = m_pWorkMan.GetWork(CECHPWork.Host_work_ID.WORK_MOVETOPOS);
if (pWork != null)
{
CECHPWorkMove pWorkMove = pWork as CECHPWorkMove;
if (pWorkMove != null)
{
pWorkMove.SetDestination(CECHPWorkMove.DestTypes.DEST_DIR, a3dMoveDir);
}
}
else if (m_pWorkMan.CanStartWork(CECHPWork.Host_work_ID.WORK_MOVETOPOS))
{
CECHPWorkMove pWorkMove = (CECHPWorkMove)m_pWorkMan.CreateWork(CECHPWork.Host_work_ID.WORK_MOVETOPOS);
pWorkMove.SetDestination(CECHPWorkMove.DestTypes.DEST_DIR, a3dMoveDir);
m_pWorkMan.StartWork_p1(pWorkMove);
}
return;
}
// Handle trace target / Handle trace target
if (idTraceTarget != 0)
{
if (m_pWorkMan.IsSitting())
{
UnityGameSession.c2s_CmdStandUp();
return;
}
// Trace a object / Trace a object
if (iTraceReason == CECHPWorkTrace.Trace_reason.TRACE_ATTACK)
{
if (!CanDo(ActionCanDo.CANDO_MELEE))
@@ -115,14 +243,15 @@ namespace BrewMonster
{
if (!CanDo(ActionCanDo.CANDO_MOVETO))
return;
CECHPWork pWork;
if (iTraceReason == CECHPWorkTrace.Trace_reason.TRACE_PICKUP)
{
//PickupObject(idTraceTarget, false);
PickupObject(idTraceTarget, false);
}
else if (iTraceReason == CECHPWorkTrace.Trace_reason.TRACE_GATHER)
{
//PickupObject(idTraceTarget, true);
PickupObject(idTraceTarget, true);
}
else if ((pWork = m_pWorkMan.GetWork(CECHPWork.Host_work_ID.WORK_TRACEOBJECT)) != null)
{
@@ -137,6 +266,13 @@ namespace BrewMonster
}
}
}
// Tell server we select a target / Tell server we select a target
if (idSelTarget != 0 && m_idSelTarget != idSelTarget)
{
m_idUCSelTarget = idSelTarget;
SelectTarget(m_idUCSelTarget);
}
}
public void OnMsgHstJump()
@@ -226,5 +362,78 @@ namespace BrewMonster
//m_GndInfo.bOnGround = GroundCheck(out lastGroundHit);
OnMsgHstJump();
}
// Pickup an object / Pickup an object
public bool PickupObject(int idTarget, bool bGather)
{
if (IsDead() || IsSpellingMagic() || idTarget == 0 || !GPDataTypeHelper.ISMATTERID(idTarget))
return false;
// Check matter type / Check matter type
CECMatter pMatter = EC_ManMessageMono.Instance.EC_ManMatter.GetMatter(idTarget);
if (pMatter == null)
{
#if UNITY_EDITOR || DEVELOPMENT_BUILD
Debug.LogWarning(
$"PickupObject: GetMatter returned null. idTarget={idTarget} (0x{idTarget:X8}) bGather={bGather}");
#endif
return false;
}
if (bGather != pMatter.IsMine())
return false;
if (bGather && !CanGatherMatter(pMatter))
return false;
bool bOK = true;
// Trace a object / Trace a object
CECHPWork pWork = m_pWorkMan.GetWork(CECHPWork.Host_work_ID.WORK_TRACEOBJECT);
if (pWork != null)
{
CECHPWorkTrace pWorkTrace = pWork as CECHPWorkTrace;
if (pWorkTrace != null)
{
CECTracedObject traceTarget = pWorkTrace.CreatTraceTarget(idTarget,
bGather ? CECHPWorkTrace.Trace_reason.TRACE_GATHER : CECHPWorkTrace.Trace_reason.TRACE_PICKUP,
false);
if (traceTarget != null)
{
pWorkTrace.SetTraceTarget(traceTarget);
bOK = true;
}
}
}
else if (m_pWorkMan.CanStartWork(CECHPWork.Host_work_ID.WORK_TRACEOBJECT))
{
CECHPWorkTrace pWorkTrace = (CECHPWorkTrace)m_pWorkMan.CreateWork(CECHPWork.Host_work_ID.WORK_TRACEOBJECT);
if (pWorkTrace != null)
{
CECTracedObject traceTarget = pWorkTrace.CreatTraceTarget(idTarget,
bGather ? CECHPWorkTrace.Trace_reason.TRACE_GATHER : CECHPWorkTrace.Trace_reason.TRACE_PICKUP,
false);
if (traceTarget != null)
{
pWorkTrace.SetTraceTarget(traceTarget);
m_pWorkMan.StartWork_p1(pWorkTrace);
bOK = true;
}
}
}
return bOK;
}
// Check whether host can gather specified matter / Check whether host can gather specified matter
public bool CanGatherMatter(CECMatter pMatter)
{
if (pMatter == null || !pMatter.IsMine())
return false;
// TODO: Add level requirement check and other gather validations / TODO: Add level requirement check and other gather validations
// For now, return true if it's a mine / For now, return true if it's a mine
return true;
}
}
}
@@ -46,6 +46,22 @@ namespace PerfectWorld.Scripts.Managers
// Storage for matter data that players can access later
private Dictionary<int, info_matter> matterDataStorage = new Dictionary<int, info_matter>();
private Dictionary<int, CECMatter> m_MatterTab = new Dictionary<int, CECMatter>();
/// <summary>
/// Unity-only recovery: ensure an existing scene matter is present in the manager table.
/// This helps recover after Unity script/domain reload where Dictionaries are not serialized.
/// </summary>
public void RegisterExistingMatter(CECMatter matter)
{
if (matter == null)
return;
int mid = matter.GetMatterID();
if (mid == 0)
return;
m_MatterTab[mid] = matter;
}
public bool ProcessMessage(ECMSG Msg)
{
if (Msg.iSubID == 0)
@@ -4,6 +4,7 @@ using BrewMonster;
using BrewMonster.Managers;
using CSNetwork.GPDataType;
using CSNetwork.Protocols;
using PerfectWorld.Scripts;
using UnityEngine;
///////////////////////////////////////////////////////////////////////////
@@ -229,8 +230,8 @@ public class CECObject : MonoBehaviour
return ((CECPlayer)pObject).GetCharacterID();
else if (pObject.IsNPC())
return ((CECNPC)pObject).GetNPCID();
//else if (pObject.IsMatter())
// return ((CECMatter*)pObject)->GetMatterID();
else if (pObject.IsMatter())
return ((CECMatter)pObject).GetMatterID();
else
return 0;
}
@@ -4,8 +4,9 @@ namespace BrewMonster
{
protected int m_iMoneyCnt; // Amount of money the player has
protected int m_iMaxMoney;
public int GetMoneyAmount() { return m_iMoneyCnt; }
public int GetMaxMoneyAmount() { return m_iMaxMoney; }
public byte GetShapeMask()
{
// restore the original data from 8~15 bit
@@ -250,6 +250,10 @@ namespace BrewMonster
{
return (m_dwStates & PlayerNPCState.GP_STATE_CORPSE) != 0;
}
public bool IsInvisible()
{
return (m_dwStates & PlayerNPCState.GP_STATE_INVISIBLE) != 0;
}
public bool IsValidAction(int iIndex)
{
@@ -302,6 +302,16 @@ namespace CSNetwork.C2SCommand
public byte pvpMask;
}
// Gather material command
public struct cmd_gather_material
{
public int mid; // Matter ID
public ushort tool_pack; // Tool package
public ushort tool_index; // Tool index
public int tool_type; // Tool type ID
public int id_task; // Task ID
}
public struct cmd_error_msg
{
public int iMessage;
@@ -743,5 +743,18 @@ namespace CSNetwork.C2SCommand
// Serialize the command and return the serialized data
return SerializeCommand(CommandID.AUTO_TEAM_SET_GOAL, pCmd);
}
public static Octets c2s_CmdGatherMaterial(int idMatter, int iToolPack, int iToolIdx, int idTool, int idTask)
{
var cmd = new cmd_gather_material
{
mid = idMatter,
tool_pack = (ushort)iToolPack,
tool_index = (ushort)iToolIdx,
tool_type = idTool,
id_task = idTask
};
return SerializeCommand(CommandID.GATHER_MATERIAL, cmd);
}
}
}
@@ -1448,6 +1448,11 @@ namespace CSNetwork.GPDataType
return (id & 0x80000000) != 0 && (id & 0x40000000) == 0;
}
public static bool ISMONEYTID(int tid)
{
return tid == 3044;
}
public static bool ISMATTERID(int id)
{
return (id & 0xC0000000) == 0xC0000000;
@@ -276,6 +276,12 @@ namespace CSNetwork
gamedatasendRequest.Data = CSNetwork.C2SCommand.C2SCommandFactory.CreateGetMallShopping(count, goodsArray);
SendProtocol(gamedatasendRequest);
}
public void c2s_CmdGatherMaterial(int idMatter, int iToolPack, int idToolIndex, int idTool, int idTask)
{
gamedatasend gamedatasendRequest = new gamedatasend();
gamedatasendRequest.Data = CSNetwork.C2SCommand.C2SCommandFactory.c2s_CmdGatherMaterial(idMatter, iToolPack, idToolIndex, idTool, idTask);
SendProtocol(gamedatasendRequest);
}
public void RequestOwnItemInfoAsync(
byte byPackage,
@@ -78,8 +78,10 @@ namespace BrewMonster.Managers
(iAliveFlag == 2 && !(pObject as CECPlayer).IsDead()))
return null;
}
//else if (GPDataTypeHelper.ISMATTERID(idObject))
// pObject = GetMatterMan()->GetMatter(idObject);
else if (GPDataTypeHelper.ISMATTERID((int)idObject))
{
pObject = EC_ManMatter.GetMatter((int)idObject);
}
return pObject;
}
@@ -360,7 +360,10 @@ namespace BrewMonster.Network
{
Instance._gameSession.c2s_SendCmdAutoTeamSetGoal(type, goal_id, op);//{ ::c2s_SendCmdAutoTeamSetGoal(type, goal_id, op); }
}
public static void c2s_CmdGatherMaterial(int idMatter, int iToolPack, int idToolIndex, int idTool, int idTask)
{
Instance._gameSession.c2s_CmdGatherMaterial(idMatter, iToolPack, idToolIndex, idTool, idTask);
}
#endregion
public static void GetRoleBaseInfo(int iNumRole, List<int> aRoleIDs)
+285 -139
View File
@@ -1,140 +1,286 @@
using System.Reflection;
using System.Threading.Tasks;
using BrewMonster;
using BrewMonster.Network;
using BrewMonster.Scripts;
using CSNetwork.GPDataType;
using ModelRenderer.Scripts.Common;
using PerfectWorld.Scripts.Managers;
using UnityEngine;
namespace PerfectWorld.Scripts
{
public class CECMatter : CECObject
{
// Matter information got from server
public struct INFO
{
public int mid; // Matter id
public int tid; // Template id
}
protected EC_ManMatter m_pMatterMan;
protected INFO m_MatterInfo;
public void SetMatterInfo(INFO matterInfo)
{
m_MatterInfo = matterInfo;
}
public static async Task<CECMatter> Init(info_matter Info)
{
INFO matterInfo = new INFO();
matterInfo.mid = Info.mid;
matterInfo.tid = Info.tid & 0x0000ffff;
// get the matter template from elementdataman
DATA_TYPE DataType = DATA_TYPE.DT_INVALID;
var matterData = ElementDataManProvider.GetElementDataMan().get_data_ptr((uint)matterInfo.tid, ID_SPACE.ID_SPACE_ESSENCE, ref DataType);
if (matterData != null)
{
var matterType = matterData.GetType();
var fileMatterField = matterType.GetField("file_matter", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (fileMatterField == null)
{
fileMatterField = matterType.GetField("file_model", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
}
if (fileMatterField != null)
{
var fileMatterValue = fileMatterField.GetValue(matterData);
string filePath = ByteToStringUtils.ByteArrayToCP936String((byte[])fileMatterValue);
var matterPrefab = await AddressableManager.Instance.LoadPrefabAsync(AFile.NormalizePath(filePath.ToLower(), true));
if (matterPrefab != null)
{
var matterObject = Instantiate(matterPrefab);
matterObject.transform.position = new Vector3(Info.pos.x, Info.pos.y, Info.pos.z);
matterObject.transform.localScale = new Vector3(1f, 1f, 1f);
matterObject.transform.localRotation = Quaternion.identity;
matterObject.SetActive(true);
// Add a collider if it doesn't have one
if (matterObject.GetComponent<Collider>() == null)
{
var collider = matterObject.AddComponent<BoxCollider>();
collider.size = matterObject.GetComponentInChildren<Renderer>().bounds.size;
}
// Create text object to display item name above the cube
// CreateItemNameText(matterObject, info.tid);
// Add a script to handle click events
// MatterCubeClickHandler clickHandler = matterObject.AddComponent<MatterCubeClickHandler>();
// clickHandler.Initialize(Info.mid, this);
CECMatter matterScript = matterObject.AddComponent<CECMatter>();
matterScript.SetMatterInfo(matterInfo);
// Store reference to the cube
// matterGameObjects[info.mid] = matterObject;
return matterScript;
}
else
{
Debug.LogWarning($"Failed to load matter prefab from path: {filePath}");
}
}
else
{
Debug.LogWarning($"file_matter field not found on matter data type {matterType.FullName}");
}
}
return null;
}
private new void Update()
{
bool inputPressed = false;
Vector2 screenPosition = Vector2.zero;
// Check for touch input (mobile)
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began)
{
inputPressed = true;
screenPosition = touch.position;
}
}
// Check for mouse input (desktop)
else if (Input.GetMouseButtonDown(0))
{
inputPressed = true;
screenPosition = Input.mousePosition;
}
if (inputPressed)
{
Camera mainCamera = Camera.main;
if (mainCamera == null)
return;
Ray ray = mainCamera.ScreenPointToRay(screenPosition);
RaycastHit[] hits = Physics.RaycastAll(ray);
foreach (RaycastHit hit in hits)
{
if (hit.collider.gameObject == this.gameObject ||
hit.collider.transform.IsChildOf(this.transform))
{
Debug.Log($"CECMatter::RaycastHit():: mid: {m_MatterInfo.mid}");
UnityGameSession.RequestPickupItem(m_MatterInfo.mid, m_MatterInfo.tid);
break;
}
}
}
}
}
using System.Reflection;
using System.Threading.Tasks;
using BrewMonster;
using BrewMonster.Network;
using BrewMonster.Scripts;
using CSNetwork.GPDataType;
using ModelRenderer.Scripts.Common;
using PerfectWorld.Scripts.Managers;
using UnityEngine;
namespace PerfectWorld.Scripts
{
public class CECMatter : CECObject
{
// Matter information got from server
public struct INFO
{
public int mid; // Matter id
public int tid; // Template id
}
protected EC_ManMatter m_pMatterMan;
protected INFO m_MatterInfo;
protected int m_iLevelReq;
protected float m_fGatherDist;
protected uint m_dwMatterType; // Matter type flags / Matter type flags
private bool m_registeredToManMatter = false;
// Matter type constants / Matter type constants
public const uint MATTER_UNKNOWN = 0;
public const uint MATTER_ITEM = 1;
public const uint MATTER_MINE = 2;
public const uint MATTER_MONEY = 3;
public const uint MATTER_TYPEMASK = 0xff;
// Constructor / Constructor
public CECMatter()
{
m_iCID = Class_ID.OCID_MATTER;
}
// Awake is called when the component is initialized / Awake is called when the component is initialized
private void Awake()
{
m_iCID = Class_ID.OCID_MATTER; // Ensure CID is set after Unity initialization / Ensure CID is set after Unity initialization
TryRegisterToManMatter();
}
private void OnEnable()
{
TryRegisterToManMatter();
}
private void TryRegisterToManMatter()
{
if (m_registeredToManMatter)
return;
// Only register once we have a valid mid.
int mid = GetMatterID();
if (mid == 0)
return;
var mono = BrewMonster.Managers.EC_ManMessageMono.Instance;
if (mono == null || mono.EC_ManMatter == null)
return;
mono.EC_ManMatter.RegisterExistingMatter(this);
m_registeredToManMatter = true;
}
// Override SetUpCECObject to set class ID / Override SetUpCECObject to set class ID
public override void SetUpCECObject()
{
base.SetUpCECObject();
m_iCID = Class_ID.OCID_MATTER; // Set after base call to override the reset / Set after base call to override the reset
}
public void SetMatterInfo(INFO matterInfo)
{
m_MatterInfo = matterInfo;
}
public int GetTemplateID()
{
return m_MatterInfo.tid;
}
public int GetLevelReq()
{
return m_iLevelReq;
}
public float GetGatherDist()
{
// 采集/拾取距离:某些情况下(例如场景预制体未走 Init 流程)m_fGatherDist 可能为 0。
// Gather/pickup distance: in some cases (e.g. scene prefab not created via Init) m_fGatherDist may be 0.
// Provide a safe default consistent with native behavior.
return m_fGatherDist > 0.01f ? m_fGatherDist : 3.0f;
}
// Is this matter a mine? / Is this matter a mine?
public bool IsMine()
{
return (m_dwMatterType & MATTER_TYPEMASK) == MATTER_MINE;
}
// Is this matter an item? / Is this matter an item?
public bool IsItem()
{
return (m_dwMatterType & MATTER_TYPEMASK) == MATTER_ITEM;
}
// Is this matter money? / Is this matter money?
public bool IsMoney()
{
return (m_dwMatterType & MATTER_TYPEMASK) == MATTER_MONEY;
}
public static async Task<CECMatter> Init(info_matter Info)
{
INFO matterInfo = new INFO();
matterInfo.mid = Info.mid;
matterInfo.tid = Info.tid & 0x0000ffff;
// get the matter template from elementdataman
DATA_TYPE DataType = DATA_TYPE.DT_INVALID;
var matterData = ElementDataManProvider.GetElementDataMan().get_data_ptr((uint)matterInfo.tid, ID_SPACE.ID_SPACE_ESSENCE, ref DataType);
// Determine matter type based on DataType / Determine matter type based on DataType
uint dwMatterType = MATTER_UNKNOWN;
if (DataType == DATA_TYPE.DT_MINE_ESSENCE)
{
dwMatterType = MATTER_MINE;
}
else
{
// Default to ITEM for other essence types / Default to ITEM for other essence types
dwMatterType = MATTER_ITEM;
}
// 钱币掉落 / Money drop
// 服务器用特殊 tid 表示金币/银币拾取物;不应占用背包格子。
// The server uses a special tid to represent money matters; they should not consume inventory slots.
if (GPDataTypeHelper.ISMONEYTID(matterInfo.tid))
{
dwMatterType = MATTER_MONEY;
}
if (matterData != null)
{
var matterDataType = matterData.GetType();
var fileMatterField = matterDataType.GetField("file_matter", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (fileMatterField == null)
{
fileMatterField = matterDataType.GetField("file_model", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
}
if (fileMatterField != null)
{
var fileMatterValue = fileMatterField.GetValue(matterData);
string filePath = ByteToStringUtils.ByteArrayToCP936String((byte[])fileMatterValue);
var matterPrefab = await AddressableManager.Instance.LoadPrefabAsync(AFile.NormalizePath(filePath.ToLower(), true));
if (matterPrefab != null)
{
var matterObject = Instantiate(matterPrefab);
matterObject.transform.position = new Vector3(Info.pos.x, Info.pos.y, Info.pos.z);
matterObject.transform.localScale = new Vector3(1f, 1f, 1f);
matterObject.transform.localRotation = Quaternion.identity;
matterObject.SetActive(true);
// Add a collider if it doesn't have one
if (matterObject.GetComponent<Collider>() == null)
{
var collider = matterObject.AddComponent<BoxCollider>();
collider.size = matterObject.GetComponentInChildren<Renderer>().bounds.size;
}
// Create text object to display item name above the cube
// CreateItemNameText(matterObject, info.tid);
// Add a script to handle click events
// MatterCubeClickHandler clickHandler = matterObject.AddComponent<MatterCubeClickHandler>();
// clickHandler.Initialize(Info.mid, this);
CECMatter matterScript = matterObject.AddComponent<CECMatter>();
// Set CID immediately after AddComponent (before SetUpCECObject resets it) / Set CID immediately after AddComponent (before SetUpCECObject resets it)
matterScript.m_iCID = Class_ID.OCID_MATTER;
matterScript.SetMatterInfo(matterInfo);
matterScript.m_dwMatterType = dwMatterType; // Set matter type / Set matter type
// Set level requirement and gather distance for mines / Set level requirement and gather distance for mines
if (dwMatterType == MATTER_MINE && DataType == DATA_TYPE.DT_MINE_ESSENCE)
{
var levelReqField = matterData.GetType().GetField("level_required", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (levelReqField != null)
{
matterScript.m_iLevelReq = (int)levelReqField.GetValue(matterData);
}
var gatherDistField = matterData.GetType().GetField("gather_dist", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (gatherDistField != null)
{
float gatherDist = (float)gatherDistField.GetValue(matterData);
matterScript.m_fGatherDist = gatherDist > 3.0f ? gatherDist - 1.0f : 3.0f;
}
else
{
matterScript.m_fGatherDist = 3.0f;
}
}
else
{
// For non-mine items, set a default pickup distance / For non-mine items, set a default pickup distance
matterScript.m_fGatherDist = 3.0f; // Default pickup distance / Default pickup distance
}
matterScript.SetUpCECObject(); // This will reset m_iCID, so we set it again after / This will reset m_iCID, so we set it again after
// Force set CID again after SetUpCECObject (which resets it to OCID_OBJECT) / Force set CID again after SetUpCECObject (which resets it to OCID_OBJECT)
matterScript.m_iCID = Class_ID.OCID_MATTER;
// Store reference to the cube
// matterGameObjects[info.mid] = matterObject;
return matterScript;
}
else
{
Debug.LogWarning($"Failed to load matter prefab from path: {filePath}");
}
}
else
{
Debug.LogWarning($"file_matter field not found on matter data type {matterDataType.FullName}");
}
}
return null;
}
private new void Update()
{
// Recovery: after Unity domain reload, manager dictionaries reset but scene objects persist.
// Keep trying until we successfully register.
if (!m_registeredToManMatter)
TryRegisterToManMatter();
// Check for touch input (mobile)
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began)
{
// Touch handling intentionally disabled for now. / Touch handling intentionally disabled for now.
}
}
// Check for mouse input (desktop)
// else if (Input.GetMouseButtonDown(0))
// {
// inputPressed = true;
// screenPosition = Input.mousePosition;
// }
//
// if (inputPressed)
// {
// Camera mainCamera = Camera.main;
// if (mainCamera == null)
// return;
//
// Ray ray = mainCamera.ScreenPointToRay(screenPosition);
//
// RaycastHit[] hits = Physics.RaycastAll(ray);
//
// foreach (RaycastHit hit in hits)
// {
// if (hit.collider.gameObject == this.gameObject ||
// hit.collider.transform.IsChildOf(this.transform))
// {
// Debug.Log($"CECMatter::RaycastHit():: mid: {m_MatterInfo.mid}");
// UnityGameSession.RequestPickupItem(m_MatterInfo.mid, m_MatterInfo.tid);
// break;
// }
// }
// }
}
public int GetMatterID()
{
return m_MatterInfo.mid;
}
}
}
+62
View File
@@ -5304,5 +5304,67 @@ namespace BrewMonster
CANDO_REBUILDPET = 19,
CANDO_SWITCH_PARALLEL_WORLD = 20;
}
public bool CanTakeItem(int idItem, int iAmount)
{
bool bCanPick = false;
if (GPDataTypeHelper.ISMONEYTID(idItem))
{
if (GetMoneyAmount() < GetMaxMoneyAmount())
bCanPick = true;
}
else
{
if (PackInventory.CanAddItem(idItem, iAmount, false) >= 0)
bCanPick = true;
}
return bCanPick;
}
public bool FindMineTool(int tidMine, ref int piPack, ref int piIndex, ref int pidTool)
{
if (tidMine == 0)
return false;
DATA_TYPE DataType = DATA_TYPE.DT_INVALID;
object pDataPtr = ElementDataManProvider.GetElementDataMan().get_data_ptr((uint)tidMine, ID_SPACE.ID_SPACE_ESSENCE, ref DataType);
if (DataType != DATA_TYPE.DT_MINE_ESSENCE)
{
//ASSERT(DataType != DATA_TYPE.DT_MINE_ESSENCE);
return false;
}
MINE_ESSENCE pData = (MINE_ESSENCE)pDataPtr;
int idTool = (int)pData.id_equipment_required;
bool bRet = true;
if (idTool != 0)
{
int iIndex = PackInventory.FindItem(idTool);
if (iIndex >= 0)
{
piPack = EC_Inventory.Inventory_type.IVTRTYPE_PACK;
piIndex = iIndex;
pidTool = idTool;
}
else if ((iIndex = TaskInventory.FindItem(idTool)) >= 0)
{
piPack = EC_Inventory.Inventory_type.IVTRTYPE_TASKPACK;
piIndex = iIndex;
pidTool = idTool;
}
else
bRet = false;
}
else
{
piPack = 0;
piIndex = 0;
pidTool = 0;
}
return bRet;
}
}
}