using BrewMonster; using CSNetwork; using CSNetwork.GPDataType; using DG.Tweening; using System; using System.Buffers.Binary; using System.Collections.Generic; using System.Runtime.InteropServices; using Unity.VisualScripting; using UnityEngine; public class CECNPCMan : CECObject, IMsgHandler { private Dictionary m_NPCTab = new Dictionary(512); private Dictionary m_UkNPCTab = new Dictionary(32); Vector3 m_vServerPos; public int HandlerId => (int)MANAGER_INDEX.MAN_NPC; public CECNPCMan() { m_vServerPos = Vector3.zero; m_iCID = (int)Class_ID.OCID_MONSTER; } public bool ProcessMessage(ECMSG Msg) { if (Msg.iSubID == 0) { switch (Msg.dwMsg) { case long value when value == EC_MsgDef.MSG_NM_NPCINFO: OnMsgNPCInfo(Msg); break; case long value when value == EC_MsgDef.MSG_NM_NPCMOVE: OnMsgNPCMove(Msg); break; case long value when value == EC_MsgDef.MSG_NM_NPCSTOPMOVE: OnMsgNPCStopMove(Msg); break; } } return true; } private bool OnMsgNPCStopMove(ECMSG msg) { cmd_object_stop_move pCmd = EC_Utility.ByteArrayToStructure((byte[])msg.dwParam1); CECNPC pNPC = SeekOutNPC(pCmd.id); if (pNPC) pNPC.StopMoveTo(pCmd); return true; } private bool OnMsgNPCMove(ECMSG msg) { var buffer = (byte[])msg.dwParam1; cmd_object_move pCmd = MemoryMarshal.Read(buffer); if (pCmd.use_time == 0) return true; CECNPC pNPC = SeekOutNPC(pCmd.id); if (pNPC) pNPC.MoveTo(pCmd); return true; } public CECNPC SeekOutNPC(int nid) { if (!m_NPCTab.TryGetValue(nid, out var npc)) { // Couldn't find this NPC, put it into unknown NPC table m_UkNPCTab[nid] = nid; return null; } return npc; } private bool OnMsgNPCInfo(ECMSG msg) { switch (Convert.ToInt32(msg.dwParam2)) { case CommandID.NPC_INFO_LIST: { // msg.dwParam1 chính là buffer chứa placeholder data (không có header cmd_npc_info_list) cmd_npc_info_list pCmd = MemoryMarshal.Read( ((byte[])msg.dwParam1).AsSpan()); int offset = Marshal.OffsetOf("placeholder").ToInt32(); byte[] buffer = (byte[])msg.dwParam1; Span pDataBuf = buffer.AsSpan(offset); for (int i = 0; i < pCmd.count; i++) { // giống const info_npc& Info = *(const info_npc*)pDataBuf; info_npc info = MemoryMarshal.Read(pDataBuf); int iSize = info_npc.HEADER_SIZE; if ((info.state & PlayerNPCState.GP_STATE_EXTEND_PROPERTY) != 0) iSize += sizeof(uint) * NumberDWORDsPlayerNPC.OBJECT_EXT_STATE_COUNT; if ((info.state & PlayerNPCState.GP_STATE_NPC_PET) != 0) iSize += sizeof(int); if ((info.state & PlayerNPCState.GP_STATE_NPC_NAME) != 0) { byte len = pDataBuf[iSize]; iSize += 1 + len; } if ((info.state & PlayerNPCState.GP_STATE_MULTIOBJ_EFFECT) != 0) { int countEff = BinaryPrimitives.ReadInt32LittleEndian( pDataBuf.Slice(iSize, sizeof(int))); iSize += sizeof(int) + countEff * (sizeof(int) + 1); } if ((info.state & PlayerNPCState.GP_STATE_NPC_MAFIA) != 0) iSize += sizeof(int); NPCEnter(info, false, buffer, offset); // dịch pDataBuf về sau (giống pDataBuf += iSize) pDataBuf = pDataBuf.Slice(iSize); offset += iSize; } break; } case CommandID.NPC_ENTER_SLICE: { var buffer = (byte[])msg.dwParam1; info_npc info = MemoryMarshal.Read(buffer.AsSpan(0, info_npc.HEADER_SIZE)); NPCEnter(info, false, buffer, info_npc.HEADER_SIZE); break; } case CommandID.NPC_ENTER_WORLD: { var buffer = (byte[])msg.dwParam1; info_npc info = MemoryMarshal.Read(buffer.AsSpan(0, info_npc.HEADER_SIZE)); NPCEnter(info, true, buffer, info_npc.HEADER_SIZE); break; } case CommandID.NPC_INFO_00: { var buffer = (byte[])msg.dwParam1; cmd_npc_info_00 pCmd = GPDataTypeHelper.FromBytes(buffer); //cmd_npc_info_00 pCmd = MemoryMarshal.Read(buffer.AsSpan(0, cmd_npc_info_00.)); CECNPC pNPC = SeekOutNPC(pCmd.idNPC); if (pNPC) { ROLEBASICPROP bp = pNPC.GetBasicProps(); ROLEEXTPROP ep = pNPC.GetExtendProps(); bp.iCurHP = pCmd.iHP; ep.bs.max_hp = pCmd.iMaxHP; pNPC.SetSelectedTarget(pCmd.iTargetID); } break; } case CommandID.NPC_VISIBLE_TID_NOTIFY: { cmd_npc_visible_tid_notify pCmd = (cmd_npc_visible_tid_notify)msg.dwParam1; CECNPC pNPC = SeekOutNPC(pCmd.nid); if (pNPC) pNPC.TransformShape(pCmd.vis_tid); break; } } return true; } public bool NPCEnter(in info_npc Info, bool bBornInSight, ReadOnlySpan packet, int infoOffset) { var npc = GetNPC(Info.nid); if (npc != null) { m_NPCTab.Remove(Info.nid); } // Nếu id này có trong bảng unknown thì xóa nó if (m_UkNPCTab.ContainsKey(Info.nid)) { m_UkNPCTab.Remove(Info.nid); } // Tạo NPC mới npc = CreateNPC(Info, bBornInSight, packet, infoOffset); if(npc != null) { npc.SetUpCECNPC(this); } if (object.ReferenceEquals(npc, null)) { BrewMonster.BMLogger.LogError($"Failed to create NPC ({Info.tid})"); return false; } // Thêm NPC vào bảng m_NPCTab[Info.nid] = npc; return true; } // Get NPC by id and optional bornStamp public CECNPC GetNPC(int nid, uint bornStamp = 0) { if (!m_NPCTab.TryGetValue(nid, out var npc)) return null; return npc; } public CECNPC GetNPCFromAll(int nid) { CECNPC pNPC = GetNPC(nid); if (pNPC) return pNPC; // Search from disappear array ? /*for (int i = 0; i < m_aDisappearNPCs.GetSize(); i++) { CECNPC* pNPC = m_aDisappearNPCs[i]; if (pNPC->GetNPCID() == nid) return pNPC; }*/ return null; } public CECNPC CreateNPC(info_npc Info, bool bBornInSight, ReadOnlySpan packet, int infoOffset) { CECNPC pNPC = null; int tid = Info.tid; bool bPet = (Info.state & PlayerNPCState.GP_STATE_NPC_PET) != 0; // Get data type from database var edm = ElementDataManProvider.GetElementDataMan(); // tương đương g_pGame->GetElementDataMan() DATA_TYPE dataType = edm.get_data_type((uint)tid, ID_SPACE.ID_SPACE_ESSENCE); if (dataType != DATA_TYPE.DT_NPC_ESSENCE && dataType != DATA_TYPE.DT_MONSTER_ESSENCE && dataType != DATA_TYPE.DT_PET_ESSENCE) { // Try default npc tid = 4249; dataType = edm.get_data_type((uint)tid, ID_SPACE.ID_SPACE_ESSENCE); } if (bPet) { //pNPC = new CECPet(this); } else { switch (dataType) { case DATA_TYPE.DT_NPC_ESSENCE: /*pNPC = new CECNPCServer(this);*/ break; case DATA_TYPE.DT_MONSTER_ESSENCE: pNPC = GameController.Instance.GetMonster(); break; case DATA_TYPE.DT_PET_ESSENCE:/* pNPC = new CECPet(this);*/ break; default: UnityEngine.Debug.Assert(false, "Invalid DATA_TYPE in CreateNPC"); return null; } } // Set born stamp & born-in-sight (giữ nguyên semantics) uint bornStamp = CECWorld.Instance.GetBornStamp(); if (!object.ReferenceEquals(pNPC, null)) { pNPC.SetBornStamp(bornStamp); pNPC.SetBornInSight(bBornInSight); } else { return null; } // Init với tid + Info như C++ if (!pNPC.Init(tid, Info, packet, infoOffset)) { // đảm bảo giải phóng nếu bạn có tài nguyên kèm theo //pNPC?.Release(); pNPC = null; // log lỗi tương tự glb_ErrorOutput UnityEngine.Debug.LogError($"CECNPCMan::CreateNPC failed, tid={tid}"); return null; } return pNPC; } }