Commit Graph

160 Commits

Author SHA1 Message Date
Brian Fertig b8e537787d Add Zenith faction roster with wing-volley and electrified-zone archetyp
- 8 new enemy types (frames 50–57) split 4/4: combo seekers fire flank-angle homing missiles with capped turn-rate; elementals throw darts that bloom into growing, damage-ticking electrified zones
- Balance follows the faction rule (2× damage, 3.5× armor vs pirate raider), documented in combat.json comments
- New `flank_barrage` and `shock_dart` abilities in EnemyAbilities.js with shared `stepShock` frame step wired into CombatSystem.update
- Generalized `_combatBootMode` to accept any id from `factionIds()` so new factions need no code change
- Painted frames 50–57 in ships-enemy.png/.psd; added capital-mindweave-surface-01 reference art
- Tests: dev/zenith.test.mjs (Node), dev/zenith-arena-browser.mjs (Playwright), updated combat.test.mjs roster and sheet-art checks, extended cdp-faction-arena.mjs with zenith tab
2026-09-29 19:07:23 -06:00
Brian Fertig 8cce093a79 Add 16 elite faction fighters with a duel arena and skill layer
- Register 8 Apex (frames 30-37) and 8 Mindweave (frames 40-47) in
  data/combat.json enemyTypes at 2x damage / 3.5x armor vs the pirate,
  each with a distinct weapon pattern and ability kit
- New shared skill layer (js/combat/EnemyAbilities.js): cloaked ambush,
  flank strafe, blink reposition, illusion decoys, telekinetic waves;
  wired into CombatSystem update/collide with cloak gating on fire,
  targeting, and player skills
- Add sweep/spiral/shell weapon patterns in js/combat/Weapons.js
- New ?combat=apex / ?combat=mindweave duel arena with an in-game
  DuelPicker (keys 1-8 / click, 0 = whole pack); plain ?combat demo
  unchanged
- Paint the 16 missing spritesheet frames (ships-enemy.png) so the
  fighters are visible; add a PNG alpha regression test guarding against
  blank cells
- Add dev/cdp-faction-arena.mjs CDP smoke check and extend
  dev/combat.test.mjs with faction roster, balance, ability, duel, and
  sheet-pixel checks
2026-09-28 21:33:23 -06:00
Brian Fertig aca351b035 Persist combat vitals in save data and restore damage on load
- Capture hull/shields/energy from the live CombatSystem into record.combat on save
- Stage `combatVitals` in prepareLoad so a panel load restores the damage the run was flying with (clamped, hull floored at 1) instead of always starting full
- Add `repairFull()` to CombatSystem and call it on world landing (startLanding) so the dock heals the ship
- Set `combatRepair` flag in jumpThroughGate's record so a system arrival also triggers a full repair, while panel loads never carry it
- Clear both staged values in resetRunState for fresh runs
- Carry `cargoCosts` through jump records to preserve per-bay pricing across systems
2026-09-28 19:06:11 -06:00
Brian Fertig ab7cd1857e Fix mineral hold clamping on restore and re-assert Explore gate
- Raise the ship's mining capacity from build records before applying saved minerals, so holds above the base 250 cap survive load/jump-cut instead of being clamped away.
- Re-assert the EXPLORE story gate after blanket slot re-arming in SurfaceScene to prevent it coming back live pre-'Special Delivery'.
- Move ui_hover SFX from row/node hover to close-button hover in BuildWindow and ResearchWindow.
2026-09-28 18:04:40 -06:00
Brian Fertig 52650ff5b9 Add compact video-only comms box for in-combat visual calls
- Introduce a `compact` hook on CommsHub so scenes can flag which
  comms should play as the small right-edge plate.
- GameScene passes `true` for 'visual' comms while combat is active,
  keeping the battlefield clear while the clip and voice still play.
- CommsBox builds a header/text/button-free variant scaled to
  `box.combat.scale` (2/3) with a configurable right-edge margin.
- Guard `beginText` and `setVoice` for the compact form, which has no
  text object or status dot.
2026-09-28 13:28:31 -06:00
Brian Fertig 8a85ec9fff Tune level-6+ encounters and bounty tiers with weighted rosters and XP s
- Combat bands split at level 6 (5–7), 9–12, and 13+ (9–12 cap) with
  weighted pirate rosters; compose() now draws `pirateTypes` from the band
  roster and GameScene uses it when spawning one-shots.
- Patrol encounters gain tiered bands (levels 6+) with mixed rosters,
  fatter rolling waves (up to 20 deep / 6 concurrent), contact-count odds
  overrides, and per-tier `xpScale` dials; legacy array rosters normalize
  to the weighted table.
- Bounty hunts replace the stubbed marksman band with tuned tiers
  (marksman 6–10, deadeye 11–14, apex 15+): growing pickets and escorts,
  escort roster mixes, elite armor/damage scales 3×–4×, and `xpScale`
  trims so payout pace tracks the curve.
