Commit Graph

187 Commits

Author SHA1 Message Date
troytt ef9380864c fix(tiles): 修复 Kurast / Mesa / Desert / Barricade 的瓦片引用缺失 (Issue #83)
- wilderness.ts: 在 applyGroundTransitions 中增加 options.levelTypeName 守卫,避免对非 Act 1 关卡执行 Act 1 地面与水岸过渡,消除 Desert (43) 与 Barricade (112, 117) 的无效瓦片生成。
- wilderness-acts.ts:
  - Kurast (79, 80, 81): 大街、桥梁与堤岸 balustrade 改用 Kurast 实际存在的 style 20 (Floors.dt1 / Walls.dt1),消除不存在的 style 5 / style 4 引用。
  - Mesa (104, 105, 106): 峡谷地面根据 levelTypeName 区分,Act 4 - Mesa 使用 Mesa Floor.dt1 的 style 10 峡谷地表,消除仅在 Lava 存在的 style 20 引用。
- wilderness-siege.ts:
  - Siege (110): 攻城木桩栅栏与破损立柱改用 fence.dt1 存在的 style 2 sequence 10 (type 1 与 type 7);投石台墙壁使用 palisade wall;废墟使用 camp.dt1 的 style 18 地砖与墙壁。

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 11:27:14 +00:00
troytt 6a59edd0cb feat(items): 加载 Armor.txt (Issue #91) 2026-09-18 11:25:14 +00:00
troytt 193c1209b2 feat(items): 加载 Armor.txt (Issue #91) 2026-09-18 11:24:47 +00:00
troytt 9896ada25e docs(audit): 完成 DRLGOUTDESR 与 DRLGOUTJUNG 子系统对照审计与源码溯源标注 (Issue #86)
TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 11:17:51 +00:00
troytt 7187154ac8 feat(wilderness): 还原 DRLGOUTDOORS_SpawnAct12Waypoint 原版 8x8 单元网格打乱与有效性判定 (Issue #80)
- 还原 1.13c 原版 DRLGOUTDOORS_SpawnAct12Waypoint (D2Common.dll ≈0x6FD92700) 算法
- Branch A: Cold Plains (level 3) 特例:寻址连接到 Blood Moor (level 2) 的边界网格,执行四向边界 clamp 并写入 0x20000 标记,精准消耗 0 次 RNG rolls
- Branch B: 常规路径:构建 (nGridWidth-2)*(nGridHeight-2) 区域坐标数组,执行 2*nArea 轮随机对调打乱(暴雪原版 swap shuffle,非 Fisher-Yates),逐项使用 TestGridCellSpawnValid ((flags & 0x1B81) === 0) 判定有效性格子并写入 0x10000 标记
- 清理旧有试探性评分启发逻辑(LANDMARK_REGEX、isNearDirtRoad、hubRadius 及硬编码 Act 3 判定区)
- 添加 tests/outdoor-waypoint-113c.test.ts 单元测试并对齐已有测试用例

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
2026-09-18 11:15:41 +00:00
troytt a1a549e8a0 feat(items): 加载 ItemStatCost.txt 与 Properties.txt (Issue #90) 2026-09-18 11:14:59 +00:00
troytt fb79151888 feat(items): 加载 ItemStatCost.txt 与 Properties.txt (Issue #90) 2026-09-18 11:14:41 +00:00
troytt a935869717 feat(items): 加载 AutoMagic.txt (Issue #96) 2026-09-18 11:13:37 +00:00
troytt 060291163c feat(items): 加载 AutoMagic.txt (Issue #96) 2026-09-18 11:13:14 +00:00
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