refactor(map): 处理凭空发明的程序化地形 pass(覆盖真实 DS1 预设) #62
Labels
No Label
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: troytt/diablo2-web#62
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?
背景
对
main分支(origin/main=4c2e399)的野外生成器做了全量审计。发现:相当一部分「地形生成」pass 不是任何东西的移植 —— 它们是凭空发明的程序化合成,并且会覆盖刚刚贴上去的真实 DS1 预设数据。
这些 pass 的共同特征:
style/sequence/prop1常量1.
stampThemedRoads—— 整套道路系统,会删除墙层src/game/wilderness.ts:3081-3209发明内容:边距 4、受保护 style
2/3/4、northEnd/westEnd = max(12, dim*0.2)、3 格宽主路、+4枢纽偏移、2×2 支路笔刷、门夹取r.y+2 … r.y+r.h-3。最危险的一处:
wilderness.ts:3103-3110道路中心线穿过墙时,删除该格全部墙层 —— 会摧毁恰好位于中心线上的边界墙与预设墙。
保护规则也是名字正则
wilderness.ts:3081-3091/:3191-3194:theme.isProtectedPreset ? … : /River|Bridge/i.test(r.name)任何名字不含 River/Bridge/Fill/Berms 的预设都会被道路覆盖。
2.
stampThemedDividers—— 石墙 / 护堤 / 栏杆网络src/game/wilderness.ts:3226-3344发明内容:
wallProp1 ?? 129、±2 block 偏移、夹取 12、边距 6、预设边距 2、牧场门x % 24 ∈ [11,13]、最短连续段 4、墙type1/2/3 与sequence0/1/2。写入的
type/sequence未经任何 DT1 验证。3. 第二幕:沙路 + 峡谷山脊 + 绿洲
src/game/wilderness-acts.ts:179-281:179-208style 5、seq r.int(0,4)===0?25:0、prop1 194、长度为画布0.1…0.9:211-24610 … w-10、缺口|x-hubX| <= 3、墙style 1 type 2 prop1 129:249-281(0.35,0.35) r=min(6,w/12)与(0.68,0.68) r=min(5,w/14),水style 2绿洲尤其可疑:
LvlPrest本来就有Act 2 - Desert Oasis 1/2/3预设,代码也确实在 0.4/0.4 贴了它们 —— 然后又额外画了两个程序化水池。3a. 迷城之谷七墓坐标全手打
wilderness-acts.ts:296-377:7 座墓在0.22/0.5/0.78与0.35/0.68比例位置、x=1、w-12;piece 名字'Act 2 - Desert Cliff {Top,Left,Right} King Tomb';物件 id66 + i;子格换算用字面* 5而非SUB_TILES_PER_TILE;warp 物件 id74。4. 第三幕:库拉斯特运河 —— 82 关被完全重绘
src/game/wilderness-acts.ts:549-573stampKurastCauseway遍历关卡每一个格子,重绘 8 格宽居中步道 + 栏杆(style 1 prop1 129)+ 运河(style 2)。它覆盖的正是 L544 刚刚贴上的
Act 3 - BridgeDS1 预设。:613-660的林荫道/运河同理:边距 6、canalX = clamp((inX+2)*8, 12, w-16)、4 格宽运河、|y-hubY| <= 2处架桥、style 5/4/2/1、prop1 194/2/129。4a. 丛林桥梁用原始地板瓦片画,而非贴
Act 3 - Bridgesrc/game/wilderness-jungles.ts:240-283:synthesizeBridgePlanks画 4 格带dy 14..17/dx 14..17、跨度 32、style 4 prop1 2。:590-609还有一次无条件强制重贴:在[78, 142] × x 32..60画 style-4 地板,无视刚刚算出来的宏观网格。这些坐标与死代码generateAct3Jungle(wilderness-acts.ts:417-528,import 了但从未调用)重复。4b. 「大沼泽跳过」是抛硬币
wilderness-jungles.ts:96-101:rng.int(0,3) !== 0(75% 跳过)+rng.chance(0.5)死路。原版第三幕的连通性来自
Levels.txt的 Vis/Warp,不是概率。5. 第四幕:熔岩裂谷是字面上的正弦波
src/game/wilderness-acts.ts:700-767桥在
0.3 / hubX / 0.7;桥宽 ±3、裂谷 ±2;岩石style 10、熔岩style 20 seq 3;Izual 台地在枢纽处 10×10。5a. 「台地」循环什么都不画
wilderness-acts.ts:768-777:循环体只递增plateauCells计数器,不写入任何格子 —— 纯粹的伪造遥测。6. 第五幕:路障 / 壕沟 / 湖泊 + 血腥丘陵手绘地图
6a. 要塞布局按画布比例
src/game/wilderness-acts.ts:817-898:路障在gridWidth*0.35 / 0.7、门在hubY ± 2、壕沟在bx - 2宽 2、湖在(0.52,0.48) r=min(8,w/8)style 2。6b. 血腥丘陵是固定手绘地图
src/game/wilderness-siege.ts:402-48319 次障碍调用,约 60 个字面坐标:
(18,8,10)(50,28,10)(34,12,7)(26,10,24,21,5)(42,8,12,13,3)(68,26,12,31,3)(12,10,4,4)(58,30,4,4)(30,30,4)(64,10,4)(98,10,10)(138,28,10)(114,10,24,21,5)(146,8,12,13,3)(86,30,4,4)(124,10,4)(186,12,10)(202,10,24,21,5)(170,30,4)除瓦片 sequence 抖动外,每个种子生成的地图完全相同。 原版血腥丘陵是预设驱动的。
配套的景物 DT1 索引同样发明(
wilderness-siege.ts:101-306):壕沟style 5 seq r.int(0,5) prop1 194宽 2;路障style 2 seq 0/r.int(1,2) prop1 129 type 1;投石机地板style 4 seq r.int(0,3) prop1 2;废墟style 1 seq r.int(0,2) prop1 2+ 墙style 1 seq r.int(0,4) prop1 129、rng.next() > 0.35衰变率。7. 「地面瓦片来自边界 DS1 站立处的统计」
src/game/wilderness.ts:955-1011deriveGroundTiles(pieces)—— 当既无groundTile(s)、又无 DT1 库、又无 canonical 条目时,通过统计边界 DS1 内部(排除外圈)踩在什么瓦片上来反推地面。文档自述这是「可观测的代理」(the observable proxy)。它是一个猜测,且会随 level type 漂移。
7a. 上游的 mask 回退让这条路径经常被触发
wilderness.ts:153-155:WILDERNESS_DT1_MASK[levelTypeName] ?? 0—— level type 名字不在 8 条硬编码里就得到 mask0,deriveGroundTilesFromDt1Libraries返回[],直接掉进合成路径。8.
prop1: 2与文档不符wilderness.ts:719 / 740 / 774 / 831 / 872-877在每个写入的地面格写prop1: 2。但同一文件头部 L69-73 的注释说 D2MOO 写的是打包后的
0x40002(bIsFloor+ wall-layer bit)—— 说了然后没做。9. 中心十字路口假设
wilderness.ts:5205-5206:inX = floor(gridWidth/2)、outY = floor(gridHeight/2)。每个关卡都得到一个完美居中的十字路口,所有幕级地形 pass 都以此为锚。
讨论:应该怎么办?
这些 pass 显然是为了让关卡「看起来不空」而写的权宜之计。但它们的代价是:
junglePathCells: 100建议分两步:
stats标记,让「这一关的地形有多少比例是合成的」可观测;修掉walls.length = 0这类破坏性行为;修���伪造遥测。完整清单
stampThemedRoadswilderness.ts:3081-3209stampThemedDividerswilderness.ts:3226-3344deriveGroundTiles(piece 统计反推)wilderness.ts:955-1011wilderness-acts.ts:179-208wilderness-acts.ts:211-246wilderness-acts.ts:249-281wilderness-acts.ts:296-377wilderness-acts.ts:549-573wilderness-acts.ts:613-660wilderness-jungles.ts:240-283:590-609wilderness-jungles.ts:96-101wilderness-acts.ts:700-767wilderness-acts.ts:768-777wilderness-acts.ts:817-898wilderness-siege.ts:402-483generateAct3Junglewilderness-acts.ts:417-528验收标准
wilderness.ts:3103-3110的walls.length = 0破坏性行为移除或严格限界stats中标记其覆盖的格子数,可观测junglePathCells: 100等伪造遥测改为真实计数generateAct3Jungle删除prop1改为文档所述的0x40002npm run typecheck零错误,npx vitest run全绿TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
父追踪 Issue:#64(完整审计报告与修复路线图)
✅ Issue #62 修复与验证完成 (Closes #62)
变更总结
generateAct3Jungle:wilderness-acts.ts中未使用的generateAct3Jungle及其重复硬编码桥梁坐标 (bridgeYPositions = [78, 142])。wilderness.ts:stampThemedRoads在雕刻道路时不再使用walls.length = 0暴力清空预设墙体,仅清理程序化 divider 墙。wilderness-acts.ts:stampKurastCauseway与synthesizeBridgePlanks在铺设桥梁路面时检测单元格是否已有放置的Act 3 - BridgeDS1 预设,优先保留真实 DS1 瓦片。WildernessStats/WildernessTelemetry,新增proceduralRoadCells、proceduralDividerCells、proceduralWaterCells、proceduralWallCells,如实上报实测涂刷格数。tests/wilderness-telemetry.test.ts专项断言可观测性。wilderness-siege.ts的 19 处障碍物放置逻辑,基于关卡 PRNG stream 进行安全范围内的动态抖动与变体选取,保留中央进攻通道通畅的同时消除全种子固定死板布局。tests/wilderness-siege-strips.test.ts中验证多种子布局差异性。独立复验记录 (Merged
c377e8e)npm run typecheck: 0 错误npx vitest run: 61 passed, 1003 passed, 2 skipped (全绿)npx tsx scripts/verify-packs.ts: 1671/1671 assertions passed/usr/local/google/home/taodao/d2w-issue-62与分支fix/issue-62已清理完毕。TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1