diff --git a/Assets/NetworkLib/Debug/netstandard2.1/CSNetwork.dll b/Assets/NetworkLib/Debug/netstandard2.1/CSNetwork.dll index 4695f2e8e3..52277f92e0 100644 Binary files a/Assets/NetworkLib/Debug/netstandard2.1/CSNetwork.dll and b/Assets/NetworkLib/Debug/netstandard2.1/CSNetwork.dll differ diff --git a/Assets/PerfectWorld/Scripts/GameData/EC_RoleType.cs b/Assets/PerfectWorld/Scripts/GameData/EC_RoleType.cs index 0bd44954e0..2bcb97ce76 100644 --- a/Assets/PerfectWorld/Scripts/GameData/EC_RoleType.cs +++ b/Assets/PerfectWorld/Scripts/GameData/EC_RoleType.cs @@ -1,4 +1,9 @@ -using UnityEngine; +using UnityEngine; + +public static class GameConstants +{ + public static int NUM_MAGICCLASS = 5; +} public struct ROLEBASICPROP { @@ -16,7 +21,174 @@ public struct ROLEBASICPROP public int iCritDamageBonus;// Critical damage bonus public int iInvisibleDegree;// Invisible degree public int iAntiInvisibleDegree; // Anti-invisible degree - public int iPenetration; // ͸ - public int iResilience; // ֿ - public int iVigour; // + public int iPenetration; // ´©Í¸Á¦ + public int iResilience; // µÖ¿¹Á¦ + public int iVigour; + + public ROLEBASICPROP(bool initialize = true) + { + iLevel = 0; + iLevel2 = 0; + iCurHP = 0; + iCurMP = 0; + iExp = 0; + iSP = 0; + iStatusPt = 0; + iCurAP = 0; + iAtkDegree = 0; + iDefDegree = 0; + iCritRate = 0; + iCritDamageBonus = 0; + iInvisibleDegree = 0; + iAntiInvisibleDegree = 0; + iPenetration = 0; + iResilience = 0; + iVigour = 0; + } +}; +public struct ROLEEXTPROP +{ + public ROLEEXTPROP_BASE bs; + public ROLEEXTPROP_MOVE mv; + public ROLEEXTPROP_ATK ak; + public ROLEEXTPROP_DEF df; + + int max_ap; + public ROLEEXTPROP(bool initialize = true) + { + bs = new ROLEEXTPROP_BASE(); + mv = new ROLEEXTPROP_MOVE(); + ak = new ROLEEXTPROP_ATK(); + df = new ROLEEXTPROP_DEF(); + max_ap = 0; + } +}; +public struct ROLEEXTPROP_BASE +{ + /* »ù´¡ÊôÐÔ */ + public int vitality; // Ãü + public int energy; // Éñ + public int strength; // Á¦ + public int agility; // Ãô + public int max_hp; // ×î´óhp + public int max_mp; // ×î´ómp + public int hp_gen; // hp»Ö¸´ËÙ¶È + public int mp_gen; // mp»Ö¸´ËÙ¶È + public ROLEEXTPROP_BASE(bool initialize = true) + { + vitality = 0; + energy = 0; + strength = 0; + agility = 0; + max_hp = 0; + max_mp = 0; + hp_gen = 0; + mp_gen = 0; + } + + // Nếu muốn có constructor với tham số + public ROLEEXTPROP_BASE(int vitality, int energy, int strength, int agility, + int max_hp, int max_mp, int hp_gen, int mp_gen) + { + this.vitality = vitality; + this.energy = energy; + this.strength = strength; + this.agility = agility; + this.max_hp = max_hp; + this.max_mp = max_mp; + this.hp_gen = hp_gen; + this.mp_gen = mp_gen; + } +}; +public struct ROLEEXTPROP_MOVE +{ + /* Ô˶¯ËÙ¶È*/ + public float walk_speed; // ÐÐ×ßËÙ¶È µ¥Î» m/s + public float run_speed; // ±¼ÅÜËÙ¶È µ¥Î» m/s + public float swim_speed; // ÓÎÓ¾ËÙ¶È µ¥Î» m/s + public float flight_speed; // ·ÉÐÐËÙ¶È µ¥Î» m/s + + public ROLEEXTPROP_MOVE(bool initialize = true) + { + walk_speed = 0f; + run_speed = 0f; + swim_speed = 0f; + flight_speed = 0f; + } + + // Constructor truyền tham số nếu muốn + public ROLEEXTPROP_MOVE(float walk_speed, float run_speed, float swim_speed, float flight_speed) + { + this.walk_speed = walk_speed; + this.run_speed = run_speed; + this.swim_speed = swim_speed; + this.flight_speed = flight_speed; + } +}; +public struct ROLEEXTPROP_ATK +{ + // 物理攻击属性 / Attack properties + public int Attack; // 攻击率 (attack rate) + public int DamageLow; // 最小物理伤害 (minimum physical damage) + public int DamageHigh; // 最大物理伤害 (maximum physical damage) + public int AttackSpeed; // 攻击速度 (单位可能是 tick) + public float AttackRange; // 攻击范围 + + // 附加魔法伤害倍数(或附加魔法伤害范围)数组 + public AddonDamageEntry[] AddonDamage; + + // 魔法攻击伤害 + public int DamageMagicLow; // 最小魔法伤害 + public int DamageMagicHigh; // 最大魔法伤害 + + public struct AddonDamageEntry + { + public int DamageLow; + public int DamageHigh; + } + public ROLEEXTPROP_ATK(bool initialize = true) + { + Attack = 0; + DamageLow = 0; + DamageHigh = 0; + AttackSpeed = 0; + AttackRange = 0f; + AddonDamage = new AddonDamageEntry[GameConstants.NUM_MAGICCLASS]; + DamageMagicLow = 0; + DamageMagicHigh = 0; + } + public ROLEEXTPROP_ATK(int attack, int damageLow, int damageHigh, + int attackSpeed, float attackRange, + int damageMagicLow, int damageMagicHigh) + { + Attack = attack; + DamageLow = damageLow; + DamageHigh = damageHigh; + AttackSpeed = attackSpeed; + AttackRange = attackRange; + AddonDamage = new AddonDamageEntry[GameConstants.NUM_MAGICCLASS]; + DamageMagicLow = damageMagicLow; + DamageMagicHigh = damageMagicHigh; + } +} + +// Role (Player and NPC) extended properties, defense part +public struct ROLEEXTPROP_DEF +{ + /* ·ÀÓùÊôÐÔ */ + public int[] resistance; // ħ·¨¿¹ÐÔ + public int defense; // ·ÀÓùÁ¦ + public int armor; // ÉÁ¶ãÂÊ£¨×°¼×µÈ¼¶£© + public ROLEEXTPROP_DEF(int defense, int armor) + { + resistance = new int[GameConstants.NUM_MAGICCLASS]; + this.defense = defense; + this.armor = armor; + } + public ROLEEXTPROP_DEF(bool initialize = true) + { + resistance = new int[GameConstants.NUM_MAGICCLASS]; + defense = 0; + armor = 0; + } }; diff --git a/Assets/PerfectWorld/Scripts/Managers/CECNPCMan.cs b/Assets/PerfectWorld/Scripts/Managers/CECNPCMan.cs index 919fbd74e7..cb773ada9e 100644 --- a/Assets/PerfectWorld/Scripts/Managers/CECNPCMan.cs +++ b/Assets/PerfectWorld/Scripts/Managers/CECNPCMan.cs @@ -38,12 +38,15 @@ public class CECNPCMan : CECObject, IMsgHandler private bool OnMsgNPCMove(ECMSG msg) { - cmd_object_move pCmd = (cmd_object_move)msg.dwParam1; + cmd_object_move pCmd = EC_Utility.ByteArrayToStructure((byte[])msg.dwParam1); if (pCmd.use_time == 0) return true; CECNPC pNPC = SeekOutNPC(pCmd.id); + + BrewMonster.Logger.LogError("HoangDev: OnMsgNPCMove MPC : " + pNPC); + if (pNPC) pNPC.MoveTo(pCmd); @@ -66,6 +69,7 @@ public class CECNPCMan : CECObject, IMsgHandler { 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()); @@ -122,6 +126,29 @@ public class CECNPCMan : CECObject, IMsgHandler 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; } diff --git a/Assets/PerfectWorld/Scripts/NPC/CECModel.cs b/Assets/PerfectWorld/Scripts/NPC/CECModel.cs new file mode 100644 index 0000000000..3a0f1d58b9 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/NPC/CECModel.cs @@ -0,0 +1,11 @@ +using UnityEngine; + +public class CECModel +{ + +} +// Action channel +public enum ActionChannel +{ + ACTCHA_WOUND = 1, +}; \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/NPC/CECModel.cs.meta b/Assets/PerfectWorld/Scripts/NPC/CECModel.cs.meta new file mode 100644 index 0000000000..63d8f7454b --- /dev/null +++ b/Assets/PerfectWorld/Scripts/NPC/CECModel.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 90988058e72b81340ac0270a4e6a74e3 \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/NPC/CECMonster.cs b/Assets/PerfectWorld/Scripts/NPC/CECMonster.cs index a9cc79ee47..934f08a631 100644 --- a/Assets/PerfectWorld/Scripts/NPC/CECMonster.cs +++ b/Assets/PerfectWorld/Scripts/NPC/CECMonster.cs @@ -4,6 +4,7 @@ using ModelRenderer.Scripts.GameData; using System; using Unity.VisualScripting; using UnityEngine; +using static CECNPC; public class CECMonster : CECNPC { @@ -17,15 +18,34 @@ public class CECMonster : CECNPC } public override bool Init(int tid, in info_npc info, ReadOnlySpan packet, int infoOffset) { - base.Init(tid, info, packet,infoOffset); + base.Init(tid, info, packet, infoOffset); BrewMonster.Logger.Log("HoangDev: MonsterInit"); var pDB = ElementDataManProvider.GetElementDataMan(); - DATA_TYPE DataType = default ; - m_pDBEssence = (MONSTER_ESSENCE)pDB.get_data_ptr((uint)tid, ID_SPACE.ID_SPACE_ESSENCE,ref DataType); + DATA_TYPE DataType = default; + m_pDBEssence = (MONSTER_ESSENCE)pDB.get_data_ptr((uint)tid, ID_SPACE.ID_SPACE_ESSENCE, ref DataType); m_fTouchRad = m_pDBEssence.size; m_BasicProps.iLevel = m_pDBEssence.level; QueueLoadNPCModel(); + + /* float fExt = m_fTouchRad * 1.5f; + m_cdr.vExts.Set(fExt, fExt, fExt); + m_pNPCModelPolicy.SetDefaultPickAABBExt(m_cdr.vExts);*/ + + // If NPC doesn't have specific name, use the name in database + if ((info.state & (int)PlayerNPCState.GP_STATE_NPC_NAME) == 0) + { + m_strName = m_pDBEssence.name.ToString(); + /* if (m_pPateName) + m_pPateName->SetText(m_strName, false);*/ + } + + if ((m_pDBEssence.combined_switch & (uint)MONSTER_COMBINED_SWITCH. MCS_FORBID_SELECTION) != 0) + SetSelectable(false); + + transform.position = EC_Utility.ToVector3(info.pos); + + StartWork((int)WorkType.WT_NOTHING, (int)WorkID.WORK_STAND); return true; } } diff --git a/Assets/PerfectWorld/Scripts/NPC/CECNPC.cs b/Assets/PerfectWorld/Scripts/NPC/CECNPC.cs index c3cd64fea6..fce68c94b6 100644 --- a/Assets/PerfectWorld/Scripts/NPC/CECNPC.cs +++ b/Assets/PerfectWorld/Scripts/NPC/CECNPC.cs @@ -27,8 +27,14 @@ public class CECNPC : CECObject protected A3DVECTOR3 m_vMoveDir; protected int m_iPassiveMove; protected bool m_bStopMove; + protected bool m_bStartFight; protected int[] m_aWorks = new int[4]; protected int m_iAction; + protected int m_idSelTarget; + protected int m_iCurWorkType; + protected int m_iCurWork; + protected ROLEEXTPROP m_ExtProps; + protected CECNPCModelPolicy m_pNPCModelPolicy; [SerializeField] protected float m_fMoveSpeed; protected static CECStringTab m_ActionNames; @@ -42,8 +48,6 @@ public class CECNPC : CECObject } public virtual bool Init(int tid, in info_npc info, ReadOnlySpan packet, int infoOffset) { - BrewMonster.Logger.Log("HoangDev: NPCInit"); - m_NPCInfo.nid = info.nid; m_NPCInfo.tid = tid; m_NPCInfo.vis_tid = info.vis_tid; @@ -51,11 +55,22 @@ public class CECNPC : CECObject m_dwStates2 = (uint)info.state2; m_vServerPos = info.pos; m_iRandomProp = (info.state & 0x0f00) >> 8; + m_pNPCModelPolicy = new CECNPCModelDefaultPolicy(this); + m_idSelTarget = 0; + m_iCurWorkType = -1; + m_iCurWork = 0; + m_bStartFight = false; + + m_BasicProps = new ROLEBASICPROP(true); // struct mặc định, các trường số = 0, mảng đã tạo + m_ExtProps = new ROLEEXTPROP(true); m_iMoveEnv = (int)((info.state & PlayerNPCState.GP_STATE_NPC_FLY) != 0 ? EnviromentMoveType.MOVEENV_AIR : (info.state & PlayerNPCState.GP_STATE_NPC_SWIM) != 0 ? EnviromentMoveType.MOVEENV_WATER : EnviromentMoveType.MOVEENV_GROUND); + var npcVisual = GetComponent(); + m_pNPCModelPolicy.SetNpcVisual(npcVisual); + // 2) Cắt “đuôi” ngay sau phần cố định info_npc int fixedSize = System.Runtime.InteropServices.Marshal.SizeOf(); var tail = packet.Slice(infoOffset); @@ -123,12 +138,24 @@ public class CECNPC : CECObject } public static bool InitStaticRes() { + m_ActionNames = new CECStringTab(); // Load action names from file if (!m_ActionNames.IsInitialized()) - m_ActionNames.Init("actions_npc.txt", false); + m_ActionNames.Init("actions_npc", false); return true; } + + public void TransformShape(int vis_tid) + { + if (m_NPCInfo.vis_tid == vis_tid) + { + return; + } + m_NPCInfo.vis_tid = vis_tid; + + QueueLoadNPCModel(); + } public void QueueLoadNPCModel() { /* if (ShouldUseMasterModel()) @@ -144,15 +171,21 @@ public class CECNPC : CECObject { return; } - var nameMonster = Path.GetFileNameWithoutExtension(szModelFile); - BrewMonster.Logger.Log("HoangDev: nameMonster :" + nameMonster); - var model = NPCBuilder.Instance.GetModelByName(nameMonster); - BrewMonster.Logger.Log("HoangDev: model.name :" + model.name); + var nameMonster = Path.GetFileNameWithoutExtension(szModelFile); + var model = NPCBuilder.Instance.GetModelByName(nameMonster); + if (model == null) return; + var monsterModel = Instantiate(model, transform); monsterModel.SetActive(true); + var npcVisual = GetComponent(); + npcVisual.InitNPCEventDoneHandler(); + //QueueECModelForLoad(MTL_ECM_NPC, GetNPCInfo().nid, GetBornStamp(), GetServerPos(), szModelFile, tid); } + public ROLEBASICPROP GetBasicProps() { return m_BasicProps; } + public ROLEEXTPROP GetExtendProps() { return m_ExtProps; } + public void SetSelectedTarget(int id) { m_idSelTarget = id; } public bool GetVisibleModel(out int tid, out string szModelFile) { tid = 0; @@ -185,7 +218,6 @@ public class CECNPC : CECObject return false; bool ret = true; - Debug.Log($"HoangDev : DataType : {dataType}"); switch (dataType) { @@ -193,7 +225,6 @@ public class CECNPC : CECObject { var ess = (MONSTER_ESSENCE)pDBEssence; szModelFile = ByteToStringUtils.ByteArrayToCP936String(ess.file_model); - Debug.Log($"HoangDev : MONSTER_ESSENCE path : {szModelFile}"); break; } case DATA_TYPE.DT_PET_ESSENCE: @@ -213,16 +244,26 @@ public class CECNPC : CECObject break; } - Debug.Log($"HoangDev : path 99 : {szModelFile}"); return ret; } public static void ReleaseStaticRes() { m_ActionNames.Release(); } + public static string GetBaseActionName(int iAct) + { + return m_ActionNames.GetANSIString(iAct); + } + public static bool IsAttackAction(int iAct) + { + return iAct == (int)NPCActionIndex.ACT_ATTACK1 + || iAct == (int)NPCActionIndex.ACT_ATTACK2 + || iAct == (int)NPCActionIndex.ACT_NPC_ATTACK; + } public void MoveTo(cmd_object_move Cmd) { + BrewMonster.Logger.LogError("HoangDev : MoveTo"); if (Cmd.use_time == 0) return; @@ -230,12 +271,14 @@ public class CECNPC : CECObject m_vMoveDir = Cmd.dest - EC_Utility.ToA3DVECTOR3(transform.position); float fDist = m_vMoveDir.Normalize(); // giả sử Normalize() trả về độ dài trước khi chuẩn hóa + BrewMonster.Logger.LogError("HoangDev : MoveTo : Cmd.use_time"); // If destination position is too far, forcely pull player if (IsLag(fDist)) { transform.position = EC_Utility.ToVector3(Cmd.dest); return; } + BrewMonster.Logger.LogError("HoangDev : MoveTo : IsLag"); int iMoveMode = Cmd.move_mode & (int)GPMoveMode.GP_MOVE_MASK; @@ -247,6 +290,7 @@ public class CECNPC : CECObject UnityEngine.Debug.Assert(false, "Invalid move mode: push/pull inside MoveTo"); return; } + BrewMonster.Logger.LogError("HoangDev : MoveTo : (int)GPMoveMode.GP_MOVE_PUSH"); m_cdr.bTraceGround = true; @@ -270,33 +314,257 @@ public class CECNPC : CECObject } m_fMoveSpeed = fDist / (Cmd.use_time * 0.001f); - // float fSpeed = FIX8TOFLOAT(Cmd.sSpeed); - // m_fMoveSpeed = Mathf.Clamp(m_fMoveSpeed, 0.0f, fSpeed * 1.2f); // Adjust NPC's direction - /* if (!IsDirFixed()) - { - var vDir = m_vMoveDir; - vDir.y = 0.0f; - if (!vDir.IsZero()) - { - vDir.Normalize(); - SetDestDirAndUp(vDir, g_vAxisY, 150); - } - } - */ - /* if (m_aWorks[(int)WorkType.WT_NORMAL] != (int)WorkID.WORK_MOVE || ShouldPlayNewActionFor(iMoveMode)) - { - StartWork(WT_NORMAL, WORK_MOVE); - // Play run or walk action - PlayMoveAction(iMoveMode); - }*/ + /*if (!IsDirFixed()) + { + var vDir = m_vMoveDir; + vDir.y = 0.0f; + if (!vDir.IsZero()) + { + vDir.Normalize(); + SetDestDirAndUp(vDir, g_vAxisY, 150); + } + } +*/ + BrewMonster.Logger.LogError("HoangDev : MoveTo : m_aWorks[(int)WorkType.WT_NORMAL] " + m_aWorks[(int)WorkType.WT_NORMAL]); + + if (m_aWorks[(int)WorkType.WT_NORMAL] != (int)WorkID.WORK_MOVE || ShouldPlayNewActionFor(iMoveMode)) + { + StartWork((int)WorkType.WT_NORMAL, (int)WorkID.WORK_MOVE); + // Play run or walk action + BrewMonster.Logger.LogError("HoangDev : MoveTo : PlayMoveAction"); + + PlayMoveAction(iMoveMode); + } } + public void ReleaseWork(int iWorkType) + { + Debug.Assert(iWorkType >= 0 && iWorkType < (int)WorkType.NUM_WORKTYPE); + + switch (m_aWorks[iWorkType]) + { + case (int)WorkID.WORK_STAND: + break; + + case (int)WorkID.WORK_FIGHT: + break; + + case (int)WorkID.WORK_SPELL: + break; + + case (int)WorkID.WORK_DEAD: + break; + + case (int)WorkID.WORK_MOVE: + { + // Để tránh trường hợp WORK_MOVE bị ghi đè bởi WORK_SPELL hoặc WORK khác + // dẫn đến NPC sai vị trí, ta sẽ kiểm tra và kéo NPC về đúng server position + var pos = EC_Utility.ToVector3(m_vServerPos); + var vDelta = pos - transform.position; + float fDist = vDelta.magnitude; // Vector3.magnitude trong Unity + + if (fDist > 0.1f) + { + transform.position = (pos); + /* if (!IsDirFixed()) + { + SetDestDirAndUp(m_vStopDir, Vector3.up, 150); + }*/ + //RebuildTraceBrush(); + } + break; + } + + case (int)WorkID.WORK_POLICYACTION: + { + /* m_pNPCModelPolicy?.StopChannelAction(); + m_pPolicyAction = null; + m_nPolicyActionIntervalTimer = 0;*/ + break; + } + } + + m_aWorks[iWorkType] = 0; + + if (m_iCurWorkType == iWorkType) + m_iCurWork = 0; + } + + public void StartWork(int iWorkType, int iNewWork, uint dwParam = 0) + { + Debug.Assert(iWorkType >= 0 && iWorkType < (int)WorkType.NUM_WORKTYPE); + + if (iNewWork == (int)WorkID.WORK_DEAD) + { + // Dead is a special work + ReleaseWork((int)WorkType.WT_INTERRUPT); + ReleaseWork((int)WorkType.WT_NORMAL); + + m_aWorks[(int)WorkType.WT_NORMAL] = iNewWork; + m_iCurWorkType = (int)WorkType.WT_NORMAL; + } + else if (iWorkType == (int)WorkType.WT_INTERRUPT) + { + // Release old work + ReleaseWork((int)WorkType.WT_INTERRUPT); + m_aWorks[(int)WorkType.WT_INTERRUPT] = iNewWork; + + if (m_iCurWorkType == (int)WorkType.WT_NORMAL || m_iCurWorkType == (int)WorkType.WT_NOTHING) + StopWork(m_iCurWorkType); + + m_aWorks[(int)WorkType.WT_INTERRUPT] = iNewWork; + m_iCurWorkType = (int)WorkType.WT_INTERRUPT; + } + else if (iWorkType == (int)WorkType.WT_NORMAL) + { + // Release old work + ReleaseWork((int)WorkType.WT_NORMAL); + m_aWorks[(int)WorkType.WT_NORMAL] = iNewWork; + + if (m_iCurWorkType < 0 || m_iCurWorkType == (int)WorkType.WT_NORMAL || m_iCurWorkType == (int)WorkType.WT_NOTHING) + { + if (m_iCurWorkType == (int)WorkType.WT_NOTHING) + StopWork((int)WorkType.WT_NOTHING); + + m_iCurWorkType = (int)WorkType.WT_NORMAL; + } + else + return; + } + else // iWorkType == WT_NOTHING + { + // Release old work + ReleaseWork((int)WorkType.WT_NOTHING); + m_aWorks[(int)WorkType.WT_NOTHING] = iNewWork; + + if (m_iCurWorkType < 0 || m_iCurWorkType == (int)WorkType.WT_NOTHING) + m_iCurWorkType = (int)WorkType.WT_NOTHING; + else + return; + } + + StartWorkByID(iNewWork, dwParam); + } + + public void StopWork(int iWorkType) + { + } + public void StartWorkByID(int iWorkID, uint dwParam) + { + // Ignore all message if this NPC is dead. + // if (IsDead()) + // return; + + switch (iWorkID) + { + case (int)WorkID.WORK_STAND: StartWork_Stand(dwParam); break; + case (int)WorkID.WORK_FIGHT: StartWork_Fight(dwParam); break; + case (int)WorkID.WORK_SPELL: StartWork_Spell(dwParam); break; + case (int)WorkID.WORK_DEAD: StartWork_Dead(dwParam); break; + case (int)WorkID.WORK_MOVE: StartWork_Move(dwParam); break; + case (int)WorkID.WORK_POLICYACTION: StartWork_PolicyAction(dwParam); break; + } + + // if (iWorkID != WORK_MOVE) m_iPassiveMove = 0; + m_iCurWork = iWorkID; + } + public void StartWork_Stand(uint dwParam) + { + if (!m_bStartFight) + { + if (IsMonsterOrPet()) + PlayModelAction((int)NPCActionIndex.ACT_STAND); + else + PlayModelAction((int)NPCActionIndex.ACT_NPC_STAND); + } + } + + public void StartWork_Fight(uint dwParam) + { + // dwParam được dùng như “thời gian chiến đấu còn lại” + //m_nFightTimeLeft = (int)dwParam; + // Không play animation ở đây vì animation được điều khiển bởi message tấn công + } + + public void StartWork_Spell(uint dwParam) + { + // Trong C++ không có xử lý gì, giữ nguyên + } + + public void StartWork_Dead(uint dwParam) + { + if (IsMonsterOrPet()) + PlayModelAction((int)NPCActionIndex.ACT_DIE); + else + PlayModelAction((int)NPCActionIndex.ACT_NPC_DIE); + } + + public void StartWork_Move(uint dwParam) + { + m_bStartFight = false; + /* + if (m_pNPCModelPolicy != null && m_pNPCModelPolicy.IsModelLoaded()) + { + ClearComActFlag(true); + + // Khi NPC đang di chuyển thì bỏ trace brush (không cần va chạm) + ReleaseTraceBrush(); + }*/ + } + + public void StartWork_PolicyAction(uint dwParam) + { + /* if (m_pPolicyAction == null) + m_pPolicyAction = new CECPolicyAction(); + + // Trong C++: m_pPolicyAction->Init((const S2C::cmd_object_start_play_action *)dwParam); + // Sang C#: dwParam không thể cast trực tiếp. Bạn sẽ cần truyền object phù hợp vào. + m_pPolicyAction.Init((S2C.cmd_object_start_play_action)dwParam); + m_pPolicyAction.Tick(0); + m_nPolicyActionIntervalTimer = 0; + + CheckStartPolicyAction();*/ + } + + public bool ShouldPlayNewActionFor(int iMoveMode) + { + if (m_pNPCModelPolicy.IsPlayingAction()) + { + int iAction = GetMoveAction(iMoveMode); + return !m_pNPCModelPolicy.IsPlayingAction(iAction) + && m_pNPCModelPolicy.HasAction(iAction); + } + return false; + } + public int GetMoveAction(int iMoveMode) + { + if (iMoveMode == (int)GPMoveMode.GP_MOVE_RUN || iMoveMode == (int)GPMoveMode.GP_MOVE_RETURN) + { + if (IsMonsterOrPet()) + return (int)NPCActionIndex.ACT_RUN; + else + return (int)NPCActionIndex.ACT_NPC_RUN; + } + else + { + if (IsMonsterOrPet()) + return (int)NPCActionIndex.ACT_WALK; + else + return (int)NPCActionIndex.ACT_NPC_WALK; + } + } + bool IsMonsterOrPet() { return IsMonsterNPC() || IsPetNPC(); } + bool IsMonsterNPC() { return (int)ClassID.OCID_MONSTER == m_iCID; } + bool IsPetNPC() { return (int)ClassID.OCID_PET == m_iCID; } public void PlayMoveAction(int iMoveMode) { + BrewMonster.Logger.LogError("HoangDev : MoveTo : PlayMoveAction 2"); + // Play run or walk aciton if (iMoveMode == (int)GPMoveMode.GP_MOVE_RUN || iMoveMode == (int)GPMoveMode.GP_MOVE_RETURN) { + PlayModelAction((int)NPCActionIndex.ACT_WALK, false); /*if (IsMonsterOrPet()) PlayModelAction(ACT_RUN, false); else @@ -311,8 +579,10 @@ public class CECNPC : CECObject PlayModelAction(ACT_NPC_WALK, false);*/ } } - public void PlayModelAction(int iAction, bool bRestart/* =true */) + public void PlayModelAction(int iAction, bool bRestart = false) { + BrewMonster.Logger.LogError("HoangDev : MoveTo : PlayModelAction 3"); + m_iAction = iAction; /* if (IsDead()) { @@ -337,6 +607,7 @@ public class CECNPC : CECObject } } }*/ + m_pNPCModelPolicy.PlayModelAction(iAction, bRestart); } bool IsLag(float fDist) diff --git a/Assets/PerfectWorld/Scripts/NPC/CECNPCModelDefaultPolicy.cs b/Assets/PerfectWorld/Scripts/NPC/CECNPCModelDefaultPolicy.cs new file mode 100644 index 0000000000..fb508f45dd --- /dev/null +++ b/Assets/PerfectWorld/Scripts/NPC/CECNPCModelDefaultPolicy.cs @@ -0,0 +1,125 @@ +using CSNetwork.Protocols; +using UnityEngine; + +public class CECNPCModelDefaultPolicy + : CECNPCModelPolicy +{ + CECModel m_pNPCModel; + + public CECNPCModelDefaultPolicy(CECNPC pNPC) + { + + } + + public string GetActionName(int iAct, bool bAttackStart = false) + { + // Tạo builder thay cho static char[128] + var sb = new System.Text.StringBuilder(); + + // Thêm tên base action + sb.Append(CECNPC.GetBaseActionName(iAct)); + + // Nếu là attack action thì thêm hậu tố + if (CECNPC.IsAttackAction(iAct)) + { + sb.Append(bAttackStart ? "Æð" : "Âä"); + } + + // Thêm chuỗi môi trường lien quan toi pet + //string szEnv = GetActionEnvString(); + /* if (!string.IsNullOrEmpty(szEnv)) + { + sb.Append("_"); + sb.Append(szEnv); + }*/ + + return sb.ToString(); + } + public override bool PlayModelAction(int iAction, bool bRestart) + { + BrewMonster.Logger.LogError("HoangDev : MoveTo : PlayModelAction 4 : " + iAction); + + /* if (m_pNPCModel == null) + { + return false; + } +*/ + bool result = false; + + if (iAction == (int)NPCActionIndex.ACT_WOUNDED) + { + string szAct = GetActionName(iAction); + if (!_npcVisual.IsAnimationExist(szAct)) + { + szAct = GetActionName((int)NPCActionIndex.ACT_WOUNDED2); + } + result = _npcVisual.TryPlayAction(szAct); + } + else if (iAction == (int)NPCActionIndex.ACT_ATTACK1 || iAction == (int)NPCActionIndex.ACT_ATTACK2) + { + //bool bHideFX = !CECOptimize::Instance().GetGFX().CanShowAttack(m_pNPC->GetNPCID(), m_pNPC->GetClassID()); + result = _npcVisual.TryPlayAction(GetActionName(iAction, true)); + if (result) + { + /* m_pNPCModel->QueueAction(GetActionName(iAction, false), 0, 0, false, false, bHideFX); + m_pNPCModel->QueueAction(GetActionName(CECNPC::ACT_GUARD), 300);*/ + } + } + else if (iAction == (int)NPCActionIndex.ACT_IDLE) + { + result = _npcVisual.TryPlayAction(GetActionName(iAction)); + if (result) + { + //m_pNPCModel->QueueAction(GetActionName((int)NPCActionIndex.ACT_STAND)); + } + } + else if (iAction == (int)NPCActionIndex.ACT_NPC_IDLE1 || iAction == (int)NPCActionIndex.ACT_NPC_IDLE2) + { + result = _npcVisual.TryPlayAction(GetActionName(iAction)); + if (result) + { + //m_pNPCModel->QueueAction(GetActionName((int)NPCActionIndex.ACT_NPC_STAND)); + } + } + else if (iAction == (int)NPCActionIndex.ACT_NPC_ATTACK) + { + //bool bHideFX = !CECOptimize::Instance().GetGFX().CanShowAttack(m_pNPC->GetNPCID(), m_pNPC->GetClassID()); + result = _npcVisual.TryPlayAction(GetActionName(iAction)); ; + if (result) + { + /*m_pNPCModel->QueueAction(GetActionName(iAction, false), 0, 0, false, false, bHideFX); + m_pNPCModel->QueueAction(GetActionName(CECNPC::ACT_NPC_STAND), 300);*/ + } + } + else if (iAction == (int)NPCActionIndex.ACT_COMMON_BORN) + { + result = _npcVisual.TryPlayAction(GetActionName(iAction)); + if (result) + { + //m_pNPCModel->QueueAction(GetActionName((int)NPCActionIndex.ACT_STAND)); + } + } + else + { + BrewMonster.Logger.LogError("HoangDev : MoveTo : PlayModelAction 4 : " + GetActionName(iAction)); + result = _npcVisual.TryPlayAction(GetActionName(iAction)); + } + return result; + } + public override bool IsPlayingAction() + { + return _npcVisual.IsPlayAnimation(); + } + public override bool IsPlayingAction(int iAction) + { + return _npcVisual.IsPlayAnimation(GetActionName(iAction)); + } + public override bool HasAction(int iAction) + { + return _npcVisual.IsAnimationExist(GetActionName(iAction)); + } + public override void SetNpcVisual(NPCVisual npcVisual) + { + _npcVisual = npcVisual; + } +} diff --git a/Assets/PerfectWorld/Scripts/NPC/CECNPCModelDefaultPolicy.cs.meta b/Assets/PerfectWorld/Scripts/NPC/CECNPCModelDefaultPolicy.cs.meta new file mode 100644 index 0000000000..8a15c1111c --- /dev/null +++ b/Assets/PerfectWorld/Scripts/NPC/CECNPCModelDefaultPolicy.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c6c8a7068612e5f46a7231b8fd589baf \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/NPC/CECNPCModelPolicy.cs b/Assets/PerfectWorld/Scripts/NPC/CECNPCModelPolicy.cs new file mode 100644 index 0000000000..26ab3ab3ea --- /dev/null +++ b/Assets/PerfectWorld/Scripts/NPC/CECNPCModelPolicy.cs @@ -0,0 +1,18 @@ +using UnityEngine; + +public abstract class CECNPCModelPolicy +{ + protected NPCVisual _npcVisual; + + public virtual bool PlayModelAction(int iAction, bool bRestart) { return false; } + public virtual bool IsPlayingAction() { return false; } + public virtual bool IsPlayingAction(int iActions) { return false; } + public virtual void SetNpcVisual(NPCVisual npcVisual) + { + + } + public virtual bool HasAction(int iAction) + { + return false; + } +} diff --git a/Assets/PerfectWorld/Scripts/NPC/CECNPCModelPolicy.cs.meta b/Assets/PerfectWorld/Scripts/NPC/CECNPCModelPolicy.cs.meta new file mode 100644 index 0000000000..f60d1e263f --- /dev/null +++ b/Assets/PerfectWorld/Scripts/NPC/CECNPCModelPolicy.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ef759b50028f2f24b9562dd1dd04f021 \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/NPC/NPCVisual.cs b/Assets/PerfectWorld/Scripts/NPC/NPCVisual.cs index 21e1be24c0..5460ccc9e5 100644 --- a/Assets/PerfectWorld/Scripts/NPC/NPCVisual.cs +++ b/Assets/PerfectWorld/Scripts/NPC/NPCVisual.cs @@ -1,16 +1,35 @@ +using Animancer; using UnityEngine; public class NPCVisual : MonoBehaviour { - // Start is called once before the first execution of Update after the MonoBehaviour is created - void Start() - { - - } + [SerializeField] NamedAnimancerComponent namedAnimancer; - // Update is called once per frame - void Update() + public bool TryPlayAction(string animationName) { - + if(namedAnimancer == null) return false; + if(namedAnimancer.IsPlaying(animationName)) return false; + BrewMonster.Logger.LogError("TryPlayAction "+ animationName); + + return namedAnimancer.TryPlay(animationName) == null; } + public void InitNPCEventDoneHandler() + { + namedAnimancer = GetComponentInChildren(); + if (namedAnimancer == null) + { + BrewMonster.Logger.LogError("animancer == null"); + return; + } + } + private void OnDestroy() + { + + } + public bool IsAnimationExist(string animationName) + { + return namedAnimancer.States.TryGet("ActionName", out var existingState) ? true : false; + } + public bool IsPlayAnimation() => namedAnimancer.IsPlaying(); + public bool IsPlayAnimation(string animationName) => namedAnimancer.IsPlaying(animationName); } diff --git a/Assets/Prefabs/MonsterPrefab.prefab b/Assets/Prefabs/MonsterPrefab.prefab index d8762b184a..e028b4cf45 100644 --- a/Assets/Prefabs/MonsterPrefab.prefab +++ b/Assets/Prefabs/MonsterPrefab.prefab @@ -10,6 +10,7 @@ GameObject: m_Component: - component: {fileID: 3521696612905468212} - component: {fileID: 2542060226037108388} + - component: {fileID: -3520322077839857420} m_Layer: 0 m_Name: MonsterPrefab m_TagString: Untagged @@ -44,3 +45,17 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 2cd22b82fc76bed46ac948cef9c7119d, type: 3} m_Name: m_EditorClassIdentifier: + m_fMoveSpeed: 0 +--- !u!114 &-3520322077839857420 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6975799234359536760} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8c7b669b45068cf469ae3116991b9026, type: 3} + m_Name: + m_EditorClassIdentifier: + namedAnimancer: {fileID: 0} diff --git a/Assets/Prefabs/NPCPrefab.prefab b/Assets/Prefabs/NPCPrefab.prefab index 4e4bf9cf48..29419e1dd3 100644 --- a/Assets/Prefabs/NPCPrefab.prefab +++ b/Assets/Prefabs/NPCPrefab.prefab @@ -10,6 +10,7 @@ GameObject: m_Component: - component: {fileID: 3521696612905468212} - component: {fileID: 2999449018738386437} + - component: {fileID: -6135441729487348920} m_Layer: 0 m_Name: NPCPrefab m_TagString: Untagged @@ -44,3 +45,16 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: e7f08b1c572f8f140b1f25edb7e5dd47, type: 3} m_Name: m_EditorClassIdentifier: + m_fMoveSpeed: 0 +--- !u!114 &-6135441729487348920 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6975799234359536760} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8c7b669b45068cf469ae3116991b9026, type: 3} + m_Name: + m_EditorClassIdentifier: diff --git a/Assets/Scenes/MonsterRender.unity b/Assets/Scenes/MonsterRender.unity index 4bc9357860..d04c285fa9 100644 --- a/Assets/Scenes/MonsterRender.unity +++ b/Assets/Scenes/MonsterRender.unity @@ -2429,6 +2429,7 @@ GameObject: m_Component: - component: {fileID: 1295080370} - component: {fileID: 1295080373} + - component: {fileID: 1295080374} m_Layer: 0 m_Name: A3DSkinnedMeshRenderer(Clone) m_TagString: Untagged @@ -2476,6 +2477,28 @@ Animator: m_AllowConstantClipSamplingOptimization: 1 m_KeepAnimatorStateOnDisable: 0 m_WriteDefaultValuesOnDisable: 0 +--- !u!114 &1295080374 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1295080369} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c75c0dcb6d50eb64abd727a90406ca2b, type: 3} + m_Name: + m_EditorClassIdentifier: + _Animator: {fileID: 1295080373} + _ActionOnDisable: 0 + _PlayAutomatically: 1 + _Animations: + - {fileID: 7400000, guid: e7a5b03a5002f406b9a394e553a2ac9a, type: 2} + - {fileID: 7400000, guid: 8f804a48aaae74ea584994f54c2b3bb7, type: 2} + - {fileID: 7400000, guid: 9aac5b94918534a069b5d9b0f0dbcdab, type: 2} + - {fileID: 7400000, guid: 6ce7ea75839424db88c6e2e1fac83603, type: 2} + - {fileID: 7400000, guid: d95521e8bd8664f69a0b8f5dac6fa152, type: 2} + - {fileID: 7400000, guid: 6bd82249f37b94af7b2270cca56e85aa, type: 2} --- !u!1 &1328669173 GameObject: m_ObjectHideFlags: 0 @@ -3798,6 +3821,7 @@ GameObject: m_Component: - component: {fileID: 1761919880} - component: {fileID: 1761919883} + - component: {fileID: 1761919884} m_Layer: 0 m_Name: A3DSkinnedMeshRenderer(Clone) m_TagString: Untagged @@ -3845,6 +3869,41 @@ Animator: m_AllowConstantClipSamplingOptimization: 1 m_KeepAnimatorStateOnDisable: 0 m_WriteDefaultValuesOnDisable: 0 +--- !u!114 &1761919884 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1761919879} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c75c0dcb6d50eb64abd727a90406ca2b, type: 3} + m_Name: + m_EditorClassIdentifier: + _Animator: {fileID: 1761919883} + _ActionOnDisable: 0 + _PlayAutomatically: 1 + _Animations: + - {fileID: 7400000, guid: 455f73dbc706f4f01be2a49237758abf, type: 2} + - {fileID: 7400000, guid: 6ff013485dd2f4822bd7e5062683a98d, type: 2} + - {fileID: 7400000, guid: 7fc83c309c6eb481089ad03465d170e4, type: 2} + - {fileID: 7400000, guid: d967418450b764b1fb5cde162add119c, type: 2} + - {fileID: 7400000, guid: 8dd91c33e4b4244f8b2f1dffac73b915, type: 2} + - {fileID: 7400000, guid: 54ed398049959439abd1f20dba285f02, type: 2} + - {fileID: 7400000, guid: 9d4148203261d48d499b3805b3ca6493, type: 2} + - {fileID: 7400000, guid: e7e87d5056bec4e20b65789d14fddaf4, type: 2} + - {fileID: 7400000, guid: 1d3b556541aaf40cb83ac55256edd614, type: 2} + - {fileID: 7400000, guid: 073ead96c59c24c96adc3c4ec1f960f2, type: 2} + - {fileID: 7400000, guid: d6256486e9d294bc59ca3964e00d4ee0, type: 2} + - {fileID: 7400000, guid: 0b93f45e94ed249e8bb3ce16c8765603, type: 2} + - {fileID: 7400000, guid: 89616b414c43a487d8c9d9b9ee5336a3, type: 2} + - {fileID: 7400000, guid: 28e870607d15e42a2b7186825c603e13, type: 2} + - {fileID: 7400000, guid: 082e512e1d70747f9ab1c5d7bcdf40f8, type: 2} + - {fileID: 7400000, guid: 5092575ab0ff14211a7e18068de530f9, type: 2} + - {fileID: 7400000, guid: 6dffa0347f1134368953849c2918cee5, type: 2} + - {fileID: 7400000, guid: 779009ded4fb841e9b2e33b3a726fbc1, type: 2} + - {fileID: 7400000, guid: c7cd31ecc034444feb17d7e164c0323b, type: 2} --- !u!1 &1770128547 GameObject: m_ObjectHideFlags: 0 @@ -4239,7 +4298,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!4 &2107333969 Transform: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/EC_Utility.cs b/Assets/Scripts/EC_Utility.cs index 83352e13a8..ff3bdc45e2 100644 --- a/Assets/Scripts/EC_Utility.cs +++ b/Assets/Scripts/EC_Utility.cs @@ -2,6 +2,7 @@ using CSNetwork.GPDataType; using System; using System.Collections; +using System.Runtime.InteropServices; using UnityEngine; using static EC_Player; @@ -30,6 +31,18 @@ public static class EC_Utility return (byte)(fDeg * fInvInter); } } + public static T ByteArrayToStructure(byte[] bytes) where T : struct + { + GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); + try + { + return Marshal.PtrToStructure(handle.AddrOfPinnedObject()); + } + finally + { + handle.Free(); + } + } public static System.Numerics.Vector3 ToNumerics(this UnityEngine.Vector3 v) { return new System.Numerics.Vector3(v.x, v.y, v.z); diff --git a/Assets/Scripts/GameController.cs b/Assets/Scripts/GameController.cs index 371c59dcf3..bdd3c803f6 100644 --- a/Assets/Scripts/GameController.cs +++ b/Assets/Scripts/GameController.cs @@ -45,7 +45,6 @@ public class GameController : MonoBehaviour { hostPlayer = FindAnyObjectByType(); } - Debug.Log("hostPlayer " + hostPlayer); return hostPlayer; } public void InitCharacter(cmd_self_info_1 info) diff --git a/Assets/Scripts/PlayerVisual.cs b/Assets/Scripts/PlayerVisual.cs index 3524d2b318..f71a3d3350 100644 --- a/Assets/Scripts/PlayerVisual.cs +++ b/Assets/Scripts/PlayerVisual.cs @@ -4,7 +4,7 @@ using UnityEngine; public class PlayerVisual : MonoBehaviour { - [SerializeField] NamedAnimancerComponent animancer; + [SerializeField] NamedAnimancerComponent namedAnimancer; [SerializeField] private INFO _playerInfo; @@ -12,13 +12,13 @@ public class PlayerVisual : MonoBehaviour private void PlayActionEventHandler(PlayActionEvent @event) { BrewMonster.Logger.Log("PlayActionEventHandler : "+@event.AnimationName); - animancer.TryPlay(@event.AnimationName); + namedAnimancer.TryPlay(@event.AnimationName); } public void InitHostPlayerEventDoneHandler() { - animancer = GetComponentInChildren(); - if(animancer == null) + namedAnimancer = GetComponentInChildren(); + if(namedAnimancer == null) { BrewMonster.Logger.LogError("animancer == null"); return; @@ -36,4 +36,8 @@ public class PlayerVisual : MonoBehaviour { EventBus.UnsubscribeAllInChannel(_playerInfo.cid); } + public bool IsAnimationExist(string animationName) + { + return namedAnimancer.States.TryGet("ActionName", out var existingState) ? true : false; + } }