troytt
526d623f66
Merge branch 'feat/automap-113c'
...
# Conflicts:
# public/ui/manifest.json
# src/scene/act-scene.ts
# src/ui/baked-ui-meta.ts
2026-09-28 01:33:06 +00:00
troytt
14aa512e85
feat(automap): implement authentic Diablo II v1.13c isometric sprite Automap & Minimap
2026-09-28 01:27:10 +00:00
troytt
b6fc2512d4
feat(drlg): generate Act I outdoor levels with the 1:1 D2MOO port ( #455 )
...
The eight Act I outdoor levels (Blood Moor 2, Cold Plains 3, Stony Field
4, Dark Wood 5, Black Marsh 6, Tamoe Highland 7, Burial Grounds 17, Moo
Moo Farm 39) are now generated by the function-by-function port of
D2MOO's D2Common DRLG in src/game/drlg, bit-exact against a native D2MOO
oracle. The legacy Act I generator (src/game/wilderness/*, the Blood Moor
branch and the Act I paths of wilderness.ts) is deleted; there is no
fallback. Acts 2-5 are unchanged.
Port (completes the kernel committed earlier):
- Tile-map stage: DRLGROOMTILE_AddRoomMapTiles with its tile-data,
linked-tile and warp helpers, DRLGPRESET_AddPresetRoomMapTiles,
DRLGOUTROOM_InitializeDrlgOutdoorRoom (drlg-outroom.ts), the
DrlgDrlgLogic.cpp parts they reach (drlg-logic.ts), D2CMP shim updates.
- drlg-dump.ts: runAct1() driver API; schema 3 dumps with per-room tile
lists. The port is now type-checked (tsconfig no longer excludes it).
Integration:
- drlg-map.ts (replaces drlg/adapter.ts): turns a generated level into
the bake's DS1-shaped canvas with exact (style, sequence, type) per
tile and per-cell DT1 masks, objects, seams from the DRLG level links,
warps from their UNIT_TILE preset units; blank cells stay blocked.
- pack-act-assets: one D2 game seed per (act, variant), a DRLG bake job
and index.json actLayouts; scripts/lib/drlg-mpq-source.ts feeds the
port from the MPQs with the oracle's file list.
- act-variants.ts: WorldVariants picks one generated copy per act (and
the Rogue Encampment DS1 that copy fixed); act-scene.ts and
verify-world-walk both use it. act-scene no longer paints grass into
the blank cells of DRLG scenes.
- world-graph: remove the invented Stony Field <-> Dark Wood seam
(gAct1WildernessDrlgLink / gAct1MonasteryDrlgLink keep them apart;
they meet only through the Underground Passage); world-placement
anchors the second group at the Monastery Gate (3000, 1000).
- level-links: findBorderOpening takes an optional edge range.
- d2map: dt1MaskAllows extracted, behaviour unchanged.
Tools and tests:
- tools/d2moo-oracle: tile-map stage and schema 3; the 4 fixtures are
regenerated (scripts/drlg-fixtures.ts).
- scripts/render-drlg-compare.ts (both dumps through the same adapter
and renderer, fails unless the XOR is empty) and
scripts/audit-levels-browser.ts (headless audit of every baked map).
- tests/drlg-act1-oracle.test.ts (all stages, all 4 fixtures) and
tests/drlg-act1-invariants.test.ts; tests and stress scripts of the
deleted generator are retired or now run on Acts 2-5 only.
- verify-world-walk: start in the town the runtime opens and floor the
spawn sub-tile like the movement collision does.
Verification:
- drlg-diff --levels 2-7,17,39 --seeds 32: 32/32 identical at every
stage; --trace-check 64/64, --isolated 32/32, difficulty 1 and 2 16/16.
- render-drlg-compare, 3 bake copies x 8 levels: 24/24 XOR empty.
- Full re-bake: 365 maps, 0 missing tiles. Browser audit: 355/365 maps,
133/136 level ids clean (112/124/129: Act 5 minions without art, as
before).
- verify:world-graph 37583 assertions; verify:world-walk 29/29;
verify:entity-packs, typecheck clean. vitest 6369 passed; the 6
failures (5 ground-item name formatting, 123-var3 waypoint) and the
verify:packs / verify:generators failures also occur at bad6806 .
Test pins updated, measured on the new bake: SceneEntrance 172 -> 166
and world-graph edges 286 -> 284 (the removed seam), seamless pairs
27 -> 26 and placement pairs 19 -> 18; full-spectrum floors / walls /
PNGs 1008898 / 237542 / 1390 (the old pins matched no bake of the
current code).
TAG=agy
CONV=a4f9163c-a011-4161-b169-7f361b1ed57e
2026-09-27 15:20:41 +00:00
troytt
acdeef7daa
fix(waypoint): 修复冰冷之原等14处场景小站未渲染缺陷并消除全量跨幕ID冲突 ( #409 )
...
- 在 object-lookup.ts 中导出权威小站判定辅助函数 isWaypointDs1Object 与 isWaypointObjectsTxtId
- 根除 wilderness.ts 中跨幕混淆的 ALL_WAYPOINT_IDS 扁平集合,使用 isWaypointDs1Object 精确识别预设小站
- 修复 maze.ts 中神秘避难所(Level 74)中心房变体被 assignVariants 覆盖重置的问题,确保 SanctNSEW4.ds1 与小站实体完好保留
- 修复 maze.ts 中迷宫房间内部嵌入小站实体的按幕精确识别与坐标统计
- 完善 pack-act-assets.ts,通过 isWaypointObjectsTxtId 识别 Objects.txt 行号 38(WT,ExpansionWildernessWaypoint),消除亚瑞特高原(Level 112)贴图与交互坐标脱节问题
- 在 verify-packs.ts 中新增全量 39 个小站场景 109 个变体的严格 Ground-Truth Invariant 断言(source 必须为 'object' 且 scene.objects 必含渲染实体)
- 新增 tests/waypoint-fidelity-regression.test.ts 专项全量回归测试套件并更新基线哈希
Closes #409
2026-09-24 06:10:49 +00:00
troytt
a8295c265d
feat(render): refactor transparency and blend modes for light beams and monsters
2026-09-23 14:29:50 +00:00
troytt
ce6fdf131e
fix(render): animated torches, candles, environmental fires, and DT1 tile sequences ( Fixes #398 )
2026-09-23 08:58:23 +00:00
troytt
3704e7d088
feat(scene): authentic 1.13c animated map objects and phase staggering ( Fixes #387 )
...
- Decode and extract all frames for map objects (DCC/DC6) in scripts/pack-act-assets.ts
- Deduplicate unique member art across object placements and pack into objectPages atlas
- Serialize animatedFrames, frameDelta, frameDurationMs, cycleAnim, and sync from Objects.txt into scene.json
- Support multi-frame MapRuntime ObjectDrawable ticking and rendering in src/scene/act-scene.ts
- Introduce SplitMix64 spatial phase hash (computeObjectPhaseOffsetMs) for non-synced objects to eliminate unnatural mechanical synchronization across scene torches and campfires
- Preserve backward-compatibility for single-frame static objects (Stash B6)
- Add 100% passing test suite in tests/object-animation-113c.test.ts
2026-09-23 03:14:14 +00:00
troytt
752d7e8066
feat(map): authentic Diablo II 1.13c shadow layer (type 13) rendering and pack verification
2026-09-23 01:22:51 +00:00
troytt
e2a74cc8f0
feat(ui/controls): 实现完整 UI/HUD 系统、操作快捷键对齐与装备词条染色 ( fixes #134 , fixes #135 , fixes #136 , fixes #137 , fixes #138 , fixes #139 , fixes #140 )
...
- R1 (Issue #134 ): 离线烘焙预生成 UI 图集 (public/ui/) 与 embedded DropTables,彻底消除 2 秒纯色占位回退,实现零运行期 MPQ/DLL 依赖
- R2 (Issue #135 ): 修复地图缩放 (0.4x~8.0x) 及多 DPR 下 NPC 名称标签与模型顶部的投影对齐
- R3 (Issue #136 ): HUD 与全屏画布采用 1:1 纵横比等比缩放与居中,彻底修复宽屏下红蓝球被横向拉伸为椭圆的问题
- R4 (Issue #137 ): 传送点菜单 (Waypoint Menu) 支持点击无缝跨场景传送,随机选取地图变体并平稳降落在对应小站子网格
- R5 (Issue #138 ): 角色属性面板 (InvChar6) 文本与数字完美对准背景凹槽石框,加点按钮对齐
- R6 (Issue #139 ): 物品栏 5 级回退 DC6 图标渲染,消灭空白占位框;装备 Tooltip 等比缩放与视口翻折防遮挡
- R7 (Issue #140 ): 任务日志界面集成 1~5 幕全量 27 个任务图标 (a1q1..a5q6) 及 27 帧已完成印章,第 4 幕自动进行插槽钳制保护
- Controls Parity: 还原原版左键移动/NPC交互/小站/攻击,Shift 强制原地站立攻击,右键释放技能与被动/无技能移动回退,解除 A/W/S/1..4 快捷键与移动冲突
- Tooltips & Dyes: 基于 1.13c PL2 调色板 Table 104 (cgrn) 渲染翡翠绿军帽,战争召唤按 descpriority 完整呈现 11 行符文之语属性与三行正统标题
2026-09-21 11:09:10 +00:00
troytt
8ebedb2a35
fix(map-gen/act5): isolate snow and dirt barricade dt1 libraries and filter broken debris presets ( fixes #111 , #120 )
...
- Differentiate Act 5 Barricade SubType 10 (dirt) and SubType 11 (snow) in wildernessDt1Mask
to load Ground.dt1 (mask 0x01) and snow.dt1 (mask 0x10) exclusively, eliminating 5x5 checkerboard grid.
- Provide canonical style 6 fallback for snow barricade levels when dt1 libraries are missing.
- Exclude broken debris.tg1 border presets in getWildernessPieces for Act 5 Barricade.
- Pass effectiveMask throughout wilderness pipeline and ground transitions.
- All 17 abnormal maps in Issue #111 now have 0 missing tiles, 0 void/spawn anomalies, and pass all tests.
TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-19 05:46:05 +00:00
troytt
985d85ebb0
fix(wilderness): 修复 pack-act-assets 漏传 dt1Libraries 及 wilderness 自动贴图跨章节污染 (Issue #113 )
...
- scripts/pack-act-assets.ts: 在 generateWilderness 中透传 dt1Libraries: resolvedLibraries,保证地面自动过渡与瓦片解析能获得关卡所加载的全部 DT1 库。
- src/game/wilderness.ts:
- applyGroundTransitions: 当 options.libraries 为空或未指定时直接返回 0 处过渡,修复 isTileAvailable 无库时无条件返回 true 的漏洞。
- 增加关卡类型与章节编号守卫(非 Act 1 - Wilderness 关卡与非 Act 1 关卡跳过),避免对 Mesa (104)、Desert (43)、Barricade (112, 117) 注入无效瓦片。
- 在 phase 1 与 phase 2 中严格校验 (update.style, update.sequence) 瓦片是否存在于当前 DT1 库中,确保不会写入不存在的过渡瓦片。
- tests/wilderness-ground-blend.test.ts: 更新并补充 auto-tiling 测试,覆盖无库安全回退以及非 Act 1 关卡拦截。
TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 14:26:16 +00:00
troytt
5d6dc8a15f
feat(anim): 接入 LvlPrest.txt 的 Animate 列还原逐关卡动画速度 (Issue #85 )
...
- 从 LvlPrest.txt 读取 Animate 列作为动画速度 animSpeed (D2 1.13c 默认速度 80,即 80/256 帧/tick @ 25Hz => 128ms/帧)
- 在 animated-tiles.ts 中提供 frameDurationMsFromAnimSpeed 公式函数与 128ms 默认帧时长
- 统一在 acts.ts、maze.ts、preset.ts、wilderness.ts、pack-act-assets.ts 中解析并传递 animSpeed 与 frameDurationMs
- 在 act-scene.ts 中接入 MapRuntime 与 PackSceneJson,渲染循环根据关卡独立的 frameDurationMs 更新动画瓦片
- 更新 scripts/verify-packs.ts 校验门禁并通过 1671/1671 项断言
- 新增 tests/animated-tiles-speed.test.ts 专项单测,全量 vitest 1137 测试全通
TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 10:57:00 +00:00
taodao
a15da9e000
feat(maze): 还原相邻 Room 边界消隐去重 (bKillEdge) 与门洞平滑衔接 (Issue #77 )
...
以 Diablo II v1.13c D2Common.dll 为黄金标准,在 maze.ts 的 stamp() 中补齐两处
原版装载期行为。
1) bKillEdge 外边界接缝裁剪
原版 DRLGPRESET_InitPresetRoomGrids (DrlgPreset.cpp:1105) 由 LvlPrest.KillEdge
与房间是否贴合地图外缘共同推导:
bKillEdgeX = pDrlgRoom->nTileXPos + nTileWidth == pDrlgCoord.nPosX + nWidth
再由 DRLGROOMTILE_LoadInitRoomTiles (DrlgRoomTile.cpp:449) 转成
nTileCountX = nTileWidth + (bKillEdgeX == 0)。
- MazePiece 新增 killEdge 字段(来源 LvlPrest.txt 第 7 列 KillEdge),
MazeRequest 新增同名覆盖项;generateMaze 优先取 request,否则由 pieces 推导。
- stamp() 在 KillEdge 生效时将合成图收窄一列一行,并对贴合外缘的房间按
variant.width - 1 / height - 1 落图,内部接缝的一格重叠保持不变。
- pack-act-assets.ts 的 getMazePieces 读取真实 KillEdge 列。
经实测 MPQ 内 LvlPrest.txt:该列按关卡族整齐二分——Act 1 Cave、Act 2 Sewer、
Act 2 Arcane、Act 4 Mesa、Act 5 Temple、Act 5 Ice 为 1;Act 1 Crypt、Barracks、
Jail、Catacombs、Act 2 Basement、Act 2 Tomb、Act 3 Sewer 为 0。此前引擎完全忽略
该列,上述 6 个关卡族生成的地图恒比原版多一列一行。
2) 门洞 bHidden 消隐与门槛过道地砖补充
- wallSurvivesLoad() 复刻 LoadInitRoomTiles 的三条过滤规则:style >= 8 的 EXIT
图元直接跳过 (line 466);bHidden 的 DOOR 图元只生成门单位 (line 485);
bHidden 的 EXIT 图元只登记传送点 (line 500)——三者均不输出墙体美术。
- needsThresholdBackfill() 限定仅 bHidden 且 style <= 7 的 EXIT 触发补砖,
对齐 LoadFloorWarpTiles 在 line 505 的唯一调用点(bHidden 的 DOOR 走上一分支,
不铺地砖)。
- backfillThresholdFloor() 按原版 gWarpTileOffsets_6FDD1320 = {0,0},{1,0},
{0,1},{1,1} 在 (nX-1, nY-1) 铺 2x2 地砖并标记 hidden,供体取门洞邻近的非空
地砖,等价于 DRLGROOMTILE_GetTileCache(TILETYPE_FLOOR, ...)。
- 补砖在所有房间落图完成后统一执行:门洞位于两房交界,供体可能属于当时尚未
落图的邻居。
verify-packs 只校验 preset 关卡(entry.kind !== 'preset' 时跳过),故迷宫尺寸
变化不会影响 1671 项逐字节断言。
新增 tests/killedge-113c.test.ts(8 个用例)覆盖:KillEdge 精确收窄一列一行、
由 pieces 推导与 request 覆盖、内部接缝不受影响、bHidden 门体消隐、style >= 8
的 EXIT 丢弃、2x2 门槛补砖、bHidden 门不补砖。已做变异测试:关闭裁剪或关闭墙体
过滤各会导致 3 个用例失败,确认断言有效。
Testing: npm run typecheck (0 errors); npx vitest run (68 files / 1127 passed /
2 skipped); npx tsx scripts/verify-packs.ts (1671/1671).
TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 09:05:17 +00:00
troytt
0b08b4e125
fix(assets): track fallback warps in stats.notes and set assertion cap in verify-packs
...
- Track fallback warps in buildSceneLinks with level and destination names, recording to stats.notes in scene.json
- Expose notes and unresolved in WildernessResult.stats and SceneLinks.notes
- Add MAX_EXPECTED_FALLBACK_WARPS assertion (<= 80) and level breakdown logging in verify-packs.ts to prevent silent proliferation of invented staircases
TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 13:55:56 +00:00
troytt
a3a3e44838
refactor(acts): unify ACT_TOWNS and townLevelForAct single source of truth
...
TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 13:31:07 +00:00
troytt
5b3a9db986
fix(maze): 让 UNIMPLEMENTED_PASSES 反映真实缺口
...
`UNIMPLEMENTED_PASSES` 此前被硬编码为 `[]`,注释声称 11 个核心地牢 pass 全部
实现,于是 `stats.unimplementedPasses` 在每一关都无条件报告「零缺口」——包括
那些 `specials` 表为空、根本不生成楼梯房间的 level type。
改为从 `MAZE_LEVEL_TYPE_PROFILES` 实测推导:
- 新增 `unimplementedPassesFor(levelTypeName)`,按 level type 返回缺口:
没有 profile 行,或 `specials` 为空且不是 `singleRoom` 定制关。
- `UNIMPLEMENTED_PASSES` 改为所有 level type 缺口的并集,因此非空。
- `generateMaze` 的 `stats.unimplementedPasses` 改报本关自己的缺口,
而不是全局常量;`unresolvedRoles` / `notes` 本来就已随 stats 返回。
同时修正两处已经与代码不符的注释:`maze.ts` 里「per-act 装饰 pass 均未复现」
(它们已在 `maze-special-passes.ts` 中实现),以及 `pack-act-assets.ts` 里
「第二幕下水道 / 墓穴 / 第三幕地牢 specials 为空」(这三者现在都有 specials,
真正为空的是 Harem、Basement、Spider、Act 5 Lava)。
测试:原 `UNIMPLEMENTED_PASSES is completely empty` 断言的是被修正的错误行为,
改为断言它恰好列出所有 `specials` 为空的 level type;另加 4 个用例覆盖
按 level type 查询与 `generateMaze` 的 stats 透出。
TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 11:29:06 +00:00
troytt
893e9b8550
Merge branch 'fix/issue-48'
...
# Conflicts:
# src/game/maze.ts
2026-09-17 08:41:40 +00:00
troytt
e1163b1253
Merge branch 'fix/issue-47'
...
# Conflicts:
# src/game/maze.ts
2026-09-17 08:40:55 +00:00
troytt
801afd7ddb
fix(map-gen/maze): enforce Levels.txt global bounds SizeX/SizeY on room growth ( fixes #48 )
2026-09-17 06:59:31 +00:00
troytt
776e2d3c20
feat(map-gen/maze): integrate waypoint room into maze preset replacement pass ( fixes #47 )
2026-09-17 06:33:16 +00:00
troytt
72aa835260
refactor(map-gen/preset): move Chaos Sanctuary (Level 108) into preset pipeline ( fixes #45 )
2026-09-17 06:20:35 +00:00
troytt
e9f2ceec9b
feat(map-gen/population): implement wilderness population phase, waypoints, shrines, superuniques & safe zones ( fixes #36 )
2026-09-17 03:44:12 +00:00
troytt
29cfccaa37
feat(world): 全局世界连通、关卡无缝切换、传送点、传送门与小地图 ( fixes #23 )
...
实现《暗黑破坏神 II》全五幕(136 个关卡)的完整世界连通拓扑、烘焙期链接落位、
运行时无缝边界与楼梯渐变过渡、传送点网络、城镇传送门以及实时小地图。
1. 全局世界连通拓扑 (src/game/world-graph.ts)
- 融合 Levels.txt (Vis0..7, Warp0..7)、28 对野外无缝相邻接缝 (SEAMLESS_ADJACENCY)、
23 对双向传送门链接 (PORTAL_LINKS) 以及 LvlWarp.txt (88 行几何偏移)
构建 136 关卡、288 条有向边连通图。
- 实现确定性边界方向分配器 (assignGateSides),保证同一关卡的多条边不会冲突。
2. 楼梯瓦片识别与几何计算 (src/game/warp-tiles.ts, src/game/level-links.ts)
- findWarpTiles: 扫描 DS1 墙体层 (type 10/11) 提取 style 0..7 对应 Warp0..7 瓦片。
- level-links: 显式栈 4-连通分量洪水填充提取最大可行走主区域 (largestWalkableRegion),
向内扫描边界缺口 (findBorderOpening),以及计算安全向内落脚点 (seamArrivalSpot)。
3. 野外道路与锚点导出 (src/game/wilderness.ts, src/game/acts.ts)
- planAct1OutdoorLevel 导出城镇过渡口、野外边界门、洞穴入口与路边地标。
- 实现泥土路网与 256 项 8 邻域位掩码地砖自动铺设。
4. 烘焙期流水线增强 (scripts/pack-act-assets.ts)
- buildSceneLinks 5 级流水线:DS1 真实瓦片 -> 野外生成器锚点 -> 迷宫楼梯间 ->
预设边界扫描与营地四朝向自适应 -> 兜底安全落脚点。
- 全量重新烘焙 samples/d2-packs (365 张地图),输出 entrances, warps, waypoints
与 world-graph.json。
5. 运行时交互与表现 (src/scene/act-scene.ts, src/scene/transition.ts, src/game/portal.ts, src/ui/minimap.ts)
- 支持接触边界无缝切换 (90ms 淡入淡出) 与按 G 键 / 交互楼梯传送 (220ms 淡入淡出)。
- swapLevel 保留玩家状态(位置、属性、背包、随从)并在对应配对出入口安全落地。
- WaypointNetwork: 小站发现与跨图传送;TownPortalSlot: T 键回城与往返传送。
- Minimap: Tab 键切换右上角小地图 / 全屏自动地图,绘制迷雾、玩家、出口、小站、回城门。
6. 验证护栏与测试
- verify:world-graph: 40,003 项断言确认全 136 关卡全连通、边双向对称。
- verify:world-walk: 模拟玩家徒步全图 338 张有出口地图,1046/1046 出入口 100% 互达。
- 补齐 tests/world-graph.test.ts, tests/warp-tiles.test.ts, tests/wilderness-roads.test.ts。
2026-09-16 23:39:33 +00:00
troytt
602565030f
fix(map-gen): fix Act 4 Chaos Sanctuary, Act 3 Jungles/Travincal/Kurast, Act 2 Deserts & Act 5 Frozen Tundra layouts
2026-09-16 03:30:40 +00:00
troytt
25ae7289ee
fix(map-gen): assemble Act 5 Bloody Foothills 15 siege strips, Barricade fortifications & Act 1 stone walls/dirt roads (Issue #35 )
2026-09-16 03:06:48 +00:00
troytt
2527678c10
fix(packs): restore full Acts 1..5 index and map/bake all 741 monster IDs across all acts ( #33 )
2026-09-16 01:43:11 +00:00
troytt
270c743aef
perf(entities): offline pre-bake Sorceress and monster atlases into Act packs ( #30 )
2026-09-16 01:16:38 +00:00
taodao
cc12e7bc66
M7 第二步:怪物接进关卡,并补上 MonLvl 缩放
...
Stage A 把 MonStats.txt 读了出来,接上去才发现读出来的数字不能用:
一只一级堕落者按表里的 A1MinD=51 / A1MaxD=101 算,一击 76 点伤害,
而玩家只有 60 点血。
MonStats.txt 里的数字不是游戏里的数字。真实公式是
最终值 = MonStats[列] × MonLvl[怪物等级][对应列] ÷ 100
这不是猜的。同一个公式让五个互不相干的列同时对上游戏已知数值:
fallen1 的 Exp 61→18.3(游戏 18)、zombie1 的 111→33.3(33)、
quillrat1 的 71→21.3(21)、fallen1 的 AC 84→5.0(5)、
A1TH 101→8.1(8)。五发全中不是巧合,所以照做而不是推给 M9。
顺带纠正我在 Stage A 里写下的判断:MinHP(N)/MinHP(H) 不是噩梦和地狱
的血量,594 个有血量的行里有 426 行的 MaxHP(N) 比普通还低。难度靠的
是怪物自己的 Level(N)=36、Level(H)=67 再过一遍 MonLvl 的 HP(N)/HP(H),
这才是噩梦堕落者有 131-289 血而不是 25-55 血的原因。MonLvl 的 L- 前缀
列(扩展版那套)在 1.13c 里和原列逐字节相同,读哪套都一样。
boss 查过要不要豁免缩放,没有豁免:缩放后的 Diablo 是 13818,记忆里
是 13741;缩放后的 Andariel 是 1025,记忆里约 960;但 Duriel 常被引用
的 4757 是原值。凭这种强度的记忆去开特例就是猜了,幕 boss 单独处理。
接线:
- monsters.ts 新增 readMonsterScaling / monsterScaleFor / UNSCALED,
monsterStatsOf 接受一个 MonsterScale。先掷骰再缩放——低等级的乘数
只有百分之几,先缩放边界会把 21..61 压成 1..4 再四舍五入,一个范围
里四个值有三个取不到。
- 新增 planLevelMonsters:从关卡 id 一路走到掷好点数的怪物群。在线
路径和资源包烘焙都走它,「烘焙出来的关卡和在线的一致」因此是个性质
而不是巧合。
- engine.ts 新增 monsterPacks 选项,优先于 stats + monsterCount。
- act-scene.ts 两条路径都接上;资源包路径读 scene.json 里新烘焙的
monsters 字段,浏览器在这条路径上没有 Levels.txt 可读。
- pack-act-assets.ts 烘焙怪物,每张图 scene.json 增加约 4 KB。
血色荒野现在是:堕落者 2-4 血 / 2 伤 / 18 经验,僵尸 8-13 血 /
33 经验 / 移速 28(堕落者是 142),棘鼠 2-5 血 / 21 经验。51 只怪
分 30 群。
验证:tsc 零错误;npm test 523 通过(基线 461);
npm run verify:monsters 65/65;npm run verify:all 通过;
npm run verify:packs 1302/1302 逐像素断言通过。
TAG=agy
CONV=c89513df-cd12-4749-b5bd-5e9f16639391
2026-09-15 08:22:48 +00:00
troytt
2d7f5f7a00
fix(wilderness): add interior stone walls, probabilistic river, and perimeter opening control
...
- Implement isClosedBorder, selectBorderVariant, and getPerimeterOpenings
- Moo Moo Farm (Level 39): enforce 0 exits (100% closed perimeter), add interior corrals and stone fences
- Blood Moor (Level 2): enforce exactly 1 entrance (South) and 1 exit (East), add stone corrals/fences and probabilistic river/bridge presets
- Add 'Act 1 - Fence Fill', 'Act 1 - River', 'Act 1 - Bridge' to Act 1 Wilderness piece families
- Add comprehensive unit tests in tests/wilderness-features.test.ts
- Optimize publish-packs.ts with batch incremental pushes by Act
2026-09-15 05:25:09 +00:00
troytt
36d5624b14
fix(wilderness): 修复空地面图层擦除底图与补全第一幕专属预设景观 (refs #15 )
...
1. 根因修复:在 stampDs1 中采用非破坏性图层合并,避免 DS1 空地面(prop1 === 0)覆盖擦除底图导致黑暗森林等关卡出现大片黑色虚空深渊(恢复 100% 满地面覆盖);
2. 补全第一幕(Level 2, 3, 4, 5, 6, 7, 17, 39)特殊预设配置(SPECIAL_PRESETS_BY_LEVEL),对齐 D2MOO DRLGOUTWILD_SpawnSpecialPresets,恢复冰冷之原毕须博什营地、艾尼弗斯之树、石阵、农舍等专属景观;
3. 扩充 WILDERNESS_PIECE_FAMILIES 并增加针对 Level 3 与 Level 5 的回归断言护栏;
4. 全量 70 个迷宫关卡 + 31 个野外关卡通过 verify:generators 自动化测试(785/785 项断言通过)。
2026-09-15 01:52:12 +00:00
troytt
bbc1af7a72
fix(wilderness): 补全奶牛关 Moo Moo Farm 专属预设景观生成 ( fixes #15 )
...
- 在 src/game/wilderness.ts 中实现户外特殊预设生成 spawnSpecialPresets,针对奶牛关(Level 39)按 D2MOO 规范放置牛圈 Corral、营地 Bivouac、水塘 Pond 与沼泽/石圈等专属景观
- 保持内部网格碰撞检测与确定性洗牌放置,防止预设重叠与边界穿越
- 扩充 scripts/verify-generators.ts 与 scripts/pack-act-assets.ts 中 Act 1 - Wilderness 预设家族
- 增加 Level 39 的专用断言,验证连通性 100% 与图块 0 缺失
- 重新打包地图数据并通过全量 verify:generators 与 verify:packs 校验
2026-09-15 00:23:51 +00:00
troytt
e180520ef5
fix(npc): 补齐城镇 NPC 解析的收尾问题并加上回归护栏 (refs #6 )
...
承接前两个提交。上一版把 type-1 条目接了进来,但收尾有几处缺口,
逐项补齐:
1. 恢复被误删的注释
`objectsTableTyped` 上方 `/** Same table, through the typed loader ... */`
在重构中被删掉了,与改动无关,还原。
2. token 大小写统一
`monstats.txt` 的 `Code` 列拼写不统一——同一批数据里既有 `K9` 又有
`k9`,`ja` / `6z` / `7i` / `7j` 也是小写。而 `monsterMembers` 的索引
键一律大写,于是 `map.get(token)` 直接落空,Jamella 和三个受伤的野蛮人
被以「无美术」的形式烤进了 scene.json。上一版在查表处补了
`.toUpperCase()`,这里进一步在**产出源头**归一化:同一个怪物不会再以
两种拼写出现,调用方也不必猜大小写。
3. 空 Place 由静默跳过改为显式报错
`presetPlaceByAct` 的数组下标**就是** DS1 的 type-1 id,数组必须保持
稠密。原先的 `if (!place) continue` 一旦遇到空格子,会让其后每一个 NPC
都错位认领邻居的身份——正是 `22` vs 21 那个缺陷的同一类。实测 1.13c
五个 act 共 0 个空 Place,所以这条守卫永远不会触发;它的意义是万一数据
变了要**响亮地失败**,而不是悄悄错号。
4. 补两层回归测试
- `tests/resolve-type1.test.ts`:清掉两个未使用的 import、补回项目惯用的
`.ts` 后缀、把边写边想的注释换成「这条用例为什么存在」,并新增一条
覆盖小写 `Code` 归一化的用例。
- `tests/packed-npc-art.test.ts`(新增):数据层不变式——凡是进入
scene.json 的 NPC 必须同时具备 `member` 与 `frame`;同一 token 不得
以两种拼写出现。这是之前唯一能抓住那 4 个哑火 NPC 的层次,单元测试
抓不到,因为缺陷在打包脚本的查表里。
守卫锚定在它真正读取的 `samples/d2-packs/index.json` 上,而不是
`samples/d2` 目录。
验证(全部本地实跑):
tsc --noEmit 0 error
vitest 23 文件通过 / 1 跳过,429 用例通过 / 2 跳过
pack:data 365 张地图
verify:packs 1302/1302 断言通过
NPC 实例 889,未解析 0,token 拼写不一致 0
变异测试:
- 把 `presets[objectId]` 改成 `presets[0]` → resolve-type1 2 条转红
- 还原 `map.get(token.toUpperCase())` 为 `map.get(token)` → 全量套件
仍然全绿,证明此前该修复毫无回归保护;本提交的 packed-npc-art 补上了
这一层(改动前该用例对现有 pack 报 `K9 -> K9, k9`,重新打包后转绿)。
2026-09-14 15:58:51 +00:00
troytt
3cd4549332
fix: 修复非基底对象 NPC 丢失及其资源检索区分大小写的问题
...
在前一次提交的基础上修复并强化了 Type 1 对象(怪兽及 NPC 原型)解析与打包的正确性。
1. 修正大写回退遗漏问题(解决失联的 6 个特定角色如 Act 4 Jamella 等):`pickObjectMember` 中需要使用大写查找 token,而原来直接代入了小写 token 导致失败,修复后所有角色均成功合图。
2. 添加安全跳过统计 (`skippedMissingArtSpawns`),使得未匹配上图片的隐藏对象能干净跳过。
3. 增加 `tests/resolve-type1.test.ts` 提供核心覆盖率。
4. 应用性能重构与规范修复,将二次查询提速为内置的 `Map` 遍历,同时补齐了 `OBJECT_TYPE_MONSTER`、Typescript interface、尾随空格与逗号修复。
5. 后向保留了省略 tables 时旧版逻辑的注解。
(refs #6 )
2026-09-14 15:37:32 +00:00
troytt
451816d627
fix: 修复非基底对象 NPC 丢失问题
...
Diablo 2 城镇中的部分 NPC(如 Gheed, Charsi, Akara, Kashya, Warriv)没有定义在
Objects.txt 作为 Type 2 对象,而是通过 MonPreset.txt 定义为 Type 1(怪兽生成点)。
以前此类对象被一律视为 kind: 'monster' 并且放弃生成(silently dropped)。
此修复:
1. `src/game/acts.ts`: 在 `loadActTables` 中增加了 `MonPreset.txt` 和 `monstats.txt` 解析。
2. `src/game/objects.ts`: `resolveDs1Object` 现在接受 `monsters` 表以处理 `type === 1` 对象查询:
`MonPreset` 通过 Act + `id` => `Place`;
`monstats.txt` 的第一列 (Id, 恰好对应 Place) => `Code` (对应资源 token) 和 `NameStr` (角色名)。
3. `scripts/pack-act-assets.ts`: 将解析后的新表传递给 resolver。对真实存在艺术资源的 `kind: 'npc'` 进行打包;对于确实无资源的占位对象(如 place_nothing, trap-*)现在会干净地跳过并做数量统计 (Skipped artless spawns),不再将它们报告为艺术丢失或引发异常。
4. 抽象统一 `MONSTER_ROOT` 并移除所有写死的 'data\\global\\monsters\\' 字面量。
5. `src/game/npc.ts`: 扩展 `NPC_NAME_FALLBACKS` 清单,解决部分名称未定义的显示为短码的问题(例如 RG 替换为 Rogue, CW 为 Cow 等)。
在保证现有 Type 2 行为完全不变和所有测试继续通过的前提下,总共正确重新召回了 67 个原先丢失的 Type 1 镇民、卫兵遗漏和活物 (Cow, Chicken等) 实例。
(refs #6 )
2026-09-14 15:14:15 +00:00
troytt
68c7890eee
fix(packer): 修正 monsters 前缀长度,恢复全部 NPC 美术解析 (refs #6 )
...
打包器在切分 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\`,未受影响。
2026-09-14 14:51:49 +00:00
troytt
11e0688930
feat(packer): 提取可交互 NPC 的地图定位并独立存储
...
将场景数据生成时对 objectType 的判定粒度细化。保留对普通怪物的忽略过滤,对于包含 baseIsMonsters 标识的命名 NPC 记录,将类型标识拆分为 'npc' 并单独放入 scene.json 的 npcs 数组中打包。
2026-09-14 13:08:46 +00:00
troytt
2a0dd4ce25
feat(packer): bake all 101 generated levels into static packs with 3 variants each
...
- Update scripts/pack-act-assets.ts to bake 3 deterministic variants for each maze (DrlgType 1) and wilderness (DrlgType 3) level
- Cache mazePieces, wildernessPieces, substitutions, and palettes for fast one-pass baking
- Populate LEVEL_NAMES_ZH with all 136 standard Chinese level names
- Update variantLabelZh to format var1..var3 as 变体 1..3
- Guard scripts/verify-packs.ts to verify preset levels cleanly while supporting non-preset entries
2026-09-14 11:15:13 +00:00
troytt
72731bb143
feat(objects): 完整实现对象层解码、打包图集与场景深度绘制 ( #5 )
...
- 数据与查找修复:
- 修复 src/game/object-lookup-data.ts 中 Act 3 墨菲斯托红门 (104:342:1Y:ON) 错位问题,恢复 3,615 条记录唯一性。
- 排除 COF 非图元文件,优先选取主体图元 (tr) 与 DCC/DC6 静态帧。
- 资产打包与图集生成 (scripts/pack-act-assets.ts):
- 引入全局 decodedMemberCache,一次性解码去重。
- 将所有有效美术对象装箱至 objectPages (objects-*.png),并正确计算象限锚点偏移 (-16 + box.left / f.offsetX, 16 + box.top / (offsetY - height + 1))。
- 动态统计 objectsArtPending,62 张地图全部实现 0 待解。
- 场景渲染与深度排序 (src/scene/act-scene.ts):
- 预加载 scene.objectPages 纹理并在 beforeunload 中释放,避免内存泄漏。
- 将 objectDrawables 与 runtime.walls 按 (subX+subY)/5 与 cellX+cellY 的统一深度双指针交替绘制,并保持与角色前后的画家算法正确遮挡。
- 自动化验证 (scripts/verify-packs.ts & scripts/verify-object-lookup.ts):
- 新增图集页边界与 0 待解帧断言 (1302/1302 全数通过)。
- 更新查找表统计与点位断言 (20/20 全数通过)。
2026-09-14 09:27:54 +00:00
troytt
897b3735d6
初始提交:从零实现的暗黑破坏神 II WebGL2 引擎(TypeScript + Vite,零运行时依赖)
...
内容概览
- 归档层:MPQ v1 容器(hash/block 表、Storm 加密、分扇区编解码),含 PKWARE
implode/explode 移植;支持 HTTP Range 只读取所需字节。
- 格式层:dc6 / ds1 / dt1 / pal / pl2 / cel / pcx / tbl / sprite,以及 dcc / cof / bitstream。
- 地图层:等距投影场景构建(地板/墙/屋顶分层、逐格瓦片变体加权随机、sub-tile 碰撞),
资源包(索引色 PNG 图集 + scene.json)离线烘焙与逐像素差分校验。
- 角色层:DCC + COF 合成(8 方向),法师行走/站立替换占位棋子。
- 页面:acts.html 全屏画布 + 三级选择器(章节 / 场景 / 细分场景),资源包优先、
可回退到直读归档(?live=1)。
- 验证脚本:verify:all / implode / acts / d2 / packs / dcc / deploy / listfile /
object-lookup / alignment / tiles / generators 等,均带断言与退出码。
数据与许可
- 游戏数据(samples/)与第三方参考源码(reference/)不入库:体积大且无再分发许可,
见 THIRD_PARTY_NOTICES.md。
- 生成关卡(70 迷宫 + 31 野外)尚未烘焙,现状与修复清单见 HANDOVER.md §4f。
2026-09-13 18:10:28 +08:00