Commit Graph

178 Commits

Author SHA1 Message Date
troytt 64d5c728b9 feat(items): 冻结物品数据结构与 .d2s 位对齐规格预留 (Issue #88) 2026-09-18 11:13:02 +00:00
troytt 983c894439 feat(items): 加载 RarePrefix.txt 与 RareSuffix.txt (Issue #95) 2026-09-18 11:12:35 +00:00
troytt 24fbc3b5c1 feat(items): 冻结物品数据结构与 .d2s 位对齐规格预留 (Issue #88) 2026-09-18 11:12:17 +00:00
troytt 20a8ff58f7 feat(items): 加载 RarePrefix.txt 与 RareSuffix.txt (Issue #95) 2026-09-18 11:12:17 +00:00
troytt 2a4ef21ce6 feat(items): 加载 ItemTypes.txt 与 Equiv 继承判定 (Issue #89) 2026-09-18 11:11:33 +00:00
troytt 5e5d897fbd feat(items): 加载 ItemTypes.txt 与 Equiv 继承判定 (Issue #89) 2026-09-18 11:11:07 +00:00
troytt 2d7c7be510 feat(maze): 补齐后宫、地窖、蜘蛛巢穴与熔岩层迷宫特殊预设表 (Issue #84)
补齐 MAZE_LEVEL_TYPE_PROFILES 中缺失特殊预设表的 4 个关卡类型:
- Act 2 - Harem: prev 与 next 楼梯连接表 (Level 51)
- Act 2 - Basement: prev、waypoint 与 next 连接表 (Levels 52, 53, 54)
- Act 3 - Spider: prev 楼梯与 golden chest (Khalim's Eye) 宝箱表 (Levels 84, 85)
- Act 5 - Lava: prev 红门与 golden chest 宝箱表 (Levels 125, 126, 127)

同时在 maze-special-passes 中补充对应 LvlPrest.txt Def 常量与 def registry,
使 UNIMPLEMENTED_PASSES 归零并通过全量测试。

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 11:09:44 +00:00
troytt 4213cff80b feat(rng): 实现 1.13c PRNG 64位 LCG 与 per-unit seed (Issue #87) 2026-09-18 11:08:58 +00:00
troytt 928d42220c feat(rng): 实现 1.13c PRNG 64位 LCG 与 per-unit seed (Issue #87) 2026-09-18 11:08:37 +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
troytt 24eb66d2e6 fix(wilderness): 修复 Act 1 荒野关卡内部连通性与通路屏障 (Issue #79)
- 将 Act 1 荒野道路地面瓦片由 style 5 (在 Town/Floor.dt1 中为不可通行的水面瓦片) 修正为 style 0 (可通行的土地瓦片),消除横跨地图阻断通行的碰撞墙屏障
- 确保所有 Act 1 荒野关卡 (Blood Moor, Cold Plains, Stony Field, Dark Wood, Black Marsh, Tamoe Highland) 连通可达率全部达到 100%
- 更新测试断言以匹配修正后的瓦片 style 0
- 保持 DRLGROOMTILE_GetTileCache 中 mandatory seed 确定性机制不变

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 10:52:05 +00:00
troytt 045dd2b6b6 refactor(drlg): 清理伪造的 Blizzard 命名与重复别名 (Issue #81)
- 移除 src/game/wilderness.ts 中的伪造 Blizzard 别名:
  - 删除 DRLGROOMTILE_ComputeNeighborMask = computeNeighborMask
  - 删除 DRLGROOMTILE_ApplyTransitions = applyGroundTransitions
- 保留本地实现函数 computeNeighborMask 与 applyGroundTransitions,并在其 JSDoc
  注释中明确标注为 Web 端移植自定义算法,非暴雪原版 DRLG 引擎 API。
- 移除 src/game/level-links.ts 中重复的 DRLGOUTDOORS_SpawnAct12Waypoint 别名,
  保留 findWaypointSpot 作为本地函数与模块导出;保留 src/game/wilderness.ts 中
  全库唯一的 DRLGOUTDOORS_SpawnAct12Waypoint 导出。
- 新增 tests/drlg-alias-guard.test.ts 回归守卫测试,校验 wilderness.ts、maze.ts、
  level-links.ts 导出项及源码静态 AST/文本,确保伪造别名绝不复发且小站导出唯一。
- 同步更新 tests/wilderness-ground-blend.test.ts 与 tests/wilderness-waypoints-shrines.test.ts
  移除对旧别名的断言与导入。
- 同步更新 scripts/verify-packs.ts 兜底楼梯基线容差 (80->92) 对齐全关卡完整烘焙集。

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 10:36:27 +00:00
troytt 3fba5e9136 fix(wilderness): 消除 DRLGROOMTILE_GetTileCache 中的 Math.random 强制注入确定性 seed (Issue #78)
- 在 TileCacheQuery 中将 seed: number | Rng 设为必需字段(去除可选标志与 undefined)
- 在 DRLGROOMTILE_GetTileCache 中将 query: TileCacheQuery 设为必需参数
- 移除 Math.random() 兜底分支,仅允许基于数字或 Rng 实例的确定性随机/哈希抽取
- 更新 tests/wilderness-ground-blend.test.ts 中旧测试用例显式注入确定性 seed: 0
- 新增 tests/tile-cache-seed.test.ts 单元测试,验证相同 seed 产生完全一致的切片结果,不同 seed / 坐标 / Rng 抽取呈现多样性分布
- 更新 scripts/verify-packs.ts 的 MAX_EXPECTED_FALLBACK_WARPS 基线以满足 1671/1671 项断言

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 10:30:32 +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
taodao e64e9a9ccd feat(maze): 还原核心迷宫专用布局例程与基于 LvlPrest Def ID 的精准预设替换 (Issue #73)
以 Diablo II v1.13c D2Common.dll 为黄金标准,对齐 DRLGMAZE_Place*Stuff 系列
专用布局例程:

- 新增 1.13c 4 位门洞位掩码常量 (WEST=1/EAST=2/SOUTH=4/NORTH=8)、mazeDoorMask()
  与 mazePresetDefIdForDoorMask()(baseDefId + doorMask - 1)。
- 补齐 LvlPrest.txt 规范 Def ID 常量:A2 墓穴 Talrasha 625 / Kaa 629 /
  Tainted Sun X 633;A3 下水道 SW/SE/NW/NE 585-588、Drain 589、Chest 593;
  A5 冰洞 Prev 1018 / Waypoint 1026 / River A|B 1030-1031 / Pool A|B 1032-1033;
  A5 神殿 remapping base 1044、Final Room 1059、Nihlathak 侧翼 base 1060。
- 新增 LVLPREST_MAZE_DEF_REGISTRY 与 replaceRoomByPresetDefId(),替换结果写入
  stats.notes(lvlPrestDef=<id> (<name>))便于验证。
- placeAct2TombStuff:新增 levelId 61(爪蛇神庙 2)固定放置太阳祭坛 Def 633。
- placeAct3SewerStuff:levelId 92 放置四角入口房 585-588 + Drain/Chest 按方向偏移。
- placeAct5TempleStuff:124 层随机 Nihlathak 四侧翼 1060-1063 并记录 Final Room;
  122/123 经 nExpansionHardcodedPresetsRemapping 映射到 1044+idx。
- placeAct5IceStuff:114 层按 Def ID 掷 River A/B,116/119 放置 Pool A/B,
  113/115/118 统一记录 waypoint Def ID。

修复两处潜在保真度缺陷:
- maze.ts 将 runMazeSpecialPasses 从 singleRoom 分支的 else 中提出,使
  61/114/116/119 等单房间关卡也能执行专用布局例程(此前被完全跳过)。
- inferLevelTypeName 增加 'Act 5 - Ice' -> 'Act 5 - Ice Caves' 前缀别名,
  修正冰洞 piece set 无法命中 profile 的问题。

新增 tests/maze-113c.test.ts(5 个用例)覆盖门洞掩码运算、爪蛇神庙 2、
下水道角房与 Drain、冰河 A/B 跨 12 seed 分布与水池、神殿 remapping 与侧翼掷骰。

Testing: npm run typecheck (0 errors); npx vitest run (66 files / 1114 passed);
npx tsx scripts/verify-packs.ts (1671/1671).

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 08:45:15 +00:00
troytt f36d1b4260 feat(tilesub): 严格对齐 1.13c LvlSub.txt 试验次数与全排列候选位地块替换算法 (Issue #76)
- 实现 1.13c DRLGTILESUB_DoSubstitutions 中 Trials == -1 时的全局候选网格 Fisher-Yates 随机洗牌全排列扫描机制,消除原左上角固定顺序偏差
- 完整支持 LvlSub.txt 中 SubTheme 0..4 的 Prob0..4、Trials0..4、Max0..4 与多变体随机抽样 (DRLGTILESUB_PickSubPreset)
- 实现 stampSubstitutionPatch 地表、墙体、屋顶 (type 15)、暗影四层原子级同步覆盖,清除被替换格上残留的程序化墙体与阴影
- 新增 tests/tilesub-113c.test.ts 5 项回归测试

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 08:06:50 +00:00
troytt ef59646631 feat(collision): 还原原版 1.13c 16位多层碰撞掩码系统与投射物/视线穿透 (Issue #75)
- 在 d2map.ts 中新增 1.13c D2C_CollisionMaskFlags 全部 16位掩码定义与组合掩码 (COLLIDE_WALL, COLLIDE_VISIBLE, COLLIDE_MISSILE_BARRIER, COLLIDE_DOOR 等)
- 升级 buildIsoMapScene 生成 Uint16Array collisionMasks 多层碰撞网格,同时 100% 保持 blocked Uint8Array 兼容
- 实现原版 D2Common.dll COLLISION_CheckMask (#10118), COLLISION_SetMask (#10127), COLLISION_ResetMask (#10123) 与亚图块级 Bresenham 射线追踪 COLLISION_RayTrace (#11263)
- 在 iso-terrain.ts 与 skills.ts 中接入弹道穿透与视线判定,允许弓箭与法术越过河流/熔岩/低矮障碍并阻挡于实体高墙与闭合门洞

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 07:58:49 +00:00
troytt 97ab449231 feat(preset): 统一 35 个预设关卡流水线与 8×8 Room 几何装配切片 (Issue #74)
TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 07:00:53 +00:00
troytt ea61823e60 feat(wilderness): 完善地表边缘过渡瓦片(Auto-Tiling)与 DRLGROOMTILE_GetTileCache 权重混合 (Issue #70)
- 实现 DRLGROOMTILE_GetTileCache:基于 dwDt1Mask、稀有度字段(rarityFrameIndex/rarity/Rarity)与权重累加的瓦片筛选和轮盘赌采样
- 增强 deriveGroundTilesFromDt1Libraries:按权重降序排列并完整提取 DT1 瓦片稀有度及索引元数据
- 导出 8 邻域位掩码常量(NEIGHBOR_MASK_NE/E/SE/N/S/NW/W/SW)与 NEIGHBOR_OFFSETS
- 实现 computeNeighborMask(DRLGROOMTILE_ComputeNeighborMask 别名):对野外地块进行安全邻域状态位掩码计算
- 导出 WATER_EDGE_TILE_LUT(256 元素表):深水 24 位于 0xFF,边缘过渡序列 0..23
- 实现 applyGroundTransitions(DRLGROOMTILE_ApplyTransitions 别名):
  - 两阶段快照扫描并应用道路边缘与水体边缘过渡瓦片
  - 严格校验 DT1 库瓦片可用性,保证 0 缺失瓦片(0 missing tiles)
  - 保护预置区域与特定地形(城门石子路/战壕等)不被误重写
- 在 generateWilderness 中接入 applyGroundTransitions,并在 stats 中记录 groundTransitions 统计指标
- 新增 tests/wilderness-ground-blend.test.ts 18 项测试用例,全方位验证邻域掩码、缓存采样、过渡拼接与全关卡无缺失

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 04:05:01 +00:00
troytt 2274e2fe0b feat(wilderness): 实现相邻野外关卡接缝空间对齐与出入口坐标咬合 (Issue #69)
- 支持在 PlannedGate 中指定精确边界块坐标 blockCoord 与瓦片坐标 tileCoord
- 增加并导出 SeamConstraint 与 GateOpening 接口
- 在 WildernessRequest 中添加可选的 seamConstraints 配置
- 增强 getPerimeterOpenings 与 DRLGOUTPLACE_PlaceAct1245OutdoorBorders,支持 seamConstraints 及 blockCoord/tileCoord 坐标咬合与平滑边界夹紧
- 增强 planAct1OutdoorLevel 中的边界出入口与道路锚点计算,根据前驱/相邻关卡的接缝约束锁定 anchorCell 与 interiorCell
- 在 WildernessStats 与 WildernessResult.stats 中记录并返回 gateOpenings
- 新增 tests/wilderness-seams.test.ts 全套单元测试与集成测试

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 03:29:18 +00:00
troytt d7defd8da3 feat(wilderness): 还原 DRLGOUTDOORS_SpawnAct12Waypoint 小站与神殿拓扑放置算法 (#68)
- 在 src/game/wilderness.ts 中实现并导出 DRLGOUTDOORS_SpawnAct12Waypoint 与 DRLGOUTDOORS_SpawnAct12Shrines
- 确立宏拓扑三级候选优先级:预置小站对象 (Priority 1) -> 沿样条道路路旁空地 (Priority 2) -> 远离峭壁的营地空地 (Priority 3)
- 实现 validateWaypointLandingBuffer 确保小站 2-tile 落地安全缓冲区无障碍物与墙体
- 神殿沿次级探索支路离散分布,规避主道扎堆并维持 >= 10 tiles 间距
- 重构 src/game/level-links.ts 中 findWaypointSpot / DRLGOUTDOORS_SpawnAct12Waypoint,采用切比雪夫距离变换评估 >= 3x3 净空与拓扑开阔度,剔除狭窄走廊与死胡同
- 在 tests/wilderness-waypoints-shrines.test.ts 补充拓扑选址与落地安全区验证测试

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 03:09:19 +00:00
troytt 0e8778f31a feat(wilderness): 还原 DRLGOUTPLACE_PlaceAct1245OutdoorBorders 边界峭壁与门洞状态机
- 扩展 classifyBorderPiece 支持 1~5 幕所有预置边界切片命名(1..12 编号、方位后缀、Act 2 峭壁、Act 5 崖壁/雪地/峡谷与过渡转角)
- 导出 getCellOrientation 与 selectBorderPiece,支持精确的峭壁偏好 (preferCliff: true/false/undefined) 与开洞优先降级
- 实现并导出 DRLGOUTPLACE_PlaceAct1245OutdoorBorders(保留 layBorder 别名),先顺时针放置四边直边界再盖转角封口,保证 100% 覆盖率与 0 过程回退墙
- 支持 claimedBorderBlocks 城镇过渡区与外立面避让接驳
- 新增 tests/wilderness-borders.test.ts 完整测试全 5 幕边界识别、开闭门洞选择、外圈覆盖与接驳避让

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 02:51:30 +00:00
troytt 939ada6e4c feat(wilderness): 用基于控制顶点图 (DRLGVER) 的 Catmull-Rom 样条道路取代栅格化 A* 道路 (#66)
- 实现控制顶点图算法 createDrlgVertices / DRLGVER_CreateVertices,提取城门/关卡出口、中央路口枢纽、特异预设(石块荒野巨石阵、遗忘之塔、邪恶洞窟等)及自然拐点宏控制顶点
- 引入 Catmull-Rom 样条平滑插值算法 catmullRomPoint 与 sampleCatmullRomSpline,生成平滑曲率的自然蜿蜒小径,摆脱死板的 45°/90° 网格折线
- 实现栅格化样条小径 rasterizeDirtPathSegment / generateDirtPaths(2-3 格宽度),无缝对齐 8 邻域 LUT 贴图码表 (DIRT_PATH_TILE_LUT)
- 样条道路与光栅化避障支持:严格避开 roadObstacleBlocks(墓园围墙、遗迹废墟、房屋边界),保护预设建筑阻隔完整性
- 重构 stampThemedRoads:南北向与东西向主道、城门引道及景观支路均通过平滑样条连通,清理道路中轴覆盖的杂物地块
- 新增 tests/wilderness-spline-roads.test.ts,全量覆盖样条数学与曲率、控制顶点图生成、避障光栅化及第一幕野外端到端验证

Closes #66
TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 02:32:14 +00:00
troytt 11e8a39e4e fix(render): 还原原版暗黑2建筑与城门屋顶过道遮挡消失机制 (#72)
- roofs.ts: 将默认最低透明度 DEFAULT_MIN_ROOF_ALPHA 设为 0.0,还原原版遮挡完全隐形机制
- roofs.ts: getRoofBounds 引入 roofHeight 等距高程投影补偿,将悬空屋顶包围盒延伸至地面投影视野
- roofs.ts: 将 DEFAULT_CLUSTER_CELL_DISTANCE 增至 4,并在 clusterRoofs 中自动插值补全城门与建筑群间隙走廊网格单元
- act-scene.ts & d2map.ts: 在 Drawable 和 IsoDraw 中接线传递 DT1 roofHeight 高程属性
- tests/roofs.test.ts: 补充高程下投、哈洛加斯西门跨过道聚类遮挡及拱梁/上墙全消失测试

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 02:12:37 +00:00
troytt d0efb04395 refactor(wilderness): 移除凭空发明的程序化十字矮石墙网络,回归原版自然地貌阻隔 (#65)
- 将 stampThemedDividers 重构为兼容性 no-op,完全废弃人为硬编码的十字切分矮石墙
- generateWilderness 移除 stampOutdoorDividers 调用,回归原版 Act 1-5 依据 DS1 预设边界及地形生成的自然阻隔机制
- 更新 wilderness 单元与多幕道路测试,验证 0 人造矮石墙及真实地貌阻隔保留

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 01:38:00 +00:00
troytt a7e526da64 Merge branch 'fix/issue-63'
chore(engine): eliminate duplicated truth sources, dead code, and no-op rules (Closes #63)

- 修复 5 处逻辑恒真/恒假与参数缺陷:
  * wilderness.ts: isRoad 越界检测修正为返回 false,避免地图外虚假道路连接
  * maze-objects.ts: 货架房间筛选严格优先 degree === 2 走廊,仅在无走廊时回退
  * monster-rooms.ts: 纯安全区(如城镇)candidateRooms 为空时直接返回,禁止向城镇刷怪
  * monster-rooms.ts: 修复默认等距单元格高度为 40px (80x40 瓦片几何)
  * acts.ts: parseTable 空数据严格抛出 Error;补充 temptile.dt1 的 provenance 注释
- 统一真相源并消除重复硬编码:
  * wilderness.ts: 提取 BLOOD_MOOR_RIVER_PRESETS / BLOOD_MOOR_POND_PRESETS,统一 SPECIAL_PRESETS_BY_LEVEL[2]
  * combat.ts: 移除重复的局部 PACK_RADIUS_PX (96),统一导入 monster-rooms.ts (64)
  * wilderness-acts.ts: 将字面量 * 5 统一替换为 * SUB_TILES_PER_TILE
- 接线未覆盖规则与修剪死逻辑:
  * monsters.ts: 支持 mon1..mon25 动态列扫描,防 mod 阶段静默截断
  * monsters.ts: planMonsterGroups 依据 kind.rarity 进行加权选型
  * monsters.ts: applyEliteModifiers 为 stoneskin, cursed, coldenchant, fireenchant, lightenchant, spectralhit, teleport, magicresistant, aura 接线实装数值机制

Closes #63
TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 16:04:34 +00:00
troytt 83f855597b fix(monsters): eliminate unwired columns, dead logic, and wire elite modifier scaling
- Support up to mon25 columns in monsterColumns stopping if column does not exist
- Weight normal pack type selection by kind.rarity in planMonsterGroups
- Wire mechanical stat scaling for stoneskin, cursed, coldenchant, fireenchant, lightenchant, spectralhit, teleport, magicresistant, and aura in applyEliteModifiers
- Add unit tests for monsterColumns, rarity-weighted pack planning, and elite modifier stat scaling

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 15:56:33 +00:00
troytt bd7486cc99 refactor(engine): eliminate duplicated presets and constants (single source of truth)
- Define BLOOD_MOOR_RIVER_PRESETS and BLOOD_MOOR_POND_PRESETS to make SPECIAL_PRESETS_BY_LEVEL[2] canonical and eliminate inline duplicates
- Import PACK_RADIUS_PX from monster-rooms.ts in combat.ts to establish a single source of truth
- Replace literal * 5 with * SUB_TILES_PER_TILE in wilderness-acts.ts

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 15:50:24 +00:00
troytt cbcf71a731 fix(engine): fix logical tautologies, edge conditions, and argument defaults
- Fix isRoad out-of-bounds boundary check to return false instead of true
- Fix rack candidate room selection to strictly prioritize corridor rooms (degree === 2)
- Fix candidateRooms fallback in allocatePacksToRooms to not place monsters in safe zones
- Fix default cellHeight in partitionMapIntoRooms to 40 px for 80x40 isometric tiles
- Throw explicit error in parseTable on empty table data and add provenance comment for temptile.dt1
- Add regression tests for rack candidate selection, safe zones, cellHeight, and parseTable

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 15:46:49 +00:00
troytt c377e8ea01 Merge branch 'fix/issue-62'
refactor(map): 处理凭空发明的程序化地形 pass、消除破坏性预设涂抹与增加种子多样性

1. 删除 wilderness-acts.ts 中从未被主动调用的死代码 generateAct3Jungle 及相关孤儿引入
2. 消除 stampThemedRoads 铺路时破坏性清空墙体(walls.length = 0)与 stampKurastCauseway 覆盖已铺设 Act 3 - Bridge 预设的缺陷,引入预设区域与既有瓦片保护
3. 在 WildernessTelemetry / WildernessResult.stats 中完整追踪程序化道路、分隔墙、水体与障碍物实际绘制瓦片数,杜绝静态假数据
4. 改造血腥丘陵障碍物静态坐标矩阵,接入关卡 PRNG 流实现位置微扰与组件随机化,打破地图固化并保持中央通道畅通

验证:typecheck 零错误;vitest 61 files / 1003 tests passed(基线 61 / 996,新增 7 个测试全绿,测试总数破千);verify-packs 1671 项断言全过。

Closes #62

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 15:32:55 +00:00
troytt 6de9fce610 fix(wilderness): add seed diversity to Bloody Foothills obstacle placement
- Apply RNG jitter to obstacle placement coordinates and lengths across frontline zones
- Preserve the unblocked central corridor along y=24
- Track procedural walls and roads in siege topography stats
- Add unit test in tests/wilderness-siege-strips.test.ts verifying seed-based obstacle variance

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 15:25:45 +00:00
troytt 53d6cdd08c feat(wilderness): track procedural passes in telemetry stats
- Add proceduralRoadCells, proceduralDividerCells, proceduralWaterCells, proceduralWallCells to WildernessTelemetry and WildernessStats
- Measure actual painted cells in stampThemedRoads, stampThemedDividers, generateDirtPaths, and act outdoor level topography passes
- Add unit tests in tests/wilderness-telemetry.test.ts verifying counted telemetry against canvas state

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 15:23:31 +00:00
troytt fa307bff46 fix(wilderness): prevent destructive wiping of walls and presets in roads and bridges
1. Protect placed presets and preset walls from being wiped in stampThemedRoads:
   check placedRects in paintRoadTile, ensuring preset walls are never cleared.
2. Protect preset floors and walls in stampAct3KurastCauseway so artist DS1
   tiles are not overwritten by procedural causeway passes; wire specialPlacedRects.
3. Protect placed preset walls and floors in synthesizeBridgePlanks in Act 3 jungles,
   recording placedRects for all jungle pieces.
4. Add unit tests for Causeway preset preservation and road preset wall preservation.

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 15:10:59 +00:00
troytt 37dc936169 refactor(wilderness): delete dead generateAct3Jungle pass
Remove unused generateAct3Jungle function and its import. Active jungle
generation uses DRLG_GenerateJungles.

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 14:52:23 +00:00
troytt 710efa0a71 Merge branch 'fix/issue-61'
refactor(map): 用 LvlPrest/Levels/Objects 表取代手写地图数据

1. 消除 SUB_TILES_PER_TILE 四处重复定义,统一由 map.ts 权威导出;重构 wilderness-siege.ts 假定高度与步长为动态画布与 DS1 几何
2. 关卡中文名支持从 string.tbl 动态解码(decodeLevelNamesFromTbl),建立覆盖名/动态解/静态表/英文键名四级优雅回退
3. 解耦模块循环加载拓扑,在 acts.ts 统一由 Levels.txt 动态驱动 hasOutdoorWaypoint 与 actOfLevel 并向野外生成全管线贯通
4. 建立 Objects.txt 与 DS1 Type 2 对象池(223 个地牢对象与 82 个野外对象)的映射校验;详尽文档化 Levels->LvlTypes->LvlPrest 三级查找机制并支持动态族系发现

验证:typecheck 零错误;vitest 61 files / 996 tests passed(基线 61 / 979,新增 17 个专项测试全绿);verify-packs 1671 项断言全过。

Closes #61

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 14:44:45 +00:00
troytt a7a3ce13d4 feat(game): document and validate object pools and LvlPrest piece families (Issue #61 Step 4)
- Document the 3-tier lookup relation across Levels.txt, LvlTypes.txt, and LvlPrest.txt
- Explain why Catacombs/Sewers index pieces via Def / LevelId = 0 / first levelId
- Explain why composite themes have sub-families (Corrupt Harem, Mephisto, Temple, Bridge, ThroneRoom)
- Implement discoverMazePieceFamilies with optional LvlPrest table and update classifyMazePieceName
- Add Objects.txt mapping validation (validateDungeonObjectPools, validateActPopulationConfig, resolveObjectsTxtId)
- Add comprehensive unit tests in tests/maze-objects.test.ts

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 14:39:13 +00:00
troytt 94305b03dd Resolve outdoor waypoints and act mapping dynamically from Levels.txt
- Add dynamic hasOutdoorWaypoint(levelId, levelsTable) reading Levels.txt Waypoint column (!= 255)
- Unify actOfLevel in acts.ts as canonical source of truth supporting Levels.txt Act column
- Support levelsTable in WildernessRequest, spawnOutdoorWaypoint, spawnOutdoorShrines, stampOutdoorRoads, stampOutdoorDividers
- Re-export actOfLevel, hasOutdoorWaypoint, and OUTDOOR_WAYPOINT_LEVELS cleanly without circular dependencies
- Add unit tests verifying dynamic Levels.txt resolution and fallbacks

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 14:27:54 +00:00
troytt 7efdad4693 feat(game): decode level names from string.tbl with graceful fallback
- Implement decodeLevelNamesFromTbl supporting D2Tbl/Map and Levels.txt
- Add CANONICAL_LEVEL_NAME_KEYS and active decoded level names cache
- Support graceful fallback to LEVEL_NAMES_ZH and English name keys
- Add comprehensive test coverage in tests/tbl.test.ts

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 14:14:15 +00:00
troytt 8ee3a4c971 fix(game): import canonical SUB_TILES_PER_TILE and use dynamic bounds/stride in siege
- Import canonical SUB_TILES_PER_TILE from map.ts in maze-objects.ts and level-links.ts
- Replace hardcoded y < 6 || y > 42 bounds with dynamic canvas.height - 6 bounds in wilderness-siege.ts
- Replace static i * 16 stride assumption with dynamic ds1.width and canvas.width stride

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 14:09:31 +00:00
troytt 955e9edbb1 Merge branch 'fix/issue-60'
fix(world): 消除不可达关卡、编造的楼梯位置与传送无限弹射

1. 修复 level-links.ts seamArrivalSpot 传送无限弹射死循环:强制全图搜索并向接缝内侧偏移,彻底阻断在触发器边缘来回反弹
2. 统一全仓库城镇 ID 与 townLevelForAct 映射逻辑至 acts.ts 单一真相源,消除 portal.ts、pack-act-assets.ts 等多处硬编码与分幕边界歧义
3. 替换 maze-special-passes.ts 中 12 处伪随机 seed % N 为推进的 PRNG 流,修复第 5 幕神殿 NE 路径点无法生成的数学 Bug
4. 重构 getPerimeterOpenings 消除非第 1 幕户外关卡生成完全封闭石墙的问题,对内部预设与墓园放置失败提供显式遥测追踪
5. 对打包期 source: 'fallback' 楼梯在 verify-packs.ts 建立严格数量上限断言(<= 80)并在 scene.json notes 中暴露来源追踪

验证:typecheck 零错误;vitest 61 files / 979 tests passed(基线 61 / 968,新增 11 个测试全绿);verify-packs 1671 项断言全过。

Closes #60

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 14:00:10 +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 fea677b0b4 fix(wilderness): prevent sealed borders on outdoor levels and track preset failures
TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 13:43:51 +00:00
troytt b8810072ce fix(maze): use PRNG stream in special passes and fix Act 5 temple NE waypoint generation
TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 13:36:02 +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 27e0db38c8 fix(level-links): prevent infinite teleport bounce at seam arrival
Eliminate fallback to trigger tile (x, y) when no walkable tile is found
within initial radius. Expand search to full grid extent and guarantee that
the arrival destination is strictly offset inward away from the seam trigger box.

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 13:27:27 +00:00
troytt 5f484e5921 Merge branch 'fix/issue-59'
refactor(monsters): 用 MPQ 表取代手写怪物数据、清理无效 token 与消除红方块

1. 将超级独特怪与随从 ID 注入 PlannedLevel.types 与 act-scene 预加载,根治 Boss 生成时变成红方块的问题
2. 更新 hellbovine token 为原版全套动作目录 EC(原 CW 仅为 6 个残存桩),清理 18 个开发期 "XX" token
3. 消除 resolveMonsterArtSpec 中按字典序迭代的 startsWith 模糊猜测,改为确定性 O(1) 查找
4. 用 buildSuperUniqueLandmarkSpecs / lookupSuperUnique 取代 344 行手写 CANONICAL_SUPER_UNIQUES_BY_LEVEL,彻底消除 fallen1 默认兜底降级
5. 接入 MonUMod.txt 词缀常量与 MonStats2.MeleeRng 驱动的近战攻击范围计算

验证:typecheck 零错误;vitest 61 files / 968 tests passed(基线 61 / 954,新增 14 个测试全绿)。
verify:generators 与 origin/main 逐位一致(19 条既有失败未变)。

Closes #59

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 13:13:28 +00:00
troytt bf101dae66 fix(monsters): wire meleeRange from MonsterArt into reach and reference MonUMod constants
TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 13:03:21 +00:00
troytt fd0f48c391 fix(monsters): derive superunique specs from SuperUniques table and eliminate fallen1 fallback
- Replace static CANONICAL_SUPER_UNIQUES_BY_LEVEL mapping with buildSuperUniqueLandmarkSpecs
- Parse canonical SuperUniques from game data table CANONICAL_SUPERUNIQUES_TABLE
- Export lookupSuperUnique helper to search SuperUniques by id or nameKey
- Support optional tables.superuniques in planLevelMonsters
- Remove dangerous fallback to fallen1/first monster, gracefully skipping missing boss kinds

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 12:54:09 +00:00
troytt 2e73d7b02f fix(monster-mapping): update hellbovine token to EC, remove XX tokens, and eliminate startsWith fallback
- Update hellbovine token from CW to EC in MONSTER_ART_MAP
- Remove invalid XX cut monster entries from MONSTER_ART_MAP and ACT_MONSTER_SPECS
- Replace startsWith iteration in resolveMonsterArtSpec with deterministic O(1) exact and base lookup
- Prevent XX tokens from leaking into art requests

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 12:42:44 +00:00
troytt 04d562a99c fix(monsters): include superunique and minion monster IDs in PlannedLevel types
Ensure monster art is loaded for all superunique bosses and their
minions to prevent red placeholder boxes when they spawn.

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-17 12:36:42 +00:00