Commit Graph

15 Commits

Author SHA1 Message Date
Brian Fertig 4312177c3d Add BOUNTY HUNT side quest with elite combat seam and verification
- Enable the `bounty` category in data/sidequests.json with level-2 gate,
  Neutral-only contexts, encounter chain odds, and 20 written leader dossiers
- Add pure bounty model to SideQuests.js: plan derivation, wave scripts, XP,
  sequential waypoint rule, and completion only via the mark's kill
- Extend combat seam (Waves/CombatSystem/EnemyShip) for a named elite unit
  deployed with the opening group at scaled hull/damage, plus haltWaves() so
  killing the mark stops deployments while survivors stay hostile
- Wire GameScene/SurfaceScene/JobBoardWindow for locked rows, dossier readout,
  sequential check-ins, engagement chain, and single idempotent claim
- Add dev/bounty-hunts.test.mjs (591 checks) and in-browser probe with JSON/PNG
  evidence; update patrol/sidequests tests for the new live category
2026-09-20 20:10:07 -06:00
Brian Fertig 69ce598f6c Add seven enemy weapon patterns and a rolling ?combat demo wave sequence
- New hostiles frames 1–7 (bomber → warden) with distinct stats and
  monotonic difficulty ramp in data/combat.json
- Shared js/combat/Weapons.js dispatch for lance, spray, burst, rapid,
  ring, missile, and combo patterns used by both combat contexts
- New dodgeable heat-seeking Missile (capped turn-rate homing) plus pure
  geometry helpers in Combat.js
- Rewire ?combat boot to startWaves('combat_demo'): 7 rolling waves, one
  type each, 6 ships per wave, 3 concurrent, replacement per kill
- Add missile_fire SFX voice and extend tests/CDP demo checks for all
  patterns and the full wave run
2026-09-20 08:17:34 -06:00
Brian Fertig 0bd93369a5 Use dedicated combat SFX voices for cannon, hit, and enemy laser
- Split the shared `fire` event into `cannon_fire`, `cannon_hit`, and `fire` so each weapon gets its own distinct voice
- Add three new sound assets (combat-mass-cannon, combat-mass-cannon-hit, combat-laser) to data/sfx.json and load them in GameScene
- Remove the 0.45 volume multiplier on enemy fire; the laser now plays at full volume since it has its own voice
- Update CombatSfx event map, both combat contexts (CombatScene, CombatSystem), dev tests, and README to reflect the ten-event SFX set
2026-09-19 19:07:50 -06:00
Brian Fertig b461bc98af Add combat XP leveling: kills earn XP, level-ups bump armor
- Kills grant the hostile type's own `xp` (the roster sets XP-per-threat;
  the curve lives in data/progression.json, 100 x n^2, capped at level 80)
- Each crossed level fires one `levelup` event: +3 hull / +2 shields with
  a full heal, cyan burst + a distinct voice, and a kill-feed line — the
  placeholder toughness the skill layer will subscribe to later
- Combat bar gains an XP row below the orbs (level, live gold fill,
  banked / next cost; hidden when the snapshot carries no xp, so the
  range bar keeps its old face)
- Kills show a gold "+N" reward floater; the level-up's SFX is the new
  8th combat event (solar_flare_01)
- XpManager is pure and tested headless (node dev/xp.test.mjs); the
  ledger saves as { level, xp } and old saves load fresh at level 0
- dev hooks (xp/grantXp), sfxcheck (range seven, main eight), shot
  captures, and README all follow along
2026-09-18 09:57:07 -06:00
Brian Fertig 5890e93cde Enforce on-screen rule for combat engagement and hits
- Add inView() pure geometry helper to test if a craft's visual circle is at least partly within the camera's visible rect
- Gate enemy fire, auto-fire target selection, currentTarget lock, and bullet collision checks behind craftVisible() so off-screen hostiles hold lances, can't be engaged, and are immune to tracers until they fly into view
- Extend pickTarget() with an optional qualifies predicate to narrow the candidate pool (used for the on-screen rule)
- Document the rule in data/combat.json and CombatSystem class comments
2026-09-17 18:12:58 -06:00
Brian Fertig 63bb6508bb Fix white-flash sticking bug with overlap guard and periodic tint purge
- Prevent overlapping hit flashes from stacking, which captured the in-flash
  state as the restore value and left crafts permanently white
