Commit Graph

216 Commits

Author SHA1 Message Date
Brian Fertig 20d8fcb0ef Rework gate-access lock copy to name the target system
- Gate locks (bounty + escort) now show "ACCESS TO {system}" instead of a generic level plate, so the player sees which system they need access to first.
- Add `gateLockSystemName`, `postingGateLocked`, and `gateLockCopy` helpers in SideQuests.js with badge/sub/deny forms; fall back to level-gate copy when no system is named.
- Stamp `gateLocked` on postings so JobBoardWindow, GameScene, and SurfaceScene can branch between gate words and the existing level-gate words.
- Update bounty/escort tests to assert the gate lock names the target system (and only that system) plus the opening level.
2026-09-30 15:23:53 -06:00
Brian Fertig f9f5dca694 Rework faction systems into active war zones with faction-driven encount
- Faction-controlled systems now spawn only opposing-faction hostiles (never pirates/neutrals), and local-faction fighters join the player as allies during the fight.
- Rework patrol, bounty, and escort side-quests to the faction war model: patrols are always in-system vs one seeded rival; bounties target a rival's member in a hostile neighbor gated on jump-gate access; escorts rescue a trapped transport in hostile space and deliver it back through the gate into friendly territory.
- Implement per-faction reputation standing (setFaction/changeFaction) with save persistence; faction-owned places now resolve standingFor() via the controlling faction.
- Job boards on faction worlds post only faction-context missions with generic war-voice descriptions ({enemy} placeholder); neutral boards unchanged.
- Update all dev test suites (encounters, bounty-hunts, escort, escort-flow, reputation, sidequests) to cover the new faction rules and gate locks.
2026-09-29 21:22:42 -06:00
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 a5f69f3221 Add Inferno and Nanobots skills to the Vanguard tree
- Inferno (Offense, seat 2): flame arcs home on the nearest 2 hostiles
  (3 from rank 4) and ignite a burn that ticks on the combat loop's clock;
  scales in duration and per-tick damage with rank.
- Nanobots (Passive, no seat): mends 1% of max hull every 2s per point;
  a full hull banks nothing.
- Wire flameArc + hullRegen effect handlers in SkillEffects.js and the
  arc/burn/step logic in CombatSystem.js.
- Add firstPassiveOf() / hullRegen() to the skill ledger so the combat
  loop can read the mend's interval and color off the ranked def.
- Update icon sheet, dev shots, and the browser/combat/data test suites for
  the two new skills.
2026-09-27 22:06:03 -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 fce8a19202 Add a soft save record so Continue resumes the last launch
- SaveManager gains softGet/softPut/softClear and an optional `soft` field in the bank blob, invisible to listSlots/filledCount but counted by hasAny/latest/exportAll.
- latest() now considers the soft record as a Continue target, winning only on a strictly newer timestamp (a same-moment manual slot stays the pick); a soft winner reports slot null.
- SurfaceScene auto-snapshots via captureState on launch and calls softPut in a try/catch so a save failure never blocks take-off.
- MenuScene logs "auto" instead of a numeric slot when resuming the soft record.
- Tests cover soft round-trip, Continue eligibility, latest tie-breaking vs manual slots, persistence across managers, softClear, and exportAll inclusion.
2026-09-27 17:24:50 -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 a28aa6f7fa Add stun visual read to Boleadoras-strung enemies
- Strung hostiles dim to the data's fx.stunAlpha and show a crackling discharge (Fx.stunArms) while the snare holds, then recover with a release beat when the stun expires or on kill/revive
- Add Fx.stunArms/killStunArms + jagBolt helpers for the per-frame discharge (jagged bolts, sparks, hull stutters) tied to the snare's clock
- Store per-craft stun color/alpha from the bola catch and drive hold/release via _stunHoldFrame/_stunRelease in CombatSystem
- Add dev/stun-shot.mjs CDP script + screenshots, extend combat tests for dim/discharge/release, bump skills-bar test timeout, reformat skills.json, refresh toxic-land video
2026-09-27 16:58:59 -06:00
Brian Fertig 12d6867eae Add Wraith skills: Boleadoras snare and Blink decoy escape
- Define both skills in data/skills.json with energy, cooldown, ranks, seats,
  and per-rank stun/decoy durations
- Implement projectile flight, target intercept, capture radius, and stun in
  CombatSystem (launchBoleadoras / _stepBoleadoras / _snagBola)
- Implement Blink: spawn a decoy sprite at the old position, teleport the
  hull to a random visible-screen point, and let the decoy absorb aggro and
  enemy bolts until its clock expires
- Add bola orb/string visuals and snag readout in Fx.js (bolaStart/Step/Stop,
  snag)
- Register new combat SFX events (bola_snag, blink) in CombatSfx.js
- Extend SkillEffects with stunMs/decoyMs helpers and boleadoras/blink handlers
- Update tests: Wraith model checks, Boleadoras/Blink combat behavior, decoy
  aggro/fizzle, and SFX event roster