- Tests updated to assert the new band shapes, roster draws, XP scaling,
  and tier resolution; skills-bar screenshots refreshed.
2026-09-27 19:45:57 -06:00
Brian Fertig 34613b254e Add real Game Over flow to end the run on hull death
- Raise a new GameOverOverlay (scrim, semi-transparent "GAME OVER" headline, Return to Main Menu / Continue) when the player's hull breaks in combat
- Add _playerWentDown beat that freezes the world and owns the screen, gating input and the update loop while active
- Route the death decision through a skill seam (_playerDeathRespawns / _syncDeathFlowRespawn) so a future respawnOnDeath skill can override; default ends the run
- Wire Continue to resume the newest save (SaveManager.latest -> prepareLoad) then scene.restart, mirroring the menu's Continue
- Update combat.json playerShip comment to reflect the real death flow replacing the placeholder respawn in the main game
- Add toxic-takeoff-01.mp4 asset
2026-09-27 18:44:19 -06:00
Brian Fertig d3341296ae Make patrol waypoints sequential like bounty hunt legs
- Enforce the sequential law in `visitWaypoint` for all route kinds: only the first unvisited waypoint logs (bounty's `anyOrder` escape hatch still applies to the kill's check-off)
- Compass target and in-world beacons now track the NEXT waypoint in order instead of the nearest unvisited; only that one pulses while the rest stand dim until their turn
- Contact checklist rows no longer name which waypoint a contact stands on, keeping the ambush hidden from the tracker
- Update data comments and tests to reflect patrols flying 1, 2, 3 in order
2026-09-27 17:15:38 -06:00
Brian Fertig 4aa0785b0d Remove rank stamp from combat bar seats
- Drop the `rankT` text object, its draw/update logic and data spec (`rankTextPx`) so seats only show icon, veil, sweep and seconds.
- Update `_ensureSkillIcons` fallback wording to reference frame/veil/sweep instead of rank stamp.
- Adjust browser test: stop asserting on rank text, recompute seat clip from the seat box center, and remove the rank-pixel regression check.
- Refresh screenshot fixtures (`dev/shots/*`) to match the new seat layout.
2026-09-27 11:52:49 -06:00
Brian Fertig a17dd7893b Wire class pick to auto-open the skill tree console
- On a confirmed class lock-in, GameScene now sets `_skillTreeAfterPick` and, once the class window's close beat finishes, delays 320 ms then opens SkillWindow with `source: 'classPick'`, handing the player straight into spending their banked points.
- Add an e2e check in classes-browser.test.mjs that a confirmed pick closes the class console and opens the skill tree with spendable points.
- Refresh skills-window screenshot fixtures to match current rendering.
2026-09-27 11:49:36 -06:00
Brian Fertig f49051be69 Sync escort convoy speed to transport while visible and out of combat
- Add `_syncEscortSpeed()` in GameScene: when an escort contract is open, the transport is on screen, and combat is inactive, cap the player hull and escort fighters at the transport's own top speed; restore each craft to its normal/roster speed otherwise.
- Fighters keep their base speed on `_baseSpeed` so the restore is exact even after a jump re-form.
- Per-frame writes only on change to avoid steady-state churn or steering disturbance.
- Hook the sync into `updateSideQuestRoute()` so it runs each frame off the escort lane (no-op elsewhere).
- Cover the new behavior in `escort-flow.test.mjs`: match applies in view/out-of-combat, restores on combat open, transport off-screen, and contract end; give test crafts distinct speeds so match and restore are observable.
2026-09-27 11:21:56 -06:00
Brian Fertig fdd0b4900c Fix skill window close button racing with fly-here handler
- Stamp `closedByButtonAt` in `SkillWindow.close()` so the scene can tell a console ✕ click apart from a world click on the same frame.
- Add a 20ms guard in `GameScene` that suppresses the fly-here action when the close just fired, matching the mining menu and comms panel contract.
2026-09-27 10:49:34 -06:00
Brian Fertig c423bfa8ab Freeze world input while skill tree console is open
- Add `skillWindow` to the full-screen click guard so clicks don't
  steer the ship or fold panels while the skill tree is open.
- Include the skill window in the shared `_frozen` condition so the
  world parks (throttle/velocity cleared) behind it, matching the
  class console and level-up marquee contract.
2026-09-27 10:47:24 -06:00
Brian Fertig 420371a4de Redesign skill tree console as grid with session-based reset
- Replace per-skill rows with a scrollable tile grid (authored `node` cells or
  auto-layout by dependency depth), elbow join lines, rank badges and empty sockets
- Add detail drawer at bottom third showing the selected skill's copy plus ADD A SKILL
  POINT, RESET and ASSIGN/CLEAR SEAT actions
