A shared readability layer (js/combat/CombatHud.js) for both combat
contexts — the training range and the main game's ?combat:
- Target/lock indicator: rotating brackets on the current target (the
auto-fire target — one rule, one target, js/combat/Combat.js#pickTarget).
- Threat cues: pulsing markers above each live hostile.
- Off-screen arrows: edge arrows toward hostiles outside the camera
(zoom-aware via a context-supplied worldToScreen).
- Kill feed: newest-first fading entries, top-right.
- Gauge flash: the combat bar's shields/hull gauge flashes on the hit
it took (CombatBar.gaugeRect feeds the shared CombatHud.flash).
Pure rules stay in Combat.js (pickTarget, feedPush, offscreenArrow),
covered by dev/combat.test.mjs. The main game re-anchors the pack
around the ship once its start position settles (CombatSystem
.resyncAroundShip + a short settle window in GameScene), so the demo
pack sits in auto-fire range instead of kilometers off.
All combat rules tests pass (0 failures); CDP-verified in both
/combat and index.html?combat.
- js/combat/Combat.js: new pure handleFeel() step (smoothed turn
velocity, ramped/decaying throttle, instant brake) with EXACT
analytic integration of the eased turn — frame-rate independent by
construction (dev/combat.test.mjs: feel block, 0 failures).
- data/combat.json: combat.player tuning surface — thrust 2000, drag
1.8 (now actually overrides ship.drag in the range), rotSpeed 6.4,
rotApproach 20, thrustRamp 18, thrustDecay 30, brake 0.9.
- js/combat/CombatScene.js: updatePlayer runs the shared feel step.
ALSO FIXES WASD: this Phaser build's addKeys() object form only
resolves single KeyCodes names, so {up:'W,UP',...} put every name on
ONE shared Key with keyCode undefined and isDown polling could never
see a key-press (WASD was dead). Now keys drive off the working
keydown-/keyup- event API (the main game's pattern) with real Key
objects registered for capture; window-blur clears held keys.
- dev/combat-flycheck.mjs: CDP flight-feel check — thrust 60% in ~300
ms, 30° turn in ~150 ms, sustained spin ≥ rotSpeed, stop from full
in ~300 ms (headless ~24 fps budgets; PASS).
- New `combat.html` + `/combat` dev-server route + `index.html?combat`
boot a fixed arena where the player ship fights a pack of enemy craft;
all tuning lives in `data/combat.json` (thrust, hulls, shields, lance,
rams, respawns).
- Add `js/combat/` modules: CombatScene, EnemyShip (standoff-ring AI +
lance), Bullet, Fx, StarDrift, and pure rules in Combat.js (armor
damage, orbit step) exercised by the new `dev/combat.test.mjs`.
- Extract shared `awaitFonts` into `js/utils/Fonts.js` so both the game
and sandbox boots use one copy.
- Preload the enemy spritesheet in GameScene so the texture is ready when
combat ships in; add `ships-enemy.png` (+ `.psd`) art.
- Expose `window.__COMBAT__` (reset, spawnEnemy, player, kills) for
devtools tuning; R resets the range, P freezes it.