From 68c7890eee8e68f4be5a9e100a715576772f86c2 Mon Sep 17 00:00:00 2001 From: troytt <47798984@qq.com> Date: Mon, 14 Sep 2026 14:51:49 +0000 Subject: [PATCH] =?UTF-8?q?fix(packer):=20=E4=BF=AE=E6=AD=A3=20monsters=20?= =?UTF-8?q?=E5=89=8D=E7=BC=80=E9=95=BF=E5=BA=A6=EF=BC=8C=E6=81=A2=E5=A4=8D?= =?UTF-8?q?=E5=85=A8=E9=83=A8=20NPC=20=E7=BE=8E=E6=9C=AF=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=20(refs=20#6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 打包器在切分 MPQ 成员名时,objects 走 `OBJECT_PREFIX.length`, monsters 却硬编码了一个手数的 `22`。`data\global\monsters\` 的真实 长度是 **21**,于是每个怪物 token 都被多切掉一个字符: slice(21) -> "DC" (正确) slice(22) -> "C" (实际发生的) `monsterMembers` 因此以 `C`/`E`/`R`/`S`/`J` 这些残缺 token 建索引, 而查表时用的是正确的 `DC`/`JE`/`XR`/`XS`/`0J`,导致**每一次 NPC 美术 查找都必然落空**。由于代码里对"找不到"是静默跳过,这个缺陷不会报错, 只会让 NPC 全部变成没有贴图的空对象——在真实 MPQ 到位之前完全无法察觉。 改为引入 `MONSTER_PREFIX` 常量并由 `.length` 推导,消除手数魔数。 实测(覆盖全部 365 个 scene.json): NPC 实例总数 103 103 其中解析出 COF 成员 0 -> 103 其中解析出精灵帧 0 -> 103 按 token 分解,修复后全部满额: 0J 82/82、DC 4/4、HR 2/2、JE 4/4、XR 2/2、XS 9/9。 抽查:Deckard Cain 现在解析到 `data\global\monsters\DC\TR\DCTRLITNUHTH.dcc`,帧 43x75,偏移 (-45,-54)。 普通场景物件仍走 `objects\`,未受影响。 --- scripts/pack-act-assets.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/pack-act-assets.ts b/scripts/pack-act-assets.ts index 6b10bf1..161d52d 100644 --- a/scripts/pack-act-assets.ts +++ b/scripts/pack-act-assets.ts @@ -64,6 +64,8 @@ const PAGE_SIZE = 2048 const HOT_RADIUS_CELLS = 8 /** Where object art lives. */ const OBJECT_PREFIX = 'data\\global\\objects\\' +/** Where monster and town-NPC art lives. */ +const MONSTER_PREFIX = 'data\\global\\monsters\\' /** * Mode directories tried in order when picking an object's art. * @@ -347,9 +349,12 @@ const objectMembers = new Map>() const monsterMembers = new Map>() for (const name of allNames) { const isObj = name.toLowerCase().startsWith(OBJECT_PREFIX.toLowerCase()) - const isMon = name.toLowerCase().startsWith('data\\global\\monsters\\') + const isMon = name.toLowerCase().startsWith(MONSTER_PREFIX.toLowerCase()) if (!isObj && !isMon) continue - const prefixLen = isObj ? OBJECT_PREFIX.length : 22 // 'data\\global\\monsters\\'.length + // Derive the length; a hand-counted magic number here was off by one (22 vs + // the real 21), which sliced the first letter off every monster token and + // silently broke all NPC art lookups. + const prefixLen = isObj ? OBJECT_PREFIX.length : MONSTER_PREFIX.length const rest = name.slice(prefixLen).split('\\') if (rest.length < 3) continue const token = (rest[0] ?? '').toUpperCase()