using BrewMonster; using BrewMonster.Network; using CSNetwork; using CSNetwork.GPDataType; using DG.Tweening; using PerfectWorld.Scripts.Managers.BrewMonster.Managers; 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; case long value when value == EC_MsgDef.MSG_NM_NPCATKRESULT: TransmitMessage(Msg); break; } } return true; } private bool TransmitMessage(ECMSG msg) { int nid = 0; switch (msg.dwMsg) { case long value when value == EC_MsgDef.MSG_NM_NPCATKRESULT: nid = ((cmd_object_atk_result)msg.dwParam1).attacker_id; break; default: return false; } CECNPC pNPC = SeekOutNPC(nid); if (pNPC) pNPC.ProcessMessage(msg); else { CECNPC pAttacker = GetNPCFromAll(nid); CECHostPlayer pHost = GameController.Instance.GetHostPlayer(); EC_ManPlayer pPlayerMan = EC_ManMessageMono.Instance.GetECManPlayer; int idTarget = (0); int dwModifier = (0); int nDamage = (0); switch (msg.dwMsg) { case long value when value == EC_MsgDef.MSG_NM_NPCATKRESULT: { cmd_object_atk_result pCmd = (cmd_object_atk_result)msg.dwParam1; idTarget = pCmd.target_id; dwModifier = pCmd.attack_flag; nDamage = pCmd.damage; } break; case long value when value == EC_MsgDef.MSG_NM_ENCHANTRESULT: { cmd_enchant_result pCmd = (cmd_enchant_result)msg.dwParam1; idTarget = pCmd.target; dwModifier = pCmd.attack_flag; //nDamage = (GNET::ElementSkill::GetType(pCmd.skill) == GNET::TYPE_ATTACK) ? -1 : -2; } break; case long value when value == EC_MsgDef.MSG_NM_NPCSKILLRESULT: { cmd_object_skill_attack_result pCmd = (cmd_object_skill_attack_result)msg.dwParam1; idTarget = pCmd.target_id; dwModifier = pCmd.attack_flag; nDamage = pCmd.attack_flag; } break; } while (idTarget != 0) { if (UnityGameSession.Instance.GameSession.ISNPCID(idTarget)) { // Bị tấn công là NPC var pTarget = GetNPCFromAll(idTarget); if (pTarget == null) { // Không tìm thấy NPC bị tấn công → thoát break; } // Nếu là pet của chính người chơi if (pTarget.IsPetNPC() && pTarget.GetMasterID() == pHost.GetCharacterID()) { // Pet của mình bị đánh → hiển thị damage //pTarget.Damaged(nDamage,(uint) dwModifier); break; } // Nếu attacker chưa xác định if (pAttacker == null) { break; } // Nếu attacker là pet của mình → cho phép hiển thị damage target if (pAttacker.IsPetNPC() && pAttacker.GetMasterID() == pHost.GetCharacterID()) { //pTarget.Damaged(nDamage, dwModifier); } break; } if (UnityGameSession.Instance.GameSession.ISPLAYERID(idTarget)) { // Bị tấn công là người chơi khác var pTarget = pPlayerMan.GetPlayer(idTarget); if (pTarget == null) { break; } if (pAttacker == null) { break; } // Nếu attacker là pet của mình → xử lý damage if (pAttacker.IsPetNPC() && pAttacker.GetMasterID() == pHost.GetCharacterID()) { //pTarget.Damaged(nDamage, dwModifier); } break; } 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: { cmd_npc_info_00 pCmd = (cmd_npc_info_00)msg.dwParam1; 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 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; } 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; } }