done gfx monster and fix bug movable when get stuned
This commit is contained in:
@@ -7,6 +7,7 @@ using ModelRenderer.Scripts.Common;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using BrewMonster.Scripts.Chat;
|
||||
using BrewMonster.Scripts.Skills;
|
||||
using UnityEngine;
|
||||
public class CECNPC : CECObject
|
||||
{
|
||||
@@ -43,6 +44,14 @@ public class CECNPC : CECObject
|
||||
protected CECNPCModelPolicy m_pNPCModelPolicy;
|
||||
protected CECPolicyAction m_pPolicyAction;
|
||||
public int m_iMMIndex;
|
||||
|
||||
// [中文] 上次从服务器收到的完整扩展状态位图(用于 SetNewExtendStates 中的 Array.Copy)
|
||||
// [English] Last full extended-state bitmask received from server (copied in SetNewExtendStates)
|
||||
protected uint[] m_aExtStates = new uint[(int)OBJECT_EXT_STATE.OBJECT_EXT_STATE_COUNT];
|
||||
|
||||
// [中文] 当前已显示的扩展状态位图(与新数据做差分,控制 GFX 增删)
|
||||
// [English] Currently displayed extended-state bitmask (diffed against new data to add/remove GFX)
|
||||
protected uint[] m_aExtStatesShown = new uint[(int)OBJECT_EXT_STATE.OBJECT_EXT_STATE_COUNT];
|
||||
public int m_idAttackTarget;
|
||||
protected UINPC m_npcUI;
|
||||
private CECModel m_pNPCCECModel; // CECModel instance for hook system / 用于挂点系统的CECModel实例
|
||||
@@ -134,7 +143,7 @@ public class CECNPC : CECObject
|
||||
var ext = new uint[ojexitStateCount];
|
||||
if ((info.state & PlayerNPCState.GP_STATE_EXTEND_PROPERTY) != 0)
|
||||
r.ReadInto(ext);
|
||||
//SetNewExtendStates(0, ext, ojexitStateCount );
|
||||
SetNewExtendStates(0, ext, (int)ojexitStateCount);
|
||||
|
||||
// PET
|
||||
m_idMaster = 0;
|
||||
@@ -199,7 +208,7 @@ public class CECNPC : CECObject
|
||||
{
|
||||
case long value when value == EC_MsgDef.MSG_NM_NPCATKRESULT: OnMsgNPCAtkResult(Msg); break;
|
||||
case long value when value == EC_MsgDef.MSG_NM_NPCSTARTPLAYACTION: OnMsgNPCStartPlayAction(Msg); break;
|
||||
//case long value when value == EC_MsgDef.MSG_NM_NPCEXTSTATE: OnMsgNPCExtState(Msg); break;
|
||||
case long value when value == EC_MsgDef.MSG_NM_NPCEXTSTATE: OnMsgNPCExtState(Msg); break;
|
||||
//case long value when value == EC_MsgDef.MSG_NM_NPCCASTSKILL: OnMsgNPCCastSkill(Msg); break;
|
||||
//case long value when value == EC_MsgDef.MSG_NM_ENCHANTRESULT: OnMsgNPCEnchantResult(Msg); break;
|
||||
//case long value when value == EC_MsgDef.MSG_NM_NPCROOT: OnMsgNPCRoot(Msg); break;
|
||||
@@ -217,6 +226,99 @@ public class CECNPC : CECObject
|
||||
//m_pNPCModelPolicy.PlayGfx(res_GFXFile(RES_GFX_LEVELUP), NULL);
|
||||
}
|
||||
|
||||
// [中文] 处理服务器发来的扩展状态更新消息,驱动状态 GFX 的添加与移除
|
||||
// [English] Handle server ext-state update message — drives state GFX add/remove
|
||||
private void OnMsgNPCExtState(ECMSG Msg)
|
||||
{
|
||||
if (Convert.ToInt32(Msg.dwParam2) == CommandID.UPDATE_EXT_STATE)
|
||||
{
|
||||
cmd_update_ext_state pCmd = GPDataTypeHelper.FromBytes<cmd_update_ext_state>((byte[])Msg.dwParam1);
|
||||
if (pCmd.id == m_NPCInfo.nid)
|
||||
SetNewExtendStates(0, pCmd.states, (int)OBJECT_EXT_STATE.OBJECT_EXT_STATE_COUNT);
|
||||
}
|
||||
}
|
||||
|
||||
// [中文] 更新扩展状态并刷新 GFX 显示
|
||||
// [English] Update the ext-state arrays and refresh GFX display
|
||||
public void SetNewExtendStates(int start, uint[] pData, int count)
|
||||
{
|
||||
if (pData == null || start + count > (int)OBJECT_EXT_STATE.OBJECT_EXT_STATE_COUNT)
|
||||
return;
|
||||
|
||||
ShowExtendStates(start, pData, count);
|
||||
Array.Copy(pData, 0, m_aExtStates, start, count);
|
||||
}
|
||||
|
||||
// [中文] 清除所有已显示的状态效果 GFX(传入全零位图触发全量移除)
|
||||
// [English] Clear all currently displayed state-effect GFX (pass all-zero bitmap to remove everything)
|
||||
private void ClearShowExtendStates()
|
||||
{
|
||||
ShowExtendStates(0, new uint[(int)OBJECT_EXT_STATE.OBJECT_EXT_STATE_COUNT],
|
||||
(int)OBJECT_EXT_STATE.OBJECT_EXT_STATE_COUNT, true);
|
||||
}
|
||||
|
||||
// [中文] 对比旧位图与新位图,逐位差分,新增或移除对应状态效果 GFX
|
||||
// [English] Diff old vs new bitmask bit-by-bit and add/remove state-effect GFX accordingly
|
||||
private void ShowExtendStates(int start, uint[] pData, int count, bool bIgnoreOptimize = false)
|
||||
{
|
||||
if (pData == null || start + count > (int)OBJECT_EXT_STATE.OBJECT_EXT_STATE_COUNT)
|
||||
return;
|
||||
|
||||
// [中文] 模型必须已加载才能挂载 GFX
|
||||
// [English] Model must be loaded before GFX can be attached
|
||||
if (!m_pNPCModelPolicy.IsModelLoaded())
|
||||
return;
|
||||
|
||||
// [中文] 策划联入\状态效果\ —— 状态效果 GFX 的基础路径(与 C++ 保持一致)
|
||||
// [English] Designer-linked state-effect GFX base path (matches C++)
|
||||
const string szBasePath = "gfx/策划联入/状态效果/";
|
||||
|
||||
const int bitSize = sizeof(uint) * 8;
|
||||
for (int index = 0; index < count; index++)
|
||||
{
|
||||
int idState = index + start;
|
||||
for (int i = 0; i < bitSize; i++)
|
||||
{
|
||||
uint dwMask = 1u << i;
|
||||
uint dwFlag1 = m_aExtStatesShown[idState] & dwMask; // currently shown
|
||||
uint dwFlag2 = pData[index] & dwMask; // incoming
|
||||
|
||||
// [中文] 两者相同(都激活或都未激活),无需处理
|
||||
// [English] Both unchanged — nothing to do
|
||||
if ((dwFlag1 == 0 && dwFlag2 == 0) || (dwFlag1 != 0 && dwFlag2 != 0))
|
||||
continue;
|
||||
|
||||
// [中文] 查询可见状态定义(NPC 固定使用 profession 127)
|
||||
// [English] Query visible state definition (NPCs always use profession 127)
|
||||
VisibleState pvs = GNET.QueryVisibleState(127, i + idState * bitSize);
|
||||
if (pvs == null)
|
||||
continue;
|
||||
|
||||
string strEffect = pvs.GetEffect();
|
||||
if (string.IsNullOrEmpty(strEffect))
|
||||
continue;
|
||||
|
||||
string strGFXFile = szBasePath + strEffect;
|
||||
|
||||
if (dwFlag1 != 0)
|
||||
{
|
||||
// [中文] 移除旧状态效果 GFX
|
||||
// [English] Remove old state GFX
|
||||
m_pNPCModelPolicy.RemoveGfx(strGFXFile, pvs.GetHH());
|
||||
}
|
||||
else
|
||||
{
|
||||
// [中文] 添加新状态效果 GFX
|
||||
// [English] Add new state GFX
|
||||
BMLogger.Log($"[HoangDev NPC StateGFX] Playing: {strGFXFile}, hook: {pvs.GetHH()}");
|
||||
m_pNPCModelPolicy.PlayGfx(strGFXFile, pvs.GetHH());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Array.Copy(pData, 0, m_aExtStatesShown, start, count);
|
||||
}
|
||||
|
||||
private void OnMsgNPCInvisible(ECMSG Msg)
|
||||
{
|
||||
cmd_object_invisible pCmd = GPDataTypeHelper.FromBytes<cmd_object_invisible>((byte[])Msg.dwParam1);
|
||||
@@ -652,10 +754,10 @@ public class CECNPC : CECObject
|
||||
}*/
|
||||
|
||||
// Clear extend states before model is released
|
||||
/* ClearShowExtendStates();
|
||||
|
||||
::memset(m_aExtStates, 0, sizeof(m_aExtStates));
|
||||
m_aIconStates.clear();*/
|
||||
// [中文] 模型释放前先移除所有状态效果 GFX,并重置位图
|
||||
// [English] Remove all state-effect GFX and reset bitmasks before the model is released
|
||||
ClearShowExtendStates();
|
||||
Array.Clear(m_aExtStates, 0, m_aExtStates.Length);
|
||||
|
||||
m_pNPCModelPolicy = null;
|
||||
PoolManager.Instance.Despawn(m_modelVisual);
|
||||
@@ -998,7 +1100,7 @@ public class CECNPC : CECObject
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
szModelFile = AFile.NormalizePath(szModelFile.ToLower(), true);
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
using BrewMonster;
|
||||
using BrewMonster.Managers;
|
||||
using CSNetwork.GPDataType;
|
||||
using CSNetwork.Protocols;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using BrewMonster.Scripts;
|
||||
using UnityEngine;
|
||||
|
||||
public class CECNPCModelDefaultPolicy
|
||||
@@ -12,6 +16,14 @@ public class CECNPCModelDefaultPolicy
|
||||
A3DAABB m_CHAABB; // AABB Updated with m_ppBrushes
|
||||
// number of brush object used in collision
|
||||
|
||||
// [中文] 状态效果 GFX 对象缓存,键为 (路径 + 挂点名)
|
||||
// [English] Active state-effect GFX objects, keyed by (path + hook name)
|
||||
private Dictionary<string, GameObject> _stateGfxObjects = new Dictionary<string, GameObject>();
|
||||
|
||||
// [中文] 挂点 Transform 缓存,键为挂点名称
|
||||
// [English] Hook transform cache, keyed by bone name
|
||||
private Dictionary<string, Transform> _hookCache = new Dictionary<string, Transform>();
|
||||
|
||||
public CECNPCModelDefaultPolicy(CECNPC pNPC)
|
||||
{
|
||||
m_pNPCModel = new CECModel();
|
||||
@@ -73,7 +85,7 @@ public class CECNPCModelDefaultPolicy
|
||||
// m_CHAABB ¸ù¾Ý͹°üÉϵĶ¥µãλÖÃÀ´¼ÆËã°üΧºÐ£¬¸ü׼ȷ²¢Óë͹°ü¼ì²â±£³ÖÒ»ÖÂ
|
||||
// ²»ÄÜʹÓà GetPos() À´µ÷Õû m_CHAABB ºó×÷Ϊ¼ÆËã½á¹û·µ»Ø
|
||||
//aabb.Center = GetPos() + A3DVECTOR3(0.0f, m_CHAABB.Extents.y, 0.0f);
|
||||
//aabb.CompleteMinsMaxs();
|
||||
//aabb.CompleteMinsMaxs();
|
||||
bRet = true;
|
||||
}
|
||||
return bRet;
|
||||
@@ -199,10 +211,106 @@ public class CECNPCModelDefaultPolicy
|
||||
public override void SetNpcVisual(NPCVisual npcVisual)
|
||||
{
|
||||
_npcVisual = npcVisual;
|
||||
// [中文] 模型重新加载时清除挂点缓存
|
||||
// [English] Clear hook cache when the NPC model is reloaded
|
||||
_hookCache.Clear();
|
||||
}
|
||||
|
||||
public override void SetDefaultPickAABBExt(A3DVECTOR3 vExt)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// IsModelLoaded / PlayGfx / RemoveGfx
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
// [中文] 判断 NPC 模型是否已加载并激活(作为状态 GFX 的前置守卫)
|
||||
// [English] Returns true when the NPC model is present and the game object is active
|
||||
public override bool IsModelLoaded()
|
||||
{
|
||||
return m_pNPCModel != null && m_pNPC != null && m_pNPC.gameObject.activeInHierarchy;
|
||||
}
|
||||
|
||||
// [中文] 异步加载并挂载状态效果 GFX 到指定挂点;以 (路径+挂点) 为键去重
|
||||
// [English] Async-load and attach a state-effect GFX to the given hook; deduplicated by (path+hook)
|
||||
public override async void PlayGfx(string szPath, string szHook)
|
||||
{
|
||||
if (string.IsNullOrEmpty(szPath)) return;
|
||||
|
||||
string key = szPath + szHook;
|
||||
if (_stateGfxObjects.ContainsKey(key)) return;
|
||||
|
||||
GameObject prefab = await AddressableManager.Instance.LoadPrefabAsync(szPath);
|
||||
if (prefab == null)
|
||||
{
|
||||
BMLogger.LogWarning($"[NPC GFX] Failed to load prefab: {szPath}");
|
||||
return;
|
||||
}
|
||||
|
||||
// [中文] 检查 NPC 是否仍然存在(异步加载期间可能被销毁)
|
||||
// [English] Guard against the NPC being destroyed during async load
|
||||
if (m_pNPC == null) return;
|
||||
|
||||
// [中文] 查找挂点,找不到则回退到 NPC 根节点
|
||||
// [English] Locate hook bone; fall back to NPC root if not found
|
||||
Transform parent = FindHookTransform(szHook) ?? m_pNPC.transform;
|
||||
|
||||
GameObject vfx = Object.Instantiate(prefab, parent);
|
||||
if (vfx == null) return;
|
||||
|
||||
vfx.transform.localPosition = Vector3.zero;
|
||||
_stateGfxObjects[key] = vfx;
|
||||
|
||||
BMLogger.Log($"[NPC GFX] Playing: {szPath}, hook: {szHook}");
|
||||
}
|
||||
|
||||
// [中文] 销毁并移除指定状态效果 GFX
|
||||
// [English] Destroy and untrack a state-effect GFX
|
||||
public override void RemoveGfx(string szPath, string szHook)
|
||||
{
|
||||
string key = szPath + szHook;
|
||||
if (_stateGfxObjects.TryGetValue(key, out GameObject vfx) && vfx != null)
|
||||
{
|
||||
Object.Destroy(vfx);
|
||||
_stateGfxObjects.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Hook lookup — child-name traversal (no SkeletonBuilder on NPC)
|
||||
// 挂点查找 —— 遍历子节点名称(NPC 无 SkeletonBuilder)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
// [中文] 在 NPC GameObject 的子节点中按名称递归查找挂点,结果缓存以提升性能
|
||||
// [English] Recursively find a child transform by name under the NPC; results are cached
|
||||
private Transform FindHookTransform(string hookName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(hookName)) return null;
|
||||
|
||||
if (_hookCache.TryGetValue(hookName, out Transform cached) && cached != null)
|
||||
return cached;
|
||||
|
||||
Transform root = m_pNPC?.transform;
|
||||
if (root == null) return null;
|
||||
|
||||
Transform found = FindChildByName(root, hookName);
|
||||
if (found != null)
|
||||
_hookCache[hookName] = found;
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
// [中文] 深度优先递归遍历子节点,按名称查找
|
||||
// [English] Depth-first recursive child search by name
|
||||
private static Transform FindChildByName(Transform parent, string name)
|
||||
{
|
||||
foreach (Transform child in parent)
|
||||
{
|
||||
if (child.name == name) return child;
|
||||
Transform found = FindChildByName(child, name);
|
||||
if (found != null) return found;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,5 +16,17 @@ namespace BrewMonster
|
||||
public abstract bool GetCHAABB(ref A3DAABB ab);
|
||||
public abstract void StopChannelAction();
|
||||
public abstract void SetDefaultPickAABBExt(CSNetwork.GPDataType.A3DVECTOR3 vExt);
|
||||
|
||||
// [中文] 模型是否已加载完毕(用于状态效果 GFX 的显示守卫)
|
||||
// [English] Whether the NPC model is loaded (guard for state-effect GFX display)
|
||||
public abstract bool IsModelLoaded();
|
||||
|
||||
// [中文] 在 NPC 模型挂点上播放状态效果 GFX
|
||||
// [English] Play a state-effect GFX on the NPC model at the given hook
|
||||
public abstract void PlayGfx(string szPath, string szHook);
|
||||
|
||||
// [中文] 移除 NPC 模型上的状态效果 GFX
|
||||
// [English] Remove a state-effect GFX from the NPC model
|
||||
public abstract void RemoveGfx(string szPath, string szHook);
|
||||
}
|
||||
}
|
||||
@@ -1546,6 +1546,11 @@ namespace CSNetwork
|
||||
|
||||
break;
|
||||
}
|
||||
case CommandID.HOST_NOTIFY_ROOT:
|
||||
case CommandID.HOST_DISPEL_ROOT:
|
||||
|
||||
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_ROOTNOTIFY, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
|
||||
break;
|
||||
default:
|
||||
#if UNITY_EDITOR
|
||||
if (isDebug)
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"header": 1992,
|
||||
"param": 0,
|
||||
"hasParam": false,
|
||||
"describe": "Buff rage",
|
||||
"lastUsedUtcTicks": 639138106383814060
|
||||
},
|
||||
{
|
||||
"header": 8903,
|
||||
"param": 73125,
|
||||
@@ -14,13 +21,6 @@
|
||||
"describe": "Up Level",
|
||||
"lastUsedUtcTicks": 639136623463073402
|
||||
},
|
||||
{
|
||||
"header": 1992,
|
||||
"param": 0,
|
||||
"hasParam": false,
|
||||
"describe": "Buff rage",
|
||||
"lastUsedUtcTicks": 639136573213427110
|
||||
},
|
||||
{
|
||||
"header": 1988,
|
||||
"param": 0,
|
||||
|
||||
@@ -609,7 +609,7 @@ namespace BrewMonster
|
||||
|
||||
private void OnMsgHstRootNotify(in ECMSG Msg)
|
||||
{
|
||||
if ((int)Msg.dwParam2 == CommandID.HOST_NOTIFY_ROOT)
|
||||
if (Convert.ToInt32(Msg.dwParam2) == CommandID.HOST_NOTIFY_ROOT)
|
||||
{
|
||||
cmd_host_notify_root pCmd = GPDataTypeHelper.FromBytes<cmd_host_notify_root>((byte[])Msg.dwParam1);
|
||||
m_dwLIES |=(uint) (1 << pCmd.type);
|
||||
@@ -633,7 +633,7 @@ namespace BrewMonster
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((int)Msg.dwParam2 == CommandID.HOST_DISPEL_ROOT)
|
||||
else if (Convert.ToInt32(Msg.dwParam2) == CommandID.HOST_DISPEL_ROOT)
|
||||
{
|
||||
cmd_host_dispel_root pCmd = GPDataTypeHelper.FromBytes<cmd_host_dispel_root>((byte[])Msg.dwParam1);
|
||||
m_dwLIES &=(uint) ~(1 << pCmd.type);
|
||||
|
||||
Reference in New Issue
Block a user