- Track flashed crafts in a Set so the expiry path can always settle them
- Add purgeCraftTints(), called every ~250 ms from update(), that re-asserts
  normal colors on any craft (tracked or roster) not holding a live flash
- Wire purgeRoster in both CombatScene and CombatSystem so the purge covers
  the full player + enemy pack, not just Fx's own flashes
- Prune destroyed crafts from the tracking set to avoid leaked references
- Add tests covering overlap extension, kill-seam settle, periodic purge,
  roster coverage, and dead-craft pruning
2026-09-17 16:50:33 -06:00
Brian Fertig db75222ca2 Fix enemy ships reviving with stuck white hit-flash tint
- Add Fx.releaseCraft() to settle a craft's pending white tintFlash
  immediately on kill/destroy, so sprites never die mid-flash
- Add exported settleCraftTint() helper that resets tint mode and tint
  to identity; call it on revive (EnemyShip), respawn, and reuse seams
- Guard restore callbacks against destroyed sprites with try/catch so a
  half-restored craft can't leak white state
- Update combat screenshots
2026-09-17 08:13:47 -06:00
Brian Fertig 716f9270ae Overhaul combat FX: laser bolts, impact reads, shield-sphere strike, and
- Replace dot tracers with elongated glowing bolts (trail band + core streak + head bloom)
  and a directional muzzle flare in both the range and main-game contexts.
- Add enemy impact reads: core flash, directional sparks along the travel vector,
  cyan arc ripple on shield hits vs orange shards on hull hits, white craft hit-flash,
  and a polished damage-number pop.
- Add player shield-sphere strike effect: full-sphere outline + bright arc centered on
  the laser's incoming bearing, radial ripple, spark burst, and a shield-collapse cue
  when shields break to hull.
- Add subtle world-camera shake on player hits, enemy kills, and shield-break while
  keeping the HUD/combat bar perfectly still via the fx-ui camera split.
- Expose new visual knobs (bolt size/trail, muzzle, arc span, shake intensity) in
  data/combat.json; fix Fx.update destroying ships on tintFlash expiry.
2026-09-17 07:55:48 -06:00
Brian Fertig 28fdfdb741 Simplify pirate ambush spawn to off-screen around player
- Remove the gate-to-capital midpoint origin logic (_pirateAmbushOrigin) and
  the startWaves opts.origin seam; wave ships now materialize off-screen
  around the player from a random direction, matching the ?combat pack.
- Update combat.json, comms.json, quests.json comments to reflect the new
  spawn behavior (off-screen around player, streaming in on chase AI).
2026-09-16 20:35:03 -06:00
Brian Fertig 78b280d7db Add 'Pirate Problems' quest with combat ambush and save-state ledgers
- New main-story quest granted after 'Special Delivery' is claimed AND its payoff comms are ACKed all the way through (unlocks.acked gate)
- Three-step checklist: speak with Trestle (trestleQ4 warning beats), defend from a rolling wave of six raiders at the gate-to-capital midpoint, return to the Neutral Capital World
- CombatSystem.startWaves gains an origin option so ambush enemies materialize in a ring around a specified point rather than off-screen around the ship
- CommsHub fires a new onAck hook before onEnd so the scene can record acknowledgments and run its grant pass in the same tick
- QuestState.isUnlockMet / grantUnlocked accept a gates.acked set; GameScene supplies it from the comms acked-ledger
- SaveData captures/restores comms.acked, combat.done, and story.capitalReturned; resetRunState clears all three on New Game
- Combat is now built for every scene (not just ?combat demo); the hostile pack spawns only in the sandbox boot
- Test suites updated: quest unlock gating, ambush wave shape, roster count 25→28, save round-trip of new ledgers
2026-09-16 20:15:21 -06:00
Brian Fertig 11f315dbf0 Add enemy wave scripts, typed hostiles, and off-screen spawns
- Waves.js: pure wave runner (rolling/fixed) + CombatSystem.startWaves
  for quest-step combat; named scripts (probe/skirmish/raid) ship in
  combat.json, driven from the console until the quest hook lands
