diablo2-web/PROJECT.md

88 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Project: D2GS v1.13c Online Gameplay Loops, Server Authority & Spell Visual Effects (`diablo2-web`)
## Architecture
The Diablo II v1.13c Web Client (`diablo2-web`) is structured into strictly isolated modules enforcing server-authoritative online gameplay over the D2GS v1.13c protocol:
```text
diablo2-web/
├── src/
│ ├── common/
│ │ ├── items/ # 1.13c item tables (embedded-drop-tables.ts), stat formatter (item-bridge.ts), tooltip builder (item-tooltip.ts), ground-items.ts
│ │ ├── skills/ # 1.13c Skills.txt & Missiles.txt data (skills-data.ts, skills-meta.ts, missiles-data.ts)
│ │ └── units/ # All-Act Town & Quest NPC descriptors (npc-table.ts), 1.13c direction mapping (direction.ts: DIR64_TO_DCC)
│ ├── netproto/
│ │ ├── index.ts # Sole public barrel export for netproto (strict boundary for src/client/**)
│ │ ├── domain/ # Typed ClientCommand (client-command.ts), ServerEvent (server-event.ts), ItemData & UnitSnapshot (ids.ts)
│ │ └── d2gs/
│ │ ├── tables/ # Authoritative 1.13c packet size tables (s2c-sizes.ts: C2S_PACKET_SIZES, S2C_PACKET_SIZES)
│ │ ├── items/ # 0x9C/0x9D item bitstream decoder (item-bitstream.ts)
│ │ ├── c2s/ # C2S binary packet encoders (items.ts, npc.ts, party.ts, movement.ts, interact.ts, ui.ts)
│ │ ├── s2c/ # S2C binary packet decoders (units.ts, npc.ts, merc.ts, party.ts, trade.ts, waypoint.ts, skills.ts, misc.ts)
│ │ └── registry.ts # encodeClientCommand & decodeD2gsServerPacket dispatchers
│ └── client/
│ ├── world/ # Authoritative ClientWorld state reducer & 25Hz simulation (client-world.ts, client-unit.ts, inventory.ts, self.ts)
│ ├── view/ # Scene drawables & picking (unit-drawables.ts, scene-source.ts)
│ ├── render/ # Pre-baked 1.13c missile & overlay metadata (missiles-meta.ts, overlays-meta.ts)
│ ├── scene/ # WorldRenderer (world-renderer.ts, missile-overlay-renderer.ts)
│ ├── session/ # OnlineSession (online-session.ts)
│ ├── input/ # CommandMapper (command-mapper.ts)
│ ├── ui-model/ # HudModel (hud-model.ts): syncs ClientWorld -> HudManager/WorldPanelsHud & emits ClientCommand
│ └── ui/ # HudManager (hud-manager.ts), WorldPanelsHud (world-panels.ts), InventoryPanel (inventory.ts), Minimap (minimap.ts)
└── tests/
├── arch/ # Architectural boundary enforcement (boundaries.test.ts)
├── client/ # Unit & integration tests (client-world, hud-session-play, missile-id-map, unit-facing-motion, hud-server-authority, vendor-stash-ui)
└── e2e-d2gs-online/ # 4-tier opaque-box E2E test suite + Tier 5 adversarial coverage hardening
```
### Architectural Invariants
1. **Diablo II v1.13c Ground Truth**: Every C2S and S2C opcode, packet byte length (`C2S_PACKET_SIZES` and `S2C_PACKET_SIZES` in `src/netproto/d2gs/tables/s2c-sizes.ts`), item bitstream field (`src/netproto/d2gs/items/item-bitstream.ts`), and Excel/TBL data table (`src/common/items/`, `src/common/skills/`, `src/common/units/npc-table.ts`) must strictly conform to Diablo II v1.13c.
2. **Server Authority**: When `HudManager` has a `_commandSink` attached (`hasCommandSink === true`), user actions MUST emit typed `ClientCommand` events (`src/netproto/domain/client-command.ts`) encoded via `encodeClientCommand` (`src/netproto/d2gs/registry.ts`) without mutating local container/stat/skill state ahead of the server. `ServerEvent` updates applied to `ClientWorld` (`src/client/world/client-world.ts`) and synced via `HudModel.syncFromWorld()` (`src/client/ui-model/hud-model.ts`) drive all UI state.
3. **Strict Import Boundary**: Files in `src/client/**` MUST ONLY import protocol types/functions from `src/netproto/index.ts` (never deep-importing `src/netproto/d2gs/**` or `src/server/**`). Files in `src/common/**` MUST ONLY import from `src/common/**` and remain 100% deterministic.
---
## Feature Inventory (Issue #550 — Spell Visual Effects)
Every feature from the Phase 0 Survey (derived from `ORIGINAL_REQUEST.md` `2026-10-02T07:31:06Z` and Explorer Reports `r1_1`, `r1_2`, `r1_3`) is listed below with its assigned milestone. No feature is left unassigned.
| # | Feature | Description | Milestone | Source |
|---|---------|-------------|-----------|--------|
| F17 | Canonical 1.13c Cast Overlays & Caster State Lifecycle (R1) | Display canonical 1.13c `Skills.txt` `castoverlay` (`Overlay.txt`, e.g. `fire_cast_1`, `fire_cast_2`, `ice_cast_1`, `ice_cast_2`, `ice_cast_3`, `light_cast_1`, `light_cast_2`, `teleport`, `bonearmor_cast`, `bonecast`, `cursecast`) on any casting unit (from server `SkillCast` `0x4C`/`0x4D`/`0x6C`/`0x99`/`0x9A` or local `useSkillAt`/`useSkillOn`/`attackUnit`) as a one-shot non-looping animation with dynamic point lighting; deduplicate local player casts against server `SkillCast` echoes within 250ms; expire `unit.castState` back to neutral mode (`1`) when cast duration finishes. | M5 | R1, Survey `r1_1`–`r1_3` |
| F18 | Client-Side Flying Missiles, `0x73 CreateMissile`, & 1.13c Trajectory Simulation (R2) | Preserve `at: { x, y }`, `level`, `owner` in `decode0x73CreateMissile` and spawn missile units (`unitType = 3`, `kind = 'missile'`) on `0x73` and spell casts; step at 25Hz according to 1.13c `Skills.txt` & `Missiles.txt` (`Vel`, `Range`, `LevRange`) for single-bolt projectiles (`Fire Bolt` 36, `Ice Bolt` 39, `Ice Blast` 45, `Fire Ball` 47, `Glacial Spike` 55, `Lightning` 49, `Chain Lightning` 53, `Bone Spear` 84, `Bone Spirit` 93, `Holy Bolt` 101, bow/crossbow skills), multi-projectile fans (`Charged Bolt` 38 with orthogonal jitter, `Teeth` 67, `Multiple Shot` 12), radial rings (`Frost Nova` 44, `Nova` 48, `Poison Nova` 92), spiral trajectories (`Blessed Hammer` 112), sub-missile emitters (`Frozen Orb` 64 emitting radial `icebolt`s + final nova), and ground/skyfall spells (`Fire Wall` 51, `Blaze` 46, `Meteor` 56, `Blizzard` 59); map velocity vectors to authentic 1.13c DCC directions (`DIR64_TO_DCC` / `velocityToDccDirection`). | M5 | R2, Survey `r1_1`–`r1_3` |
| F19 | Collision Detection & Spell Hit / Impact Explosion Visuals (R3) | Detect missile collisions against blocked map wall/obstacle sub-tiles and alive hostile units (respecting `pierce`); terminate non-piercing missiles on impact (or range expiry for `alwaysExplode` / `ExplosionMissile` missiles) and spawn canonical 1.13c `ExplosionMissile` / hit effect (`fireexplode` for `Fire Bolt`, `explodingarrowexp` for `Fire Ball`, `iceexplode` for `Ice Bolt`, `freezeexplode` for `Ice Blast`/`Glacial Spike`, `frozenorbexplode` for `Frozen Orb`, `meteorexplode` for `Meteor`, `teethexplode` for `Teeth`/`Bone Spear`, `bonespiritexplode` for `Bone Spirit`, `lightninghit` for `Lightning`/`Chain Lightning`/`Static Field`/`Telekinesis`) as a one-shot non-looping visual (`loop: false`, `loopAnim: false`) with dynamic point lighting and frame clamping (`totalFrames - 1`) that is removed upon completion. | M5 | R3, Survey `r1_1`–`r1_3` |
| F20 | Scene Drawables, `WorldRenderer` One-Shot Clamping, Automated Tests & Gitea PR Merge (R4) | Output `SceneOverlayDrawable` and `SceneMissileDrawable` with `loop: false` / `loopAnim: false` one-shot frame clamping and dynamic point lights in `unit-drawables.ts` and `world-renderer.ts`; verify with automated tests (`npx vitest run`) and production build (`npm run build`); commit, push `fix/issue-550-spell-visual-effects`, open Gitea PR (`Closes #550`), and merge into `main`. | M5 | R4, Survey `r1_1`–`r1_3` |
---
## Milestones
| # | Name | Scope | Dependencies | Status |
|---|------|-------|-------------|--------|
| M1–M4, E2E | Prior D2GS v1.13c Online Gameplay Milestones | F1–F16 (Completed in prior cycle) | none | DONE |
| M5 | Issue #550: Restore 1.13c Spell Visual Effects (Cast Overlays, Flying Missiles, Hit Explosions, Verification & PR Merge) | F17, F18, F19, F20 across `src/netproto/`, `src/common/skills/`, `src/client/world/`, `src/client/session/`, `src/client/view/`, `src/client/scene/`, and `tests/client/` | M1–M4 | IN_PROGRESS |
---
## Interface Contracts (Issue #550)
### 1. `src/netproto/domain/server-event.ts` & `src/netproto/d2gs/s2c/misc.ts`
- `SkillCast` variant on `ServerEvent` includes optional `at?: Pt | undefined` and `owner?: UnitRef | undefined`.
- `decode0x73CreateMissile` preserves `at: { x, y }`, `level: level || 1`, `owner: { unitType: ownerType, id: ownerId }`, and `target: { x: targetX || x, y: targetY || y }`.
### 2. `ClientWorld` (`src/client/world/client-world.ts`) & `OnlineSession` (`src/client/session/online-session.ts`)
- `OnlineSession` exposes `useSkillAt(x, y, skillId?, hand?, shift?)`, `useSkillOn(unitId, unitType?, skillId?, hand?, shift?)`, and `attackUnit(unitId, unitType?, shift?)`.
- `OnlineSession.tick(dtMs, nowMs)` invokes `this.world.tick(dtMs, ...)` with map walkability/wall collision check when `levelView` is loaded (returning `false` / unblocked when `levelView === null`).
- `OnlineSession.buildRenderableUnits(nowMs)` includes `this.world.getUnitsByType(3)` (missiles) and active caster overlays.
- `ClientWorld` deduplicates local player skill casts against server `SkillCast` echoes within `250ms`, expires `unit.castState` back to `mode = 1`, steps missiles at 25Hz, checks wall and hostile unit collisions, respects `pierce`, spawns one-shot `ExplosionMissile` units, and removes completed one-shot explosions/overlays.
### 3. `unit-drawables.ts` & `world-renderer.ts`
- `SceneOverlayDrawable` and `SceneMissileDrawable` carry `loop: boolean`, `loopAnim: boolean`, `totalFrames: number`, and clamp `frame` to `[0, totalFrames - 1]` when `!loop` (`loop === false` / `loopAnim === false`).
- `MISSILE_ID_TO_KEY` preserves exact `Missiles.txt` row names (`tests/client/missile-id-map.test.ts`); `unit.token` (`missileKey`) is passed to `resolveMissileMetadata(unit.classId, unit.token)` for client aliases such as `freezeexplode`.
---
## Code Layout & Write Ownership
- **M5 Worker (`teamwork_preview_worker_r1_1`)**: Owns `src/netproto/domain/server-event.ts`, `src/netproto/d2gs/s2c/misc.ts`, `src/common/skills/**`, `src/client/world/**`, `src/client/session/online-session.ts`, `src/client/view/unit-drawables.ts`, `src/client/scene/world-renderer.ts`, `src/client/scene/missile-overlay-renderer.ts`, `tests/client/client-world.test.ts`, and `tests/client/hud-session-play.test.ts`.