- Introduce a console session in SkillState: points spent while open are PENDING and
  resettable; closing commits them; reset cascades down a branch bought this session
  or refuses when it would strand a committed point
- Wire GameScene onReset/onClose doors, toast copy and passive re-roll on refund
- Move all tree geometry/palette knobs into data/skills.json (window.tree) with clamped
  fallbacks in SkillModel.windowSpec()
- Add pure treeLayout()/classTreeLayout() to SkillModel for canvas-free layout tests
- Expand dev/skills-window-browser.test.mjs to verify regions, tile placement, session
  lifecycle, scroller (wheel/drag/thumb), and pixel-prove the gold join line
- Update README, PROJECT_NOTES and screenshot fixtures
2026-09-27 10:12:47 -06:00
Brian Fertig 51fc62ec66 Add skill layer: data-driven Skill Tree, combat bar seats, and two Vangu
- New `data/skills.json` (registered in manifest) owns every number/string for the
  per-class skill trees, type taxonomy, seat table, bar look, and window copy;
  code only reads behavior off it so adding a third skill is a data edit.
- Pure skill model + ledger (`js/skills/SkillModel.js`, `SkillState.js`): rank
  points from the class bank, spend-through-unlock edges, seat bindings, save
  round-trip via `record.playerSkills` (old saves load as an empty ledger).
- Combat runtime in `CombatSystem.js`: cast pipeline with per-skill recharge
  clocks, energy spend/regen (now on by default), Ramming Charge dash + arrival
  blast (4x..12x weapon damage by rank), Reinforced Plating hull pool re-rolled
  from base so it never compounds; charge passes through the pack and settles once.
- UI: full-screen Skill Tree console (`js/ui/SkillWindow.js`) opened from the ship
  console's SKILL TREE door (lit when the class owns a tree), combat bar seats draw
  icon/rank/cooldown/affordability states with click-to-assign and right-click clear,
  keys 1-8 cast or place.
- Evidence: new Node + Playwright suites (`skills.test.mjs`, `skills-combat.test.mjs`,
  `skills-bar-browser.test.mjs`, `skills-window-browser.test.mjs`,
  `skills-devdoor-browser.test.mjs`) plus screenshots in `dev/shots/`; README and
  PROJECT_NOTES document the layer and dev doors (`?skill`, `?skilltree`).
2026-09-27 07:20:55 -06:00
Brian Fertig 2ede7cb160 Add level-5 class selection with Vanguard and Wraith bonuses
- Introduce data/classes.json as the single source for roster, copy, accents, icon frames, perk numbers, and console chrome
- Add ClassModel (pure reads), ClassState (idempotent run ledger + save round-trip), and Aiming (intercept solve + barrel geometry) so perks are testable without Phaser
- Wire CombatSystem to the class ledger: Vanguard dual-cannon volley, Wraith predictive aim with a max-lead fallback to raw aim; cooldown unchanged
- Add full-screen ClassWindow console over the frozen GameScene (looping muted feed, Trestle briefing, themed tabs, PICK confirm), opened once at level 5 when combat is quiet or via the ship console door / ?level5 dev param
- Persist record.playerClass in saves and clear it on New Game; ship console now shows a class plate with crest + banked skill points instead of the choose-class door
2026-09-26 18:58:37 -06:00
Brian Fertig bb0005b5a5 Fix action drift during freeze states by parking ship and combat bodies
The level-up marquee, jump clip, and ack comm freezes gate world
drivers but not physics: the Arcade world still steps, so coasting
bodies kept gliding through the beat. Add _parkForFreeze() to zero
ship velocity/acceleration and all enemy/ally body motion every
frozen frame, killing any mid-freeze injection on the next tick.
2026-09-26 12:08:29 -06:00
Brian Fertig d3ebb8e2a0 Add pirate-kill reputation milestone with per-system tally persistence
- Every N pirates killed in a system (default 10) grants +1 standing to all settlements in that system, configured via data/reputation.json bonuses.pirateKills
- Track per-system kill tallies as registry-backed run state, persisted through SaveData.js capture/prepare/reset with normalization and legacy-save fallback
- Wire the payout into GameScene.recordPirateKill, called from CombatSystem's onKill seam, clamping at max standing and showing a toast when at least one place moves
- Add reputation tests covering the milestone data shape, standing writes, save round-trip, garbage-entry rejection, legacy-load, and New Game reset
2026-09-26 09:50:50 -06:00
Brian Fertig 64da303c23 Add escort denial gates and transport captain comms
- Hold jumps while the escort convoy is linked or setting down: the
  player must have the transport, its surviving escorts, and the gate
  all on screen before leaving the system.
- Close the destination world's landing door until the transport has
  reached it (phase delivered) or been destroyed (phase failed), so a
  lost convoy never blocks the reset remedy.
- Arm OAC's escortDeny visual beat plus flat toasts on both denials.
- Register the transport captain character and add neutral-transport-link
  (convoy link greeting) and neutral-transport-delivery (delivery thanks)
  comms with audio pools, all once:false so they re-arm per contract.
- Require the convoy to be on screen before the delivery begins, not just
  the destination world.
- Update escort-flow and comms tests for the new witness gates, landing
  door, and transport captain beats.
2026-09-26 09:42:56 -06:00
Brian Fertig 21e9e68033 Add reputation bonuses for side-quest claims and goods sold
- Add `bonuses` config (sideQuest 10%, perGoodSold 2%) to reputation.json
- Implement `Reputation.creditFraction()` with sub-point pending credit that pays out whole points across multiple sales
- Wire GameScene to award the issuer's standing on side-quest claim and on goods sold in the shop
- Serialize/deserialize pending credits through save/load round-trip
- Add tests covering crediting, clamping, home-world pinning, and save round-trip
2026-09-23 22:39:27 -06:00
Brian Fertig b66b3b73e3 Fix escort compass chip click not retargeting the ship
- `escortCompassTarget()` now emits a `destination` kind carrying the world's discovery id (a planet's name / a station's settlement id) instead of reusing the `waypoint`/idx shape, which autopilot resolved to `route.waypoints[0]` — the escort's rendezvous point in the destination system
- `autopilotTo()` gains an `escort:obj:` branch that resolves the payload to the world's discovery entry, and maps a stale content-record-id payload onto the live entity so the click no longer silently no-ops
- `_escortDestinationObject()` now matches the live planet/station by discovery name (a Planet's `name` is its class label) and returns the entity's `discoveryId` for compass/autopilot identity
- Compass offscreen listing suppresses the destination world's own slot when the escort arrow stands it, mirroring the existing route-gate rule
- Add headless unit test (`dev/escort-compass.test.mjs`) covering planet/station destinations, the stale record-id payload, and the old `patrol:wp:` shape, plus a Playwright repro (`dev/escort-compass-click.mjs`) that drives a real mouse click on the chip in a live browser
2026-09-23 22:30:47 -06:00
Brian Fertig c6c3f8087f Change escort compass color to magenta
- Switch the escort quest compass arrow and its default fallback from green (#7ce8a4) to magenta (#e87cf5) in both data/sidequests.json and GameScene.js so the escort marker is visually distinct.
2026-09-23 21:57:46 -06:00
Brian Fertig c1c4d0e22c Tune escort encounter pacing to a hot lane cadence
- Replace near-certain short-interval rolls with a middling 40–60% chance on a 20–30s interval and a longer 15–30s cooldown, targeting roughly 2–8 engagements per run
- Update the escort director comments in sidequests.json and GameScene.js to describe the new tuning
2026-09-23 21:51:33 -06:00
Brian Fertig 5810560b18 Add escort delivery touchdown animation and on-screen witness check
- Replace the instant "delivered" completion with a two-phase flow:
  phase `delivering` (convoy breaks off, glides beside the destination
  world, fades out) followed by the seal/payout on fade close.
- The delivery now requires the destination world to be visible in the
  camera's world rect (`_escortObjectOnScreen`) rather than merely within
  a distance radius.
- Add `_runEscortDelivery` (time-driven ease-out approach + alpha/scale
  fade, frame-rate independent) and `_sealEscortDelivery` (tear down
  convoy, close phase, complete quest, toast).
- Extend `normalizeEscort` to accept the new `delivering` phase.
- `_escortDestinationObject` now returns the world's rendered radius so
  the landing slot can anchor just off the planet/station disc.
- Add `escortArrivingToast` UI string for the "convoy inbound" moment.
- Update escort-flow test to assert the delivering → delivered sequence
  and add a standalone repro script (`dev/escort-delivery-repro.mjs`).
2026-09-23 21:39:59 -06:00
Brian Fertig 0a53931f16 Add Escort side quest: convoy rendezvous, elevated lane combat, and rese
- New "Escort" contract type on neutral job boards (minLevel 3): the player
  links a persistent transport + 1-2 escort fighters at a local waypoint,
  flies them through a seeded 1-2 hop gate route to a planet/station, and
  delivers with the sky clear.
- TransportShip extends NeutralShip with a lapsed break-off window and a
  `persistent` flag so CombatSystem's active/neutralOnly gates skip it; the
  convoy never holds the jump gate or landing door open.
- Dedicated escort EncounterDirector (9-18s rolls, 85-100% chance) fires
  mixed-fighter waves ~5x more frequently than normal encounters; one wave at
  a time via the patrol contact's law.
- Transport loss fails the contract; landing on a planet resets it with a
  fresh rendezvous in the current system and full-armor transport, same
  destination.
- 20 unique escort dossier descriptions, reward band raised to 120-175 CR.
- Three new test suites: escort.test.mjs (model), escort-flow.test.mjs
  (scene flow), escort-browser.test.mjs (real browser boot + board render).
2026-09-23 05:45:18 -06:00
Brian Fertig 6f77ada8ad Gate random encounters on the 'Special Delivery' quest claim
- Add `_encountersLockedByQuest()` to GameScene: while the delivery
  chain is live (not yet completed AND claimed), keep the encounter
  director from rolling. Quests disabled = gate off too.
- AND the new scene-side hold into `encounters.update`'s `allowed` so
  the pure clock stays untouched; document the contract in Encounters.js.
- Ship new transport sprite assets (ships-transport.png/.psd).
2026-09-22 20:48:22 -06:00
Brian Fertig 81a1589307 Add neutral-only encounter comms and soundtrack
- Introduce a NEUTRAL-ONLY encounter path: when escorts are live with zero hostiles, the combat seam now carries a `neutral` flag so the scene can swap to a dedicated neutral hum instead of the combat shuffle.
- Fire two new pilot comms once per neutral encounter — `neutralScan` (pilot on screen) and `neutralScanComplete` (first pilot break-off) — via a new `onComm` seam in CombatSystem; hostile-only beats stay silent during neutral encounters.
- Add `neutral` music context to data/music.json with its own shuffle playlist, load it in GameScene, and resume the correct context on wake.
- Ship a `?neutral` dev boot (two pilots, 8 s escort window) for testing the full beat at home, plus tests covering the comm seam, mixed→neutral re-resolution, and data validation.
2026-09-22 20:12:41 -06:00
Brian Fertig 41f6dbbfd9 Make side quest job boards per-surface-world instead of per-system
- Board postings now derive from (seed, system, world key) so two worlds in one system stand different boards; count range drops to 1–3 per world
- Posting ids gain a world segment (`sq-<sys>-<world>-<cat>-<slot>`); add `parseSideQuestId` and use it for scene lookups and claim reward reads
- `allSideQuestJobIds` now builds the job space from each system's surface worlds (planets + free-space stations) via `galaxy.ensureContent`; old per-system ids no longer stand, so in-flight contracts in pre-per-world saves drop on load (documented Path A trade)
- Surface scene passes its world name/isStation through to postings, accept, and activate; window copy updated to say "world" not "system"
- Tests updated for the new signature, per-world determinism, id parsing, and job-space guards
2026-09-22 19:14:33 -06:00
Brian Fertig 2f80ddfdb1 Add periodic random ship encounters with neutral ally escorts
- New EncounterDirector (js/combat/Encounters.js): pure, data-driven clock that rolls pirate / neutral / mixed encounters in neutral non-home systems on a ~45–90s interval at 50–60% chance, one at a time with a 30–60s cooldown, never during a live wave run or on a downed player.
- Neutral ally ships (js/combat/NeutralShip.js) on spritesheet frames 20–27: escort the player for a per-ship 10–15s window, engage the nearest pirate in mixed fights, then break off and fly off-screen; player-safe by construction (never targeted or damaged by ally bolts/seekers).
- Pirate targeting generalized so hostiles can draw aggro onto escorts when the player is out of range; escort and pirate bolts/missiles collide with each other but not the player.
- Group size scales with combat level (1–2 low → 4–5 high, bands in data/combat.json); encounter hostiles are one-shots so the fight ends when the sky clears.
- New 'volley' weapon pattern (wide staggered-speed fan) and owner-tagged bolts/missiles for ally vs enemy routing in Weapons.js.
- Neutral roster config + encounters section added to data/combat.json; EnemyTypes gains neutralTypeConfig/Ids/SheetList.
- Tests: dev/encounters.test.mjs (cadence, gate, weights, size bands), dev/neutral.test.mjs (roster, escort AI, targeting, no friendly fire), plus combat/xp test updates; CDP probes and encounter-hud-check for in-browser HUD verification.
2026-09-22 07:19:20 -06:00
Brian Fertig 8ade473479 Add faction-tinted swirl to locked jump gates
- Locked gates (neutral → faction, below minLevel) now render their
  swirling center in the controlling faction's hue as an in-world
  "that gate belongs to someone" tell, complementing the comm window's
  grayed REQUEST JUMP and lock message.
- JumpGate.setFactionTint tints the active swirl (setTint) or recolors
  the procedural fallback field discs; null clears back to the ordinary
  swirl. Falsy-but-valid hues (e.g. 0x000000) are not treated as clear.
- GameScene.gateFactionTintFor resolves the destination faction's color;
  _refreshGateFactionTints re-resolves all gates at build, once the XP
  ledger exists (save may have already lifted the lock), and on each
  level-up event to clear tints as the lock lifts.
- Test harness extended with setTint/setFillStyle mocks; new checks cover
  tint apply/clear/falsy-hue on the sprite path and field-disc recolor
  + restore on the fallback path.
2026-09-21 19:07:18 -06:00
Brian Fertig 78593b1ea2 Add faction gate lock blocking neutral→faction jumps below level 5
- New `factionGateLocked` pure rule in JumpTravel.js: a gate from a neutral system into a faction-controlled one is locked while the player's combat level is below `minLevel`; gates from faction space and neutral→neutral hops are never locked
- Config section `gates.jump.factionLock` (enabled switch, minLevel 5, panel message) in data/gates.json
- Gate comm window shows the lock message in place of the LINK line and grays out REQUEST JUMP; case widens to fit the two-line message
- `jumpThroughGate` adds a flat denial toast on top as defense in depth
- Tests cover the rule's branches (neutral/faction/unknown dest, missing level, enabled switch, custom minLevel) and assert the shipped galaxy has locked gates at level 0 that all open at level 5
2026-09-21 18:56:31 -06:00
Brian Fertig 5e799bbd5a Add pirate comms: attack/defeat/bounty voice lines with speech and video
- Raiders now roll an attack comm on-screen (50%) or on rolling-wave arrival (20%), and a defeat comm on kill (20%), driven by per-type config in combat.json
- New bounty-attack comm fires when the wanted leader's fight stands up in GameScene._fireBountyEngagements
- Ship 12 pirate speech clips, 3 enemy video clips, and register three new senders (pirate, pirate_defeat, pirate_bounty) in comms.json
- Wire onComm seam through CombatSystem so the scene can arm CommsHub triggers; rolls are no-ops when the seam is absent
- Add combat, bounty-hunt, and comms test coverage for the new roll logic, trigger arming, asset presence, and roster count
2026-09-20 21:34:05 -06:00
Brian Fertig 27a9a59c03 Add combat soundtrack that swaps in when combat goes live
- Introduce `music.combat` playlist (same shuffled contract as `game`) with its own per-context volume override (`volumes.combat`).
- Add `GameScene.setCombatMusic(active)` wired to the `_setCombatBar` seam: combat live stops the deep-space shuffle and starts the combat one; combat over swaps back unless the scene is asleep on a world's surface.
- Track sleep/wake with `_asleep` so the wake handler resumes the correct context (combat if still active, otherwise deep-space).
- Generalize `startMusicShuffleOn` to take a `context` parameter for independent volume resolution.
- Update preload to queue combat tracks under their derived keys and extend tests for combat shuffle, context swap, re-fire, asleep behavior, and data contract.
2026-09-20 20:38:07 -06:00
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 658babe216 Add XP rewards for sidequest patrols and cargo sale profit
- Sidequest patrol contracts now pay an XP reward alongside credits, fixed at accept as the experience worth of defeated contacts (contacts × wave total × roster xp) and granted via XpManager with a 'sidequest' source tag on claim
- Cargo sales earn 1 XP per credit above each bay's recorded purchase cost; bays with no cost basis or sold at/below cost earn nothing, granted via the same pipe with a 'trade' source tag
- New CargoHold.costsFor() returns FIFO per-bay costs for the profit basis without mutating the hold
- Job board payout/claim lines and console toasts display both credits and XP when present; old saves without an xp field derive it from the encounters spec or fall back to 0
2026-09-19 20:44:48 -06:00
Brian Fertig 4595950fb9 Add Pirate Problems quest payoff comms and trigger arming
- Wire GameScene._questRefresh to arm the quest's own `payoffTrigger` on the once-per-quest READY moment, so Trestle's two-beat sign-off (job boards + level-5 hook) plays when 'Pirate Problems' becomes complete + unclaimed.
- Add trestle-q4-4/5 ack comms (data/comms.json) and declare `payoffTrigger: "trestleQ4Payoff"` on the quest (data/quests.json).
- Reorder stationVideos in data/landing.json to put ss-02 before ss-01.
- Extend dev/comms.test.mjs roster count and add payoff pair checks; add new dev/quest-payoff.test.mjs pinning the arming seam; extend dev/quests.test.mjs with payoff wiring checks.
2026-09-19 19:49:30 -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 ec1ad155d6 Add level-up marquee beat for combat level landings
- New js/ui/LevelUpMarquee.js: a self-contained full-screen beat (dim, old-level shatter, digit-by-digit decode, lock-in FX, neon shutter peel) driven by hand-stepped timeline with no tweens; coalesces multi-level grants into one "+N LEVELS" beat and buffers mid-beat level-ups so none are dropped
- GameScene wires the marquee to XpManager's 'levelup' event, gates ship/combat/research/build/discovery ticks on its active flag (same pause contract as comms ack), tears it down on shutdown, and exposes a ?combat dev `marquee(from,to)` preview hook
- Add headless Node test (dev/marquee.test.mjs) covering trigger/timeline/reap, coalesce, buffer, teardown, and the FIRMWARE-MAXED cap branch
- Add CDP screenshot harness (dev/marquee-shot.mjs) plus committed dev/shots/ evidence frames for each beat phase via a new debugSeek/debugRelease latch
2026-09-19 18:42:21 -06:00
Brian Fertig 2e9b1ace5a Record per-bay cargo purchase costs and use them as the profit baseline
- CargoHold gains a parallel `costs` ledger (credits/unit actually paid, or null when no cost is on file), kept aligned across add/remove/set/capacity/dropFirstNot and serialized via toJSON/fromJSON
- ShopView stamps each bought bay with the trade's unit price so the console can compare exit prices against what was really paid
- ShipScreenModel/ShipWindow surface per-bay costs, a total-cost hold meta line, and a "PAID" chip + reference line in the bay manifest; bays with no cost read "NO COST" instead of inventing one
- GameScene passes the bay's recorded cost into scoutGood() so exit profits re-baseline against the purchase (falling back to the catalog value when absent), and restore/quest-handoff paths load null costs cleanly for legacy saves
- Adds arid-shop-02 video asset and updates cargo/saves/ship-window/shop-ui tests to cover the new ledger
2026-09-19 13:40:27 -06:00
Brian Fertig dd898bd594 Color route marks by destination kind
- Cargo deliveries set from the ship manifest or surface scout now read brown on the compass and in-world pulse; story-quest and manual picks keep the route's orange
- Add `destination.kind` ('manual' | 'cargo' | 'quest') to save/load so the mark survives a session
- Carve out held "Special Delivery" quest: a cargo pick that matches its home delivery flips to kind 'quest' so the story keeps orange
- Thread the resolved mark color through JumpGate/Planet/Station `setRouteTarget` and DiscoveryCompass entry recoloring
- Fix lava shop clip references in landing.json (ice-shop → lava-shop) and add arid-shop-01.png asset
- Extend dev probes: ship-cargo-compass checks brown/orange per kind, scout-dest asserts cargo kind, ship-shot gains a park shot for the off-screen route arrow
2026-09-19 12:38:56 -06:00
Brian Fertig 8ceb21808f Make ship console BEST EXITS rows tappable to set destination
- ShipWindow hosts a ConfirmOverlay (depth 30) so the SET DESTINATION dialog always paints above the console chrome
- Each remote exit row in the bay manifest becomes a hit-testable tap surface with hover state; local-system rows read HERE and stay inert
- Tapping a row opens the confirm dialog built from shipScreen.json templates; CONFIRM fires onSetDestination(systemId, objectId) through GameScene.setDestination, same seam as the map console
- ESC cancels the dialog before closing the console; closing the console cancels an open dialog to avoid orphan scrim
- Add dev/ship-cargo-dest.test.mjs covering hosting, row tappability, ask/confirm/cancel/close/repaint paths
- Update landing.json arid-land-03 shop reference from ice-shop-03 to arid-shop-03
- Add lava-shop-01.png and lava-shop-03.png assets
- Extend dev/ship-shot.mjs with ?tapExit / ?clickExit probes for headless verification
2026-09-19 11:57:21 -06:00
Brian Fertig 154ee9fd97 Add Ship Screen console with status and cargo tabs
- Wire the deck's SHIP slot to a full-screen ShipWindow (depth 80) following the Map/Quest window contract: bay-camera video panel with NO SIGNAL fallback, tab row, glitch/sweep dressing, ESC/scrim close, and exclusive open/close against research/map/quests/comms.
- Add data-driven Ship Status plate via pure js/ship/ShipScreenModel.js resolving every stat row from data/shipScreen.json (hull/shields live vs nominal, shield recharge, flight, fire rate/DPS, cargo/mineral holds) plus the combat level journey timeline that swaps to a CHOOSE YOUR CLASS door at level 5.
- Add Cargo Holds tab: bay grid with filled/empty/locked states, mineral aggregate bar, and a selected-bay manifest reusing TradeScout.scoutGood() for ranked exits in the buy/sell style.
- Register data/shipScreen.json in the manifest, preload ship.mp4 with skip-if-missing guard, and add dev test + screenshot harness (dev/ship-window.test.mjs, dev/ship-shot.{html,mjs}).
2026-09-19 09:28:09 -06:00
Brian Fertig da2f1fecb9 Set Special Delivery destination from bar conversation
- Move the home-world destination from the capital departure to the Special Delivery bar vignette completion, matching the story trigger.
- Keep the capital departure only for story trail and post-sale comms, so later capital visits no longer override the player's destination.
- Show a destination-set console note in SurfaceScene, staggered behind the package-handoff note when it appears.
2026-09-18 12:58:40 -06:00
Brian Fertig 1a12fa5e1a Restore in-place on confirmed load instead of cutting to the menu
- A confirmed LOAD used to hand off to the main menu, relying on the
  menu's New Game to apply the staged restore; that path resets run
  state, so the load never carried the restore through.
- GameScene and SurfaceScene now add reloadRun(): close the save
  panel and sub-bar, then restart (GameScene) or re-start (SurfaceScene)
  the game scene so the fresh create() picks up the staged restore —
  the ship lands back exactly where it was saved.
- saves-ui-test now asserts the in-place restore (ship back at the
  saved position) and covers the Return to Main Menu path.
- New CDP probe/repro scripts in dev/ drive the real index.html in
  headless Chrome to exercise the save/load user flow and diagnose
  input and confirm-geometry issues.
2026-09-18 12:13:34 -06:00
Brian Fertig d3fadcf6a5 Add hostile contact encounters to patrol side quests
- Patrol routes now carry 1–3 seeded enemy contacts placed evenly across waypoints.
- Arriving at a contact fires one rolling wave of six raiders, three at a time.
- Route completion and reward claiming require every waypoint and contact cleared.
- Contact specs are fixed on the route at accept for stable saves and deterministic replay.
- Job board, quest checklist, toasts, and map markers expose contact progress.
2026-09-18 11:12:42 -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 a690ca74a2 Enable patrol side quests with full accept/active/claim flow
- Add patrol category rules: target selection (own system or faction neighbor via jump gates), deterministic waypoint route builder inside tether regions with anchor clearance and separation constraints, completable fallback for degenerate fields
- Extend SideQuestState ledger with active slot, claimed marks, and per-contract route records (waypoints + visited indices); save/restore round-trips all new fields with invariant re-establishment
- Wire GameScene seams: acceptSideQuest establishes the patrol route on the ledger; setActiveSideQuest / claimSideQuest manage the active slot and pay out exactly once; updatePatrol checks in waypoints per frame and completes the contract; _drawPatrolMarkers paints in-world beacons; patrolCompassTarget drives the yellow off-screen compass arrow (waypoint or gate)
- JobBoardWindow walks the full door state machine: ACCEPT → SET PATROL ACTIVE → active plate → CLAIM REWARD → claimed plate, with destination and route-progress lines in the detail readout
- Quests console side tab is now live: accepted contracts appear in questSnapshot with waypoint checklist, progress, reward, and claimable from the same CLAIM REWARD / SET PRIORITY doors as story quests
- SurfaceScene exposes activateSurfaceJob / claimSurfaceJob and routes side ids through the existing claim/priority seams
- Add capital-neutral-shop video + image assets; update landing.json to reference them
- Update all headless tests (sidequests, jobboard, quests) to cover patrol target/route/ledger/scene-seam behavior
2026-09-17 22:14:03 -06:00
Brian Fertig 081a086658 Add side quest job board with deterministic postings and ledger
- Introduce js/quests/SideQuests.js: seeded posting generation (patrol/delivery/bounty/escort), placeholder resolution, reward scaling, and a SideQuestState ledger with save round-trip + job-space validation
- Wire the feature into SaveData capture/prepareLoad/resetRunState so accepted/completed contract ids ride saves while postings regenerate from the galaxy seed
- Replace JobBoardWindow placeholders with live AVAILABLE JOBS rows and JOB DETAILS readout (contract copy, payout, ACCEPT door) driven by scene seams; refresh() repaints list/detail/status off those seams
- Add GameScene.sideQuestPostings/acceptSideQuest and SurfaceScene.acceptSurfaceJob so the accept lands on the sleeping game scene's ledger with a surface console note
- Ship data/sidequests.json (category gates all OFFLINE, description pools split neutral vs faction, reward bands, UI copy) and register it in manifest.json
- Add dev/sidequests.test.mjs (gate, determinism, voice, rewards, ledger) and extend dev/jobboard.test.mjs with live postings/selection/accept/refresh coverage
2026-09-17 20:49:09 -06:00
Brian Fertig 1b9b269b80 Add Job Board console UI and surface scene integration
- Introduce js/ui/JobBoardWindow.js: nearly full-screen (depth 80) build-console-style window with a muted looping 2:3 feed on the left, and AVAILABLE JOBS / JOB DETAILS placeholder sections on the right; open/close state machine, boot reveal, ambient glitch/sweep/cursor effects, ✕ close door
- Wire the Job Board slot through SurfaceScene: toggleJobBoard/leaveJobBoard, setServiceLocked (generalized from setBarLocked) so deck + drawer stand down while the board is up with the jobBoard slot kept live as a second door; pause/resume the surface clip under the console
- Preload assets/videos/jobboard.mp4 in GameScene via data/actionbar.json → surface.explore.video (2:3 aspect), adding a NO SIGNAL fallback when missing
- Update data/actionbar.json explore drawer with the jobBoard item (isHomeSystem:false, no labelActive) and the shared video block
- Add headless test dev/jobboard.test.mjs covering config, build, geometry, open/close, update ticks, and destroy; extend phaser-stub with setVisible/setDepth
- Add dev screenshot harness dev/jobboard-shot.{html,mjs} and reference shots for drawer/board/left stages
2026-09-17 19:47:02 -06:00