- EnemyTypes.js: named hostiles (raider/bomber) deep-merged onto the
  base craft, with per-type textures preloaded via enemySheetList()
- Hostiles now materialize off-screen (camera edge + game.spawnLead)
  and fly in on their chase AI instead of popping into view mid-fight
- Wave ships are run-owned: no self-respawn, kills feed rolling
  replacements, and the combat bar stays up through the lead and gaps
2026-09-16 15:20:53 -06:00
Brian Fertig 0d51184daa Readability layer: lock reticle, threat cues, off-screen arrows, kill feed, bar gauge flash
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.
2026-09-16 02:02:55 -06:00
Brian Fertig ecff203159 Feedback VFX pass: damage numbers, color-coded hit flashes, kill burst, down/respawn cues
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.
2026-09-16 01:06:02 -06:00
Brian Fertig 8fd94ce48c Combat SFX layer: seven distinct event voices, shared across both contexts
- js/combat/CombatSfx.js (new): ONE event→voice map + playback path for
  both contexts, using ONLY existing data/sfx.json files:
    fire=scan · impact=ui_click · shield=solar_buzz · hull=mining_split
    kill=thunder_01..04 (one at random) · down=deconstruct ·
    respawn=discovery
  plus combatSfxEnemyHit() (shield/hull decision in one place: a hit
  that breaks through to the hull plays both — the shield-pop read),
  loadCombatSfx() for the range's preload (the main game already loads
  all of sfx.json), and a capped dev log on window.__COMBAT_SFX__ so
  the wiring is verifiable headless (no audio manager) in both.
- js/combat/CombatScene.js: fire/enemy-fire/wall-clip/ram/lance/kill/
  down/respawn sites call the shared path; the player-hit voices
  (impact + shield/hull from applyDamage) funnel through damagePlayer.
- js/combat/CombatSystem.js: the same seven call sites, identical
  wiring (no per-context copies).
- dev/combat.test.mjs: sfx block — seven events mapped, voices exist in
  data/sfx.json, pairwise distinct, vol scaling, thunder pick, the
  shield/hull decision, the dev log, the master switch (0 failures).
- dev/combat.sfxcheck.mjs (new): CDP check that drives real events in
  /combat AND index.html?combat and asserts all seven events pass
  through the shared path — PASS in both.
2026-09-16 00:47:45 -06:00
Brian Fertig 1d0622a3d5 Integrate combat into the main game with auto-fire and a combat bar
- Add CombatSystem: owns the hostile pack, tracers, player armor/energy,
  auto-fire (nearest-in-range), swept-hit collisions, rams, respawn — all
  driven by data/combat.json `game.*` / `energy` sections
- Add CombatBar (Diablo 4 style): vertical shields + hull gauges on the
  left, energy gauge on the right, eight skill seats (keys 1–8) in the
  middle; shown in place of the action deck while combat is live
- `?combat` now boots the normal game with the pack spawning when the
  player starts (mouse steers, cannon auto-fires); the standalone WASD
  range remains at `/combat` / `combat.html`
- GameScene wires the system into its update loop, solid constraints, and
  click-through gate; exposes `window.__ORBIT_COMBAT__` for DevTools
- Extend combat tests to cover CombatSystem lifecycle (spawn, auto-fire,
  hits, rams, armor, respawn, cast stub, destroy)
2026-09-15 22:03:07 -06:00