From fb6f3b3c13019f822c9f72da3dc7172c845a66b3 Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Mon, 22 Dec 2025 17:49:59 +0700 Subject: [PATCH 01/14] Add CanTakeItem and FindMineTool method --- Assets/Scripts/CECHostPlayer.cs | 62 +++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/Assets/Scripts/CECHostPlayer.cs b/Assets/Scripts/CECHostPlayer.cs index 5c026c763d..dd01450280 100644 --- a/Assets/Scripts/CECHostPlayer.cs +++ b/Assets/Scripts/CECHostPlayer.cs @@ -5295,5 +5295,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; + } } } \ No newline at end of file From acc5a5af6e3eb8c97b8007a7a0194f8c197b8c5a Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Mon, 22 Dec 2025 17:50:57 +0700 Subject: [PATCH 02/14] Add properties and disable duplicate raycast, add method to gather --- .../PerfectWorld/Scripts/Objet/CECMatter.cs | 418 ++++++++++++------ 1 file changed, 279 insertions(+), 139 deletions(-) diff --git a/Assets/PerfectWorld/Scripts/Objet/CECMatter.cs b/Assets/PerfectWorld/Scripts/Objet/CECMatter.cs index 5ab0c86344..ab57bcf6c6 100644 --- a/Assets/PerfectWorld/Scripts/Objet/CECMatter.cs +++ b/Assets/PerfectWorld/Scripts/Objet/CECMatter.cs @@ -1,140 +1,280 @@ -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 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() == null) - { - var collider = matterObject.AddComponent(); - collider.size = matterObject.GetComponentInChildren().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(); - // clickHandler.Initialize(Info.mid, this); - CECMatter matterScript = matterObject.AddComponent(); - 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 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; + } + // Note: Money type detection would go here if needed / Note: Money type detection would go here if needed + + 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() == null) + { + var collider = matterObject.AddComponent(); + collider.size = matterObject.GetComponentInChildren().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(); + // clickHandler.Initialize(Info.mid, this); + CECMatter matterScript = matterObject.AddComponent(); + // 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; + } + } } \ No newline at end of file From da055c5ece92f501aa1f26c2ca8316f62fbc620a Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Mon, 22 Dec 2025 17:51:08 +0700 Subject: [PATCH 03/14] Add c2s_CmdGatherMaterial --- Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs b/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs index c431dd82cb..69eae9a929 100644 --- a/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs +++ b/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs @@ -359,7 +359,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 aRoleIDs) From 2a74bd359220ae5751e1383892017c8991cb2a75 Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Mon, 22 Dec 2025 17:52:32 +0700 Subject: [PATCH 04/14] Add GetMatter --- Assets/PerfectWorld/Scripts/Network/EC_ManMessageMono.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Assets/PerfectWorld/Scripts/Network/EC_ManMessageMono.cs b/Assets/PerfectWorld/Scripts/Network/EC_ManMessageMono.cs index 95a4620315..4308913f39 100644 --- a/Assets/PerfectWorld/Scripts/Network/EC_ManMessageMono.cs +++ b/Assets/PerfectWorld/Scripts/Network/EC_ManMessageMono.cs @@ -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; } From 8c449041d49d72f77f61a7bda70b4d7945901505 Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Mon, 22 Dec 2025 17:52:59 +0700 Subject: [PATCH 05/14] Add ISMONEYTID --- Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs index f3559b0b20..495739284f 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs @@ -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; From cb4e27c2b9aa7228e10a389c1af81f205cbbc062 Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Mon, 22 Dec 2025 17:53:16 +0700 Subject: [PATCH 06/14] Add c2s_CmdGatherMaterial --- .../PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs index 65f016b611..20a122452f 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs @@ -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, From a4e9e85bed258c7ea11a8c24ba9406067549394d Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Mon, 22 Dec 2025 17:53:48 +0700 Subject: [PATCH 07/14] Add c2s_CmdGatherMaterial and struct cmd_gather_material --- .../Network/CSNetwork/C2SCommand/C2SCommand.cs | 10 ++++++++++ .../CSNetwork/C2SCommand/C2SCommandFactory.cs | 13 +++++++++++++ 2 files changed, 23 insertions(+) diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs index 8d862ae804..5a40aa0491 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs @@ -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; diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs index ff424e5a88..f7b832c87c 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs @@ -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); + } } } From 7a98193ed3afb7dbf712c34e8788e5e45fe22555 Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Mon, 22 Dec 2025 17:53:59 +0700 Subject: [PATCH 08/14] Update CECPlayer.Task.cs --- Assets/PerfectWorld/Scripts/Move/CECPlayer.Task.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Assets/PerfectWorld/Scripts/Move/CECPlayer.Task.cs b/Assets/PerfectWorld/Scripts/Move/CECPlayer.Task.cs index 98d53e0503..2f8db1fcc7 100644 --- a/Assets/PerfectWorld/Scripts/Move/CECPlayer.Task.cs +++ b/Assets/PerfectWorld/Scripts/Move/CECPlayer.Task.cs @@ -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 From 6736a6d6c357c3650a24524ad17406bb3c0741a1 Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Mon, 22 Dec 2025 17:54:20 +0700 Subject: [PATCH 09/14] Add public bool IsInvisible --- Assets/PerfectWorld/Scripts/Move/CECPlayer.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs b/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs index 8ff58eb4c1..f4562ac83c 100644 --- a/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs +++ b/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs @@ -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) { From 9fcbdf7e5cbd5d70036103ba09d7a8980e087135 Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Mon, 22 Dec 2025 17:54:49 +0700 Subject: [PATCH 10/14] Add check matter IsMatter --- Assets/PerfectWorld/Scripts/Managers/EC_Object.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_Object.cs b/Assets/PerfectWorld/Scripts/Managers/EC_Object.cs index 4766bec973..8a2daaf282 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_Object.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_Object.cs @@ -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; } From 1aaec5865d7243d59d19ddf08b18399248cbc07d Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Mon, 22 Dec 2025 17:55:10 +0700 Subject: [PATCH 11/14] Update EC_ManMatter.cs --- .../Scripts/Managers/EC_ManMatter.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_ManMatter.cs b/Assets/PerfectWorld/Scripts/Managers/EC_ManMatter.cs index b19e5aea37..5a95295b3f 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_ManMatter.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_ManMatter.cs @@ -46,6 +46,22 @@ namespace PerfectWorld.Scripts.Managers // Storage for matter data that players can access later private Dictionary matterDataStorage = new Dictionary(); private Dictionary m_MatterTab = new Dictionary(); + + /// + /// 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. + /// + 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) From eb8980943cbd852370604df3ad66d5bcca8e13a0 Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Mon, 22 Dec 2025 18:06:20 +0700 Subject: [PATCH 12/14] Add trace to gather matter and gathering matter --- .../Scripts/Managers/EC_HPWorkTrace.cs | 257 ++++++++++++------ 1 file changed, 168 insertions(+), 89 deletions(-) diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkTrace.cs b/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkTrace.cs index 62652cfb66..6b1bf38cf0 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkTrace.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkTrace.cs @@ -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(); + // pObject is usually EC_ElsePlayer/CECPlayer, not CECHostPlayer. + // Using GetComponent() 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,73 @@ 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 + if (m_pHost.CanTakeItem(pMatter.GetTemplateID(), 1)) + { + // Send pickup asking and wait response command + UnityGameSession.RequestPickupItem(m_iObjectId, pMatter.GetTemplateID()); + 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; + } - // // 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; - // } + // 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 - // g_pGame.GetGameSession().c2s_CmdGatherMaterial(m_iObjectId, iPack, iIndex, idTool, pData.task_in); - // } - // } - // else - // { - // g_pGame.GetGameRun().AddFixedMessage(FIXMSG_NEEDTOOL); - // } - // } - //} + // 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 +577,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 +618,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 +646,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 +850,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 +904,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 +939,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 +1024,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() From ffb88a57ea00f7c117fbce11a9eae75bf7d789e8 Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Tue, 23 Dec 2025 10:31:20 +0700 Subject: [PATCH 13/14] Add pickup item, gather logic of movement, anim --- .../Scripts/Managers/EC_HostInputFilter.cs | 245 ++++++++++++++++-- 1 file changed, 227 insertions(+), 18 deletions(-) diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_HostInputFilter.cs b/Assets/PerfectWorld/Scripts/Managers/EC_HostInputFilter.cs index 121617c34c..4daee7ebcf 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_HostInputFilter.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_HostInputFilter.cs @@ -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(out CECObject clickedObject)) + // Check if hit terrain, building, or forest (no CECObject component) + if (!hit.collider.gameObject.TryGetComponent(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; + } } } From 6d5b905c61fdacb2b3b03156cdb4096a9a1d43d0 Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Tue, 23 Dec 2025 14:23:48 +0700 Subject: [PATCH 14/14] Fix pickup money --- Assets/PerfectWorld/Scripts/Managers/EC_HPWorkTrace.cs | 9 +++++++-- Assets/PerfectWorld/Scripts/Objet/CECMatter.cs | 8 +++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkTrace.cs b/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkTrace.cs index 6b1bf38cf0..dc8ee489a0 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkTrace.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkTrace.cs @@ -486,10 +486,15 @@ namespace BrewMonster if (m_iReason == CECHPWorkTrace.Trace_reason.TRACE_PICKUP) { // Check whether we have enougth place to hold this item or money - if (m_pHost.CanTakeItem(pMatter.GetTemplateID(), 1)) + int tid = pMatter.GetTemplateID(); + bool isMoney = pMatter.IsMoney() || GPDataTypeHelper.ISMONEYTID(tid); + + // 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, pMatter.GetTemplateID()); + UnityGameSession.RequestPickupItem(m_iObjectId, tid); bActionDone = true; } else diff --git a/Assets/PerfectWorld/Scripts/Objet/CECMatter.cs b/Assets/PerfectWorld/Scripts/Objet/CECMatter.cs index ab57bcf6c6..d6c8faa701 100644 --- a/Assets/PerfectWorld/Scripts/Objet/CECMatter.cs +++ b/Assets/PerfectWorld/Scripts/Objet/CECMatter.cs @@ -135,7 +135,13 @@ namespace PerfectWorld.Scripts // Default to ITEM for other essence types / Default to ITEM for other essence types dwMatterType = MATTER_ITEM; } - // Note: Money type detection would go here if needed / Note: Money type detection would go here if needed + // 钱币掉落 / 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) {