orbit/.pi/goals/combat-feedback-audit.md

83 lines
5.0 KiB
Markdown
Raw Permalink 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.

# Combat feedback — baseline audit (goal: make combat feel and read)
Scope: range = `combat.html` / `/combat` (js/combat/CombatScene.js), main game = `index.html?combat`
(js/scenes/GameScene.js + js/combat/CombatSystem.js + js/ui/CombatBar.js). Shared: js/combat/{Combat,
EnemyShip,Bullet,Fx}.js, data/combat.json.
## What EXISTS today
### VFX (both contexts, shared Fx.js: additive soft-dot flashes/rings/sparks, world-space, stepped in update)
- Player shot: muzzle flash (range 30 px / main 26 px, 110 ms)
- Enemy shot: muzzle flash (28 px, 100 ms)
- Bullet impact: pink flash on enemy hits, blue on player hits (130 ms)
- Player hit: generic white flash on the ship (60 px, 140 ms) — NOT distinct shield vs hull
- Enemy kill: `Fx.explode` (white core + color bloom + ring + 22 sparks)
- Player down: `Fx.explode` + range-only "SIGNAL LOST" center message (main game: no message)
- Player respawn: cyan flash
- Ram: white flash at contact midpoint
- Thrust glow dot (range only)
### SFX
- **Zero combat SFX in both contexts.**
- Range loads NO audio at all (CombatScene.preload = textures only) → range SFX needs a load step.
- Main game loads all 17 files from data/sfx.json in GameScene.preload.
- Shared voice exists: `playSfxOn(scene, name)` / `playSfxOneOfOn` (js/utils/Sfx.js) with guards
(sfx.enabled, sound manager, audio-cache check). No combat keys in data/sfx.json yet.
- Repurposable existing files (no new binaries): scan-01 (ping), ui_click/ui_hover (ticks),
ui_window (whoosh), mining-02 (crack), thunder-01..04 (boom set), type-construct (build-up),
type-deconstruct (collapse).
### Readability / UI
- Range HUD: top-left armor panel (HULL/SHD bars + numbers), top-right KILLS counter, center
message. **No damage numbers, no kill feed, no target indicator, no bar hit-flash, no threat cues.**
- CombatBar (main game): vertical SHD/HUL gauges (fill = level, updated each frame via setStats),
8 empty skill seats (cast() stub), ENR gauge (static full). **No hit-flash, no numeric readout,
no feed, no target read.**
- CombatSystem hooks: onPlayerDamage / onPlayerDown / onPlayerUp / onKill / onActiveChange —
GameScene wires ONLY onActiveChange (bar swap). The other four are available seams (currently null).
- Range has NO hooks at all — its damage/kill/down paths live inline in CombatScene.
### Ship handling
- Range (WASD): CombatScene.updatePlayer applies combat.player rotSpeed 4.8 rad/s (instant rate cap),
thrust 1500 px/s², brake 0.6×thrust (S/Down), maxSpeed 560 cap. **Drag: combat.player.drag 1.6 is
DEAD CONFIG — not read anywhere; effective drag is ship.drag 2.4 from data/ship.json via Ship.update.**
- Main game: click-to-fly (Ship.js: ship.json thrust 900, maxSpeed 480, drag 2.4, rotSpeed 10) —
mouse steers, combat adds auto-fire only. WASD not used there.
- No throttle ramp, no rotation smoothing — both feel instant/steppy (candidate micro-feel gaps).
### Duplication (parity risk)
- fire/kill/respawn/damagePlayer logic is COPIED between CombatScene and CombatSystem (each has its
own fx.flash calls, its own armor bookkeeping, its own respawn timers). Fx.js itself is shared.
- SFX wiring must go into BOTH copy sites (or a shared helper) to stay in sync.
## Gaps vs success criteria (per feedback type)
| Event | Range VFX | Main VFX | SFX (both) | Notes |
|---|---|---|---|---|
| Firing (player) | muzzle flash | muzzle flash | none | distinct sound needed |
| Firing (enemy) | muzzle flash | muzzle flash | none | (nice-to-have, distinct + quieter) |
| Bullet impact (enemy hit) | flash | flash | none | no damage number |
| Shield hit (player) | generic flash | generic flash | none | not distinct from hull hit |
| Hull hit (player) | generic flash | generic flash | none | not distinct; bar doesn't flash |
| Enemy kill | explode | explode | none | no kill feed |
| Player down | explode + msg | explode only | none | main game gives no down cue |
| Player respawn | flash | flash | none | none distinct |
| Readability item | Range | Main |
|---|---|---|
| Damage numbers (both sides) | missing | missing |
| Target/lock indicator | missing | missing (auto-fire target invisible) |
| Threat cues (hostile state) | missing | missing |
| Kill feed | KILLS counter only | missing |
| Bar hit-flash (SHD/HUL) | N/A (own HUD panel) | missing |
## Ground rules found in code (must keep)
- Pure rules in js/combat/Combat.js, tested by dev/combat.test.mjs (344 lines; stubs Phaser;
covers applyDamage/headingOf/orbitStep/EnemyShip/Bullet/CombatSystem incl. auto-fire, swept hits,
rams, respawn, cast-stub). New pure feel rules belong there + tested.
- Fx items are world-space additive dots; combat scene steps fx.update(delta) in its loop.
- No arcade colliders; swept circle checks (hitsSwept) in both contexts.
- All numbers in data/combat.json; CombatBar built like ActionBar (scene.add.* + container parenting).
- data/sfx.json key = play name, asset queued `sfx_<key>`; sfx.enabled master switch.
- Theme palette: neon #00e5ff, neon2 #ff2d6f, amber #ffc94d, ink #eaf6ff, dim #7d92c4, faint #3d4c74.