All from the one shared Fx path (js/combat/Fx.js) — both contexts call
the same cues, so the read never drifts:
- Fx.damage(): floating damage numbers on every hit, cyan when the
shields took it / orange when the armor did (both enemy + player).
- Hit flashes are now color-coded by the layer that took the hit
(FX_COLORS.shield / .hull) instead of a flat white.
- Fx.explode() (the kill burst) gains a second delayed shockwave ring
+ a denser spark burst.
- Fx.down(): the player-DOWN cue — hot-red burst + big DOWN hold.
- Fx.respawn(): the REFORMED cue — clean cyan bloom + pop.
- Fx.text() is a graceful no-op without scene.add.text (the Node test
stubs), and canvas text gets a CSS string (toCss) per the v4 Giedi
fillStyle quirk.
CombatScene + CombatSystem wire every hit/kill/down/respawn site to the
shared cues. dev/combat.test.mjs stays 0 failures; CDP screenshots
(/tmp/vfx-cues-*.png) show the damage numbers, kill burst, DOWN and
REFORMED cues live in both /combat and index.html?combat.
- 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.