Go to file
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
.gitea/workflows test: 引入 Vitest 测试框架、覆盖率统计与 CI 流水线 (fixes #14) 2026-09-14 12:25:28 +00:00
docs feat(drop): embed MonStats and SuperUniques with 1.13c multi-diff TC parity (Fixes #410, #411) 2026-09-25 13:44:35 +00:00
public fix(ui): restore Horadric Cube Transmute and Close buttons per D2Client.dll 1.13c parity 2026-09-27 08:36:19 +00:00
samples/fixtures feat(skills): authentic 1.13c Cast Overlay system and elemental spell animations (Fixes #386) 2026-09-23 03:53:57 +00:00
scripts feat(drlg): generate Act I outdoor levels with the 1:1 D2MOO port (#455) 2026-09-27 15:20:41 +00:00
src feat(drlg): generate Act I outdoor levels with the 1:1 D2MOO port (#455) 2026-09-27 15:20:41 +00:00
tests feat(drlg): generate Act I outdoor levels with the 1:1 D2MOO port (#455) 2026-09-27 15:20:41 +00:00
tools feat(drlg): generate Act I outdoor levels with the 1:1 D2MOO port (#455) 2026-09-27 15:20:41 +00:00
.gitignore chore(m5): stabilize full-suite test timeouts and track project/test docs 2026-09-22 04:20:32 +00:00
AGENTS.md docs(agents): require isolated git worktree for all changes, merge to main, then clean up 2026-09-24 06:07:28 +00:00
PROJECT.md docs(m5): mark M5 milestone DONE in PROJECT.md 2026-09-22 04:27:03 +00:00
README.md docs: update README with art assets, JSON data specifications, and UI parity 2026-09-22 13:12:25 +00:00
ROADMAP.md chore: 移除 index.html、net.html、walk.html 历史原型入口并优化根路径路由 2026-09-18 12:04:07 +00:00
TEST_INFRA.md feat(skills): complete 1.13c D2MOO parity for Assassin and Universal skills (#154-#374) 2026-09-25 22:13:38 +00:00
TEST_READY.md feat(skills): complete 1.13c D2MOO parity for Assassin and Universal skills (#154-#374) 2026-09-25 22:13:38 +00:00
THIRD_PARTY_NOTICES.md 初始提交:从零实现的暗黑破坏神 II WebGL2 引擎(TypeScript + Vite,零运行时依赖) 2026-09-13 18:10:28 +08:00
acts.html feat(debug): add monster density and elite chance selectors to acts.html (Fixes #450) 2026-09-24 14:50:27 +00:00
arena.html chore: 移除 index.html、net.html、walk.html 历史原型入口并优化根路径路由 2026-09-18 12:04:07 +00:00
boss.html feat(ui/controls): 实现完整 UI/HUD 系统、操作快捷键对齐与装备词条染色 (fixes #134, fixes #135, fixes #136, fixes #137, fixes #138, fixes #139, fixes #140) 2026-09-21 11:09:10 +00:00
loot.html feat(boss): 新建幕Boss击杀模拟与真实属性掉落页面 (boss.html) 2026-09-19 03:42:32 +00:00
package-lock.json chore(scripts): 使用 tsx 运行 TypeScript 脚本,解除对特定 Node 运行时的依赖 (fixes #12) 2026-09-14 11:44:43 +00:00
package.json test(drop): end-to-end deterministic drop tests and headless browser audit (Fixes #408) 2026-09-24 11:13:35 +00:00
skills.html feat(ui/controls): 实现完整 UI/HUD 系统、操作快捷键对齐与装备词条染色 (fixes #134, fixes #135, fixes #136, fixes #137, fixes #138, fixes #139, fixes #140) 2026-09-21 11:09:10 +00:00
tsconfig.json feat(drlg): generate Act I outdoor levels with the 1:1 D2MOO port (#455) 2026-09-27 15:20:41 +00:00
vite.config.ts feat(ui/controls): 实现完整 UI/HUD 系统、操作快捷键对齐与装备词条染色 (fixes #134, fixes #135, fixes #136, fixes #137, fixes #138, fixes #139, fixes #140) 2026-09-21 11:09:10 +00:00
vitest.config.ts test: 引入 Vitest 测试框架、覆盖率统计与 CI 流水线 (fixes #14) 2026-09-14 12:25:28 +00:00

README.md

d2web

从零实现的暗黑破坏神 II 引擎(纯 TypeScript + Vite + WebGL2,零运行时依赖),在浏览器里读取 用户自备的经典版 D2 归档(MPQ),渲染等距地图、驱动角色走动、释放 221 个原版技能,并支持把地图与模型离线烘焙成 web 原生格式。

  • 仓库不含任何暴雪资源:没有 .mpq / .dc6 / .dt1 / 图集 PNG;原版数据要自己放进 samples/d2/。
  • 代码可复现一切:格式解码、地图构建、资源包烘焙、角色合成都在这份代码里,脚本可重跑。
  • 路线图、实现细节、验证方法论与未完成项在 ROADMAP.md(README 只做总览与部署使用)。

总览

能力 说明
等距地图渲染 DS1 + DT1 投影(2:1 dimetric,格 80×40 px),地板 / 墙 / 屋顶 三层分别绘制;逐格按 RarityFrameIndex 加权随机选瓦片变体;画家序排序
碰撞系统 每格 5×5 个 sub-tile 标志位(一层内所有层做 OR 合并,含 type 13 影子层),角色按 sub-tile 判定走位,支持沿梯度脱困
真实关卡 已烘焙 365 张地图,覆盖 5 个 act:35 个预置关卡(罗格营地、修道院、大教堂、地下墓穴、鲁高因、库拉斯特码头、哈洛加斯…)+ 70 个随机迷宫 + 31 个野外关卡(生成逻辑移植自 D2MOO,按官方参数表复现)
七大职业与角色合成 完整支持亚马逊(Amazon)、刺客(Assassin)、野蛮人(Barbarian)、德鲁伊(Druid)、死灵法师(Necromancer)、圣骑士(Paladin)、女巫(Sorceress)七大职业的 DCC + COF 16 层组件装配与 8 方向动画,支持游戏中实时切换职业与初始专属装备
全技能系统(221 技能) 210 个各职业技能(每职业 3 系 × 10 技能)与 11 个通用玩家技能(普攻/踢击/投掷/反召唤/辨识/回城等),严格复现 1.13c 公式、协同加成(Synergies)、投射物引擎、光环扫描与延迟机制
完整 UI / HUD 交互 800×600 原版底栏控制台(红蓝血球与中毒覆盖、技能快捷槽、腰带)、左/右宽屏 400px 停靠面板(背包、纸娃娃、属性面板、技能树、储物箱、方块、任务日志、传送点菜单)、动态鼠标指针、Alt 物品高亮等
资源包(离线烘焙) 索引色 PNG 图集页(≤2048²,PLTE + tRNS 取自该 act 的 pal.pl2)+ scene.json(绘制列表、碰撞 RLE、出生点、对象元数据)+ manifest.json
按需读归档 也可不烘焙,直接用 HTTP Range 只拉需要的 MPQ 字节当场解码(?live=1,慢,作为回退路径)
三级关卡选择器 章节 → 场景 → 细分场景(如 第一章 / 罗格营地 / 北);中文场景名见 src/game/level-names-zh.ts

开发进展:当前底层引擎、七大职业表现、221 技能完整实现与全套 UI 面板已全面打通。下一步将深入完善野外怪物密度与 AI 行为、装备词缀掉落平衡与 .d2s 官方存档双向互通,详见 ROADMAP.md。

代码结构

src/mpq/       MPQ v1 容器:hash/block 表、Storm 加密、分扇区编解码、PKWARE implode/explode、Range 读取
src/formats/   dc6 / ds1 / dt1 / pal / pl2 / cel / pcx / tbl / sprite / dcc / cof / bitstream
src/game/      等距场景与碰撞、关卡表解析、DCC+COF 角色合成、对象与物品解析、确定性 RNG
  engine/      1.13c 真实战斗引擎、AST 公式求值器、状态机、投射物调度、光环与召唤物管理
  skills/      221 个技能的具体实现模块(uni / ama / sor / nec / pal / bar / dru / ass)
src/ui/        完整 UI / HUD 模块:背包、属性面板、技能树、控制底栏、传送点、储物箱、任务日志、字体、鼠标指针
src/render/    WebGL2 渲染器(多图集;纹理切换时 flush 以保画家序)
src/scene/     页面级装配(资源包优先,失败回退到直读归档)
src/data/      技能目录、Gitea 跟踪 Issue 数据库、掉落表与任务官方文本数据
scripts/       资源包烘焙、格式/数据/部署校验、无头浏览器检查、夹具生成
*.html         主入口页面:acts.html(游戏全功能主页面)、arena.html(数值与技能验证竞技场)

美术资源与图集资产(Assets)

项目在离线烘焙和客户端运行时划分了清晰的美术资产目录结构,所有视觉素材均可从 1.13c 原版 MPQ 自动导出与打包:

1. UI 界面图集与面板切片 (public/ui/)

  • 综合图集 (Atlases):
    • items-atlas.png:装备与物品大图集,覆盖所有武器、防具、饰品(戒指、护身符、珠宝)、符文、药水等,完整支持大/中/小护身符与首饰的原版 VarInvGfx 多视觉外观变体。
    • quests-atlas.png:1–5 幕全部任务状态图标图集(未触发、进行中、已完成)。
    • skill-tree-tabs.png:七大职业技能树的 3 系专属垂直选项卡图集(激活/未激活切片)。
  • 经典功能面板背景与控件:
    • ctrlpnl-800.png:800×600 原版底栏控制台面板(包含技能槽底框、经验槽、腰带槽)。
    • inv-sheet.png / inv-tab-0.png / inv-tab-1.png:装备纸娃娃界面与 10×4 背包网格、主副手武器切换 Tab。
    • char-sheet.png:角色属性与抗性详情面板。
    • stash-bg.png:营地私人储物箱面板(6×8 格子)。
    • cube-bg.png:赫拉迪克方块(3×4 合成面板)。
    • vendor-bg.png / buysellbtn.png:NPC 商人交易界面与买卖交互按钮。
    • quest-bg.png / quest-tabs.png:羊皮纸任务日志背景与 1–5 幕选项卡。
    • waypoint-bg.png / waypoint-tabs.png / waypoint-icons.png:传送点菜单、各幕选项卡与石碑传送状态图标。
  • 指示器与动态视觉元素:
    • globe-life.png / globe-mana.png / globe-poison.png:经典流体红蓝血球与中毒、法力燃烧覆盖层。
    • cursor.png / cursor-hand.png:硬件感暗黑手套鼠标指针(支持空闲状态机与点击动作动画)。
    • border-left.png / border-right.png / overlap-left.png / overlap-right.png:宽屏自适应下的左右面板停靠石柱边框与过渡阴影。
    • minipanel.png / minipanel-btns.png / popbelt.png / runbutton.png / menubutton.png / level-btn.png / skillpoints.png:菜单微型按钮、升级提示羽毛球、跑步状态切换钮等。
  • 原版位图字体 (public/ui/fonts/):
    • 提供 6 套暗黑 II 经典字体纹理:font6.png、font8.png、font16.png、font30.png、fontexocet10.png、fontformal12.png。

2. 技能树背景与技能图标资产 (public/skills/ 与 public/ui/)

  • 全职业技能树背景切片 (public/skills/bg_<cls>_<tab>.png):
    • 共 21 张 256×432 px 原版复古羊皮纸技能树背景原画,覆盖 7 大职业各自的 3 大技能分支:
      • 亚马逊:bg_ama_0.png(标枪与长矛)、bg_ama_1.png(被动与魔法)、bg_ama_2.png(弓与十字弓)
      • 刺客:bg_ass_0.png(武学艺术)、bg_ass_1.png(影子训练)、bg_ass_2.png(陷阱)
      • 野蛮人:bg_bar_0.png(战吼)、bg_bar_1.png(战斗专家)、bg_bar_2.png(战斗技能)
      • 德鲁伊:bg_dru_0.png(元素)、bg_dru_1.png(变形)、bg_dru_2.png(召唤)
      • 死灵法师:bg_nec_0.png(召唤术)、bg_nec_1.png(毒素与白骨)、bg_nec_2.png(诅咒)
      • 圣骑士:bg_pal_0.png(战斗技能)、bg_pal_1.png(攻击灵气)、bg_pal_2.png(防御灵气)
      • 女巫:bg_sor_0.png(火焰法术)、bg_sor_1.png(闪电法术)、bg_sor_2.png(冰冷法术)
  • 技能图标资产 (public/skills/icon_*.png 与 public/ui/):
    • 包含 221 个技能的 48×48 px 原版技能图标,细分为普通态(icon_<id>.png)与激活/按下高光态(icon_<id>_active.png)。
    • 涵盖七大职业全部 210 个技能,以及 11 个通用技能(普通攻击、踢击、投掷、反召唤、辨识/回城卷轴与书、迪勒瑞姆变身等)。
    • 动态普通攻击图标:根据当前主手装备自动切换拳头图标(icon_0_fist.png)或武器剑图标(icon_0_sword.png),并具备投掷(icon_2.png)与反召唤(icon_3.png)专用图标。

3. 角色与怪物实体动画包 (samples/d2-packs/entities/)

  • 七大职业模型:
    • 亚马逊(char-am)、刺客(char-ai)、野蛮人(char-ba)、德鲁伊(char-dz)、死灵法师(char-ne)、圣骑士(char-pa)、女巫(char-so)。
    • 由 16 个装备图层复合装配,解包出行走、跑动、各种武器攻击、施法、受击、死亡等 8 方向完整序列。
    • 伴随同名 .r8 紧凑二进制像素包,用于在 WebGL/Canvas 中零拷贝极速解码渲染。
  • 怪物实体模型:
    • 包含沉沦魔(Fallen)、骷髅(Skeleton)、僵尸(Zombie)、血鸟(Blood Raven)、督瑞尔(Duriel)、安达利尔(Andariel)、墨菲斯托(Mephisto)、暗黑破坏神(Diablo)、巴尔(Baal)等数十种怪物的完整动画包与 .r8 调色板像素数据。

4. 场景瓦片图集 (samples/d2-packs/act[1-5]/)

  • 365 个地图场景分块切片图集,每个图集包含 ≤2048² 尺寸的索引色 PNG 瓦片页(地板层、墙体层、屋顶层),附带 PLTE 调色板与透明度表。

结构化数据与 JSON 规范(Data & JSON)

引擎采用结构化 JSON 数据来驱动界面排版、图集映射、场景还原与游戏逻辑:

1. public/ui/manifest.json(UI 资源与图集总清单)

由 scripts/pack-ui.ts 自动烘焙生成,为 UI 系统提供像素级定位映射:

  • atlasWidth / atlasHeight:items-atlas.png 装备图集的分辨率规格。
  • questsAtlasWidth / questsAtlasHeight:quests-atlas.png 任务图集的分辨率规格。
  • codeToInvFile:物品 3 字母代码(如 rin、amu、cm1、hpot)到原版 MPQ 图像文件名的映射表。
  • itemRects:所有物品及装备的 UV 矩形坐标表({ x, y, w, h }),针对戒指、项链、珠宝和护身符支持 VarInvGfx 多图集变体定位。
  • questRects:1–5 幕任务日志在图集中的坐标切片。
  • images:各种独立 UI 面板背景、边框、按键在 public/ui/ 下的文件索引。

2. public/ui/fonts/metrics.json(位图字体排版度量)

  • 记录 6 套经典字体(Exocet、Formal、Font6/8/16/30)的字形度量参数。
  • 包含每个 ASCII 字符在字体图集上的矩形包围盒、字宽(charWidths)、字符高度、基线偏移与字间距(Kerning),为游戏内悬浮 Tooltip、装备描述和任务日志提供完全原版的排版效果。

3. samples/d2-packs/index.json(全关卡全局场景目录)

  • 记录全游戏 5 幕 365 个地图块的元数据。
  • 包含字段:章节编号(Act 1..5)、关卡 ID(Level ID)、内部场景标识、关卡类型(预置场景 Preset / 随机迷宫 Maze / 野外关卡 Wilderness)、网格尺寸(Grid Bounds)、可用变体数(Variants)与对应的烘焙目录相对路径。

4. samples/d2-packs/world-graph.json(世界拓扑连通网络图)

  • 从 Levels.txt 的连接参数(Vis0..7、Warp0..7)生成的全局世界拓扑图。
  • 记录场景之间的连通边(Edges)、传送点(Waypoints)分布、楼梯出入口(Warps)、野外边界开放开口(Perimeter Openings),支撑小地图与跨关卡传送切换。

5. samples/d2-packs/anim/animdata.json(动画关键帧事件权威表)

由 scripts/pack-animdata.ts 直接解析 1.13c AnimData.d2 二进制文件(570,304 字节,3,558 条记录)生成:

  • 记录每个动作 Hash 的播放帧率(Frame Speed)、总帧数(Total Frames)。
  • 精确解析动作事件标志位:0x01 判定为近战命中/施法触发判定帧,0x02 判定为投射物(Missile)发射事件帧,确保 25Hz 动作逻辑与原版单机完全一致。

6. samples/d2-packs/anim/monstats2.json(怪物组件与动画模式配置)

  • 结构化提取自 MonStats2.txt。
  • 记录所有怪物的身体构成组件、图层合成规则、各武器模式下的动作代码映射(如 hth、1hs、2ht、bow)。

7. samples/d2-packs/entities/char-*.json 与 monster-*.json(实体模型定义)

  • 描述 7 大角色职业与数十种怪物的骨骼装配结构。
  • 包含复合图层映射、8 方向动作切片元数据、每帧中心偏移(Pivot),搭配同名 .r8 文件在渲染管线中执行高效逐像素着色。

8. samples/d2-packs/act<N>/<level>/scene.json & manifest.json(单场景规格)

  • manifest.json:当前场景所需的瓦片图集页清单与调色板信息。
  • scene.json:记录等距地图的地板(Floor)、墙体(Wall)、屋顶(Roof)多层瓦片绘制列表与深度排序,采用 RLE 压缩的 5×5 子格碰撞阻挡网格(Collision Grid),以及预置对象(储物箱、石碑、传送门、出生点)。

9. src/data/gitea-skill-issues.json(技能跟踪数据库)

  • 建立 221 个技能与开发追踪系统的双向索引映射,包含 Skill ID、英文名称、职业缩写、Slug 标识与对应的 Issue/Milestone 编号。

部署与使用

1. 前置:自备游戏数据

把你自己合法拥有的经典版暗黑破坏神 II(1.13c) 数据放进 samples/d2/,至少需要 d2data.mpq、d2char.mpq、d2exp.mpq、Patch_D2.mpq。资源包模式(页面默认)只需要 samples/d2-packs/——它不在 Git 里,按下面的「资源包从哪来」下载解包后,没有原版数据也能看地图。

samples/d2/ 通常做成软链接,指向一个磁盘上的持久目录。切勿链到 /tmp——它是内存盘, 重启即清空,链接会变死链而角色/怪物退化成占位方块。本机的实际摆放、清单与逆向参考用的原版 DLL 见 docs/LOCAL_DATA.md。

2. 资源包从哪来(不在 Git 里)

图集包(62 个地图块 / 35 关,索引色 PNG + scene.json)不在本仓库里——它是 ~66 MB 的索引色 PNG,每次重烘几乎全部文件都变,而 PNG 无法被 git 的 delta 压缩,随仓库分发会让每烘焙一次仓库再长 ~60 MB。它放在图库仓库,那里刻意只保留一个提交(每次重烘 orphan 提交 + force push), 体积恒定在「一份快照」:

git clone https://git.projectdiablo2.cn/troytt/diablo2-web-assets.git samples/d2-packs

只想要一份压缩包(不需要 git):

curl -O https://www.laiseek.xyz/diablo2/assets/d2-packs-2026-09-14.tar.xz
sha256sum d2-packs-2026-09-14.tar.xz   # 应为 9cdfd0c19ac68de842c6725070b65d056e25f5bcc77f36d6d9a29cd9f1b431e8
tar xJf d2-packs-2026-09-14.tar.xz     # 解出 samples/d2-packs/

仓库里只保留 .gitignore 规则与本文档;要自己生成包就照第 3 步 npm run pack:data 从原版 MPQ 烘焙,再用 npm run publish:packs 发布到图库仓库(脚本会重建单提交历史)。

3. 本地跑起来

npm install                                          # 仅 vite / typescript / @types/node
tsx scripts/make-fixtures.ts     samples/fixtures    # 夹具:DC6 + 调色板 + 文本表
tsx scripts/make-map-fixtures.ts samples/fixtures    # 夹具:DT1 + DS1 + 夹具 MPQ
npm run pack:data                                    # 从 samples/d2/ 烘焙 365 场景图集 → samples/d2-packs
npm run pack:ui                                      # 烘焙 UI 综合图集与 manifest.json
npm run pack:animdata                                # 烘焙 AnimData.d2 与 monstats2.json
npm run pack:entities                                # 烘焙 7 大职业模型与怪物实体动画包
npm run dev                                          # 启动 Vite:http://127.0.0.1:5173/acts.html

pack:data 需要原版 MPQ(约 2 分钟);只用上面下载来的 samples/d2-packs/ 可以跳过它, 代价是 ?live=1 与重新烘焙不可用。

4. 页面与操作

页面 用途
/acts.html(或访问根路径 /) 主游戏页面:全屏 WebGL2 画布 + HUD 控制条 + 左右停靠全套功能面板 + 7 职业漫步战斗
/arena.html 战斗数值与技能测试竞技场:攻防数值数学模型、技能加成、伤害计算器(1.13c 原版公式验证)

页面查询参数

?act=1..5                章节
?level=<地图块 label>     直接指定细分场景,如 33-act-1-cathedral-cathy3
?quadrant=<DS1 名>       直读归档模式下指定该关卡的某个 DS1
?live=1                  不走资源包,直接按 HTTP Range 读 MPQ 解码(慢)
?pack=<目录>             换一个资源包目录      ?base=<目录>   换一个归档目录
?arena=1&class=<cls>&skill=<id>&slvl=<lvl>&autocast=1   启动竞技场自动验证模式

完整操作快捷键

  • 移动与视口:
    • WASD / 方向键:移动角色(按等距屏幕方向)
    • 鼠标滚轮 或 + / -:视口无级缩放;0:缩放复位
  • 功能面板(宽屏左右停靠):
    • I / B:开启 / 关闭背包与装备纸娃娃面板(右停靠)
    • A / C:开启 / 关闭角色属性面板(左停靠)
    • T:开启 / 关闭技能树面板(右停靠,3 系垂直选项卡切换与加点)
    • Q:开启 / 关闭任务日志面板(左停靠,按幕查看真实任务状态)
    • V:开启 / 关闭传送点网络菜单(左停靠,各幕传送点导航)
    • Esc:关闭当前开启的所有停靠面板与弹出层
  • 战斗与技能快捷栏:
    • 鼠标左键 / 右键:施放当前选中的左 / 右手技能
    • S 或点击底栏技能图标:打开 Speedbar 技能选择菜单
    • 鼠标悬浮在 Speedbar 技能上按 F1 ~ F8:绑定快捷键;在游戏过程中按 F1 ~ F8:即时切换技能
    • 1 ~ 4:快捷使用腰带对应列药水;~:展开 / 折叠腰带
    • Alt(按住):高亮显示地面掉落物品标签
    • R:切换跑步 / 走路模式
    • W:主副手武器切换(I / II 套)
  • 测试与调试功能:
    • 键盘数字 1 ~ 7:一键热切换 7 大职业(亚马逊、刺客、野蛮人、德鲁伊、死灵法师、圣骑士、女巫)并自动装配初始专属套装与储物箱
    • K / L:快速保存 / 读取角色快照存档

5. 部署到服务器

npm run build:game                # → dist-game/(base=/diablo2/)
rm -rf /var/www/d2web   && mkdir -p /var/www/d2web   && cp -r dist-game/.       /var/www/d2web/
rm -rf /var/www/d2packs && mkdir -p /var/www/d2packs && cp -r samples/d2-packs/. /var/www/d2packs/
mkdir -p /var/www/d2data && ln -f samples/d2/*.mpq /var/www/d2data/     # 只有 ?live=1 用得到

nginx(/etc/nginx/sites-enabled/default):

location = /diablo2  { return 301 /diablo2/; }
location = /diablo2/ { rewrite ^ /diablo2/acts.html last; }
location /diablo2/   { alias /var/www/d2web/; autoindex off; add_header Cache-Control 'no-store'; }

location /diablo2/packs/ {                       # 资源包:可缓存
    alias /var/www/d2packs/;
    autoindex off;
    add_header Cache-Control 'public, max-age=3600';
}

location ~ ^/diablo2/data/(?<d2file>[A-Za-z0-9_.-]+\.mpq)$ {   # 只暴露 .mpq,不列目录
    alias /var/www/d2data/$d2file;
    autoindex off;
    add_header Accept-Ranges bytes;
    add_header Cache-Control 'public, max-age=3600';
}

要点:归档必须支持 Range(否则页面会退化成整文件下载);/root 是 0700,所以用硬链接暴露给 nginx。 部署后跑 npm run verify:deploy 自检。

6. 自检命令

npm run verify:deploy         # 线上:页面 / 资源包索引 / 图集 / Range 206 / 旧入口 410
npm run verify:packs          # 资源包与"现读现解"逐项比对(绘制序、碰撞栅格逐字节、每帧像素哈希)
npm run verify:tiles          # 每个槽位画的是不是该有的瓦片类型(地面=type 0)+ 位置公式
npm run verify:alignment      # 墙 / 地面基线、屋顶偏移与引擎公式一致
npm run verify:object-lookup  # DS1 对象 id → token/mode 与 Objects.txt 交叉核对
npm run verify:acts           # 关卡表 → DS1 → DT1 → 等距场景 + 碰撞
npm run verify:d2             # 真 MPQ 数据端到端解码
npm run verify:implode        # PKWARE implode 解码器(全成员)
npm run typecheck             # tsc --noEmit
npm test                      # 运行 Vitest 完整测试套件

数据与许可

  • 仓库不含暴雪美术:原版 MPQ、reference/ 下的第三方参考源码都不入库(规则写在 .gitignore 与 THIRD_PARTY_NOTICES.md);samples/fixtures/(本项目自产)与 samples/d2-packs/(派生数据)只作为 /diablo2/assets/ 下的归档发布,不进 Git 历史。 图集包每次重烘都会让 Git 仓库再长 ~60 MB(PNG 无法 delta 压缩),所以不再入库。
  • 使用本引擎需要你自己合法拥有的游戏数据。将来落地的生成类关卡(迷宫 / 野外)会在页面与文档中标注为 "按官方参数表复现,非引擎原版布局"(生成逻辑移植自 D2MOO)。