2026-09-27 15:44:13 -06:00
Brian Fertig 602eaf0c9e Polish Ramming Charge FX with fire textures and SCREEN blend
- Add canvas-drawn comet head, arrival fireball, and damage-radius ring textures in Fx.js, replacing the old single-dot streak/ring combo
- Switch all ram-related (and general glow) sprites from ADD to SCREEN blend to fix the "dark box" artifact over semi-transparent nebula backgrounds
- Introduce ember trail particles that linger ~1 s behind the hull during the dash, plus a flickering comet head that rides the ship and is released on impact
- Add `ramBlast()` impact sequence: ignition flash, blooming fireball, exact damage-radius ring, shock ripples, and debris sparks
- Wire in the heaviest camera shake beat (`ram`) and layer the data's sfxEvent under a thunder boom at impact
- Bump skill fx `ringLifeMs` to 480 so the damage-radius ring lingers long enough to read
- Add dev CDP harnesses (`cdp.mjs`, `ram-shot.mjs`, `ram-video.mjs`) and reference stills/video for the charge's three key frames
2026-09-27 14:28:22 -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 ea8b58e9c4 Fix SkillWindow button label visibility and color contrast
- Add the fill graphics to the container before the label so it paints behind the text instead of covering it at 0.8 alpha
- Use C.dim instead of C.faint for inert labels and borders, which were too dark on the panel fill
- Swap tag/text colors so inert tags stay readable while active tags dim appropriately
- Refresh dev screenshots to match the corrected rendering
2026-09-27 10:30:31 -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 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 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 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 1e9c528108 Fix Job Board detail text word-wrap for Phaser 4
- Pass `wordWrap` as `{ width, useAdvancedWrap }` instead of a bare number; Phaser 4 ignores the Phaser 3 shape so description copy was running off the panel.
- Add dev-only CDP probe (`dev/jobboard-wrap-*`) that boots to the Job Board, selects a posting, and dumps text-object geometry + overflow vs the panel's right edge.
- Commit captured probe artifacts (`dev/shots/jb-wrap.{png,json}`) as a regression reference.
- Add neutral ally assets: music, attack/scan/scan-complete speech lines, and pilot video.
2026-09-22 19:33:21 -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 fededdf590 Fix combat end seam so break-off cull fires onActiveChange
- The last escort leaving the screen (encounter over without a kill) now calls `_setActive()`, which flips `active` to false and emits `onActiveChange`.
- Previously only kills triggered the flip, so GameScene's deck/soundtrack swap (`_setCombatBar`) never ran on a break-off ending.
- Add a test asserting the `true→false` active flip when the last escort is culled off-screen.
2026-09-22 17:52:06 -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 93a844f07b Fix bounty-hunt tracker so READY repaints when complete flips after all
- Include the quest's complete bit in the tracker row fingerprint so a later frame that only flips `complete` (with no check change) still triggers a repaint to READY.
- Update bounty-hunt and quest-tracker tests to pin the ordering: all legs checked while contract is still out, then COMPLETE landing on a subsequent frame with checks unchanged.
- Add station surface music tracks for spacestation variants 0 and 1 in data/music.json.
2026-09-20 20:25:06 -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 d9ba476d57 Fix LevelUpMarquee digit lock on non-zero scene clocks
- Stamp `c.lockAt` as elapsed beat time instead of an absolute scene-clock value, so `el >= c.lockAt` holds and the slots actually lock onto the final level during a live session (previously they kept cycling random digits)
- Add regression test 6 covering a marquee on a scene clock at 60s
- Refresh marquee dev screenshots
- Add trestle-q4-04/05 speech assets
2026-09-19 19:16:45 -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 6c91e5b51e Add ship→cargo→destination compass dev test and lava shop video
- Add `dev/ship-cargo-compass.{html,mjs}`: headless end-to-end flow of the SHIP console → CARGO HOLDS → bay exit → CONFIRM, then Phase A (route gate marked orange on the compass with suppressed ordinary entry), hop-by-hop jumps, Phase B (destination object marked orange with subdued pulse), and REACHED clearing.
- Add `assets/videos/lava-shop-01.mp4` video asset.
2026-09-19 12:23:18 -06:00
Brian Fertig 0cf859363c Halve XP curve base from 100 to 50 for faster early leveling
- Level 1 now costs 5 kills of the base craft instead of 10
- Total XP to reach level 80 drops from ~17.4M to ~8.7M
- Update XpManager default, progression data, tests, and README to match
2026-09-19 12:07:18 -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 36e36fb2d1 Fix MineralHud capacity restore snapping the fill back to stale fraction
- Void in-flight count-up tween when max changes so a late landing can't redraw against the old cap (250/250 → full after cap grew to 500)
- Add regression test for mid-count-up capacity restore voiding the stale tween
- Archive completed ship-screen goal and add ship.mp4 asset
2026-09-19 09:53:46 -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