fix(client/netproto): 部分怪物与营地NPC(如罗格斥候)初始渲染为死亡/尸体状态 #551
Loading…
Reference in New Issue
No description provided.
Delete Branch "%!s(<nil>)"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
问题描述
在联机 D2GS 会话中,部分营地 NPC(例如罗格营地的罗格斥候
rogue1,hcIdx = 152, tokenRG)以及野外新刷出的怪物,在初始生成(0xAC AssignNPC)时直接渲染为倒在血泊中的死亡尸体画面(DT/DD最后一帧),且被判定为isCorpse = true(在小地图隐藏、无法点击交互)。参考截图:https://screenshot-v2.corp.google.com/5rnl53i18d5pg
1.13c 权威反汇编根因分析 (
D2Game.dll&D2Client.dll)1.13c 动作模式枚举 (
MonMode.txt/PlrMode.txtvsObjMode.txt):MonMode.txt,unitType = 1):0 = DT(Death 死亡动画),1 = NU(Neutral 站立待机),2 = WL(Walk 行走),3 = GH(Get Hit 受击),12 = DD(Dead 尸体),15 = RN(Run 奔跑)。PlrMode.txt,unitType = 0):0 = DT(Death),1 = NU(Neutral),2 = WL,3 = RN,5 = TN(Town Neutral),6 = TW(Town Walk),17 = DD(Dead)。ObjMode.txt,unitType = 2) 使用0 = NU。对于玩家与怪物,0是死亡 (DT),1才是待机 (NU)(验证于D2Client.dll@0x6fafdca4..0x6fafddb4)。0xAC AssignNPC首个 4-bitnMode字段被丢弃 (src/netproto/d2gs/s2c/units.ts):D2Game.dll@0x6fcacac9..0x6fcacb01在构造0xAC比特流 (pkt[13..]) 时,通过FOG_WriteBits(&bitStream, mode, 4)写入的首个 4-bit 字段是怪物的初始动作模式nMode(若处于8=SQ、9=KB、12=DD、0=DT则原样写入,其余所有存活状态一律写入1 = MONMODE_NEUTRAL)。D2Client.dll@0x6fb5e462..0x6fb5e469->0x6fb5787c->0x6fafd345读取该 4-bitnMode并调用D2Common_11090(UNITS_ChangeCurrentMode)。decode0xACAssignNpc将bitReader.readBits(4)误注为// initial facing direction并丢弃返回值,未在UnitSnapshot上设置mode。ClientUnit默认mode = 0(DT) 导致存活怪物被判定为尸体 (isCorpse = true):ClientUnit构造函数无条件执行this.mode = snapshot.mode ?? 0,导致所有新生成的怪物/NPC 和玩家默认mode = 0(DT死亡)。OnlineSession.buildRenderableUnits(src/client/session/online-session.ts:1438) 计算const isCorpse = u.unitType === 'monster' && (u.mode === 0 || u.mode === 12 || u.lifePct <= 0)时恒为true。WorldRenderer.drawSceneUnitDrawable针对isCorpse = true强制选取dd/dt动作并冻结在最后一帧(frameCount - 1)。像营地罗格(tokenRG,与野外黑暗罗格共用含DT/DD的 DCC/COF 资源)及野外怪物因此直接显示为血泊中的尸体;阿卡拉/恰西等无DT/DDCOF 的城镇商人虽回退到nu,但动画被冻结在末帧、小地图标记被跳过、鼠标拾取被过滤。0x69 SetNPCMode/0x6A/0x6B状态命令索引表 (D2Client.dll0x6fb82e68) 与移动模式切换 (UnitMove):D2Game.dll(0x6fd13620, 表0x6fd1a718) 与D2Client.dll(0x6fb5f390->0x6fafdc70, 表0x6fb82e68) 中,0x69/0x6A/0x6B的pkt[5]是0..29的状态命令索引(例如0x06 -> 3 GH,0x07 -> 1 NU,0x08 -> 0 DT,0x09 -> 12 DD,0x0A/0x0B -> 4 A1),而非直接的MonMode.txtID。0x67 NPCMove/0x68 NPCMoveToTarget/0x6D NPCStop触发UnitMove及RemoteEntityInterpolator插值起止时,需同步将存活怪物的mode在2 (WL)/15 (RN)与1 (NU)之间切换。修复方案
decode0xACAssignNpc中解析首个 4-bitnMode(对lifePct > 0的存活单位保证非死亡模式1 = NU,对lifePct <= 0的死亡单位设为0/12),在decode0x59AssignPlayer中设置初始mode: 1。decode0x69SetNpcMode、decode0x6ANpcAction、decode0x6BNpcAction2中按D2Client.dll0x6fb82e68权威映射表转换pkt[5],并补齐0x6A/0x6B的lifePct与坐标字段。ClientUnit、ClientWorld、OnlineSession、unit-drawables.ts、world-renderer.ts中将玩家/怪物的默认待机mode统一为1 (NU),并在UnitMove与插值器停止时同步更新行走/奔跑/待机模式。