Commit Graph

283 Commits

Author SHA1 Message Date
Brian Fertig 05d483868e Add new capital planet surface assets and update planets spritesheet
- Add Krea2 turbo renders and new capital-apex/mindweave surface images
- Update capital-zenith surface variants with revised art
- Re-export planets.png spritesheet from updated planets.psd
2026-10-03 16:28:42 -06:00
Brian Fertig e7b76ceccf Scale faction enemy damage and armor by player level
- Add combat.factionScaling config: linear ramp from 1.2x damage / 2.0x armor at level 10 to the shipped 2.0x / 3.5x at level 35, clamped at both ends
- Implement isFactionType and factionScaleForLevel in EnemyTypes.js; extend enemyTypeConfig to accept a level and apply the ramp to weapon.damage, hull, and shields for faction roster fighters only (pirates, neutrals, transport stay flat)
- CombatSystem.spawnEnemy now passes the player's XP level so faction enemies scale with progression
- Gate GameScene escort rendezvous logic on originSystemId match before checking proximity, preventing convoy link in the wrong system
- Add dev tests covering ramp math at levels 0/10/20/35/99, per-fighter consistency, flat ramDamage/xp, and non-faction exclusion
2026-10-02 18:11:54 -06:00
Brian Fertig 2eb2d46ba4 Add per-faction job board voice pools with grammar linting
- 180 new side-quest descriptions (9 pools × 20) in data/sidequests.json:
  Apex, MindWeave, and Zenith each post patrol/bounty/escort copy in their
  own doctrine voice, every body naming the seeded rival via {enemy}
- descriptionPool() in SideQuests.js now chains faction's own pool → shared
  war-voice pool → neutral fallback; neutral boards never read war copy
- New dev/lib/faction-voice-lint.mjs catches article disagreement ("an Apex
  hull"), doubled words, and unresolved braces across every rival resolution
- dev/jobboard-voice-shot.mjs renders all 180 listings through the real
  JobBoardWindow panel, asserting no clipping, overflow, or grammar faults
- Updated sidequests/jobboard tests to assert pool counts, {enemy} presence,
  dossier uniqueness, fallback chain, and zero brace leaks
- Documented the pool structure in README and PROJECT_NOTES
2026-10-01 07:24:49 -06:00
Brian Fertig cb7033861f Route faction job boards to their own description pools 2026-09-30 20:35:32 -06:00
Brian Fertig 57024084f4 Read live armor from the persistent combat ledger
- The ship console's CURRENT HULL / SHIELDS now reflect the combat system's hull and shields values at all times, not just while a fight is active.
- Previously, with no combat running the plate fell back to the maxima, masking damage that persists between fights until a landing or jump-arrival repair.
- Updated the shipScreen.json comment and ShipScreenModel docs to describe the ledger as persistent across fights and clarify `nominalLabel` is an at-full copy, not an out-of-combat flag.
2026-09-30 18:53:41 -06:00
Brian Fertig 97607ec984 Add skill-point grant on level-up and deck shimmer cue for banked points
- Bank one skill point per combat level landed past 5 via the manager's 'levelup' event, landing in the class ledger whether or not a class is on file yet (spend stays gated).
- Surface the bank in the HUD: the level-up feed line now notes "+1 SKILL POINT" for levels above 5.
- Extend the deck's attract cue to shimmer the SHIP slot when skill points are banked and no ship console is open, with research winning the slot when both are ready.
2026-09-30 18:50:52 -06:00
Brian Fertig 722dbc8415 Add Mindweave bullet homing and all-hostile ability targeting
- Add `bulletHoming` config section and `stepHomingBullets` pass so Mindweave bolts/shells bend toward the nearest live hostile for a capped duration (turn-rate limited, range-limited, own-wing exempt)
- Extend telekinetic wave (`stepTkwaves`) to shove/pull every hostile craft in radius, not just the player; own wing and cloaked craft are excluded
- Extend illusion pop rule so rival-faction enemy tracers can pop their own court's phantoms (all-hostile), while own-wing bolts pass through and unclaimed courts remain player-only
- Tag illusions and tkwaves with `factionId` to support the all-hostile targeting rules
- Switch apex_mauler weapon from single "lance" to 3-bolt "burst" pattern (16 dmg each, fan 0.12 rad) per updated design comment
- Add `dev/faction-battle-sim.mjs`: headless Node harness that runs the real combat stack with Phaser stubs to fight random faction compositions and print win/loss + power reports
2026-09-30 17:54:03 -06:00
Brian Fertig 1a581af211 Add faction crossfire: rivals shoot each other in war-zones
- Bullets and seekers now carry their shooter's factionId so a
  faction's fire drops rival-faction craft but never its own wing
  (classic pack bolts/seekers are unaffected)
- NPC kills via crossfire are silent: no kill count, XP, defeat comm,
  or screen shake — the war is the world's, not the player's ledger
- Survivors of a neutral-to-player side peel off once their rivals are
  spent, clearing the sky for the director's cadence
- Bump faction cooldown to 40–60 s and update data/encounters tests
2026-09-30 16:57:52 -06:00
Brian Fertig 89fac9ef78 Rework faction encounters into two-faction crossfire with per-player dis
- Faction space now deploys the local faction AND one rival as hostile groups
  (previously only an opposing faction attacked, local was allies), with both
  sized off the level band.
- Each ship's hostility toward the player is stamped from reputation standing
  vs `encounters.faction.hostileBelow`; neutral factions are observe-only —
  untargetable by guns/skills/escort fire, their bolts/seekers/rams fizzle past
  the ship, and they peel off once rivals are spent.
- Add faction-space encounter timers (tighter roll interval + cooldown) in
  `encounters.faction`, applied via `ctx.faction` in the director.
- Route all player-targeting paths through a new `_playerPool()`/
  `playerTargetable()` gate; add `_peelOff` disengage and rival-live check.
- Update tests for the two-group compose, disposition, and faction timers.
2026-09-30 16:28:56 -06:00
Brian Fertig 6ecf199da5 Tune jumpgate activation research duration to 20 s
- Reduce data/gates.json activation.researchDuration from 45 s to 20 s so the "Unlock Jumpgates" tech completes faster once all NAV points are discovered.
- Update SystemCategory.js doc comment and fallback default to match the new duration.
- Adjust system-category.test.mjs assertions (duration, save/restore remaining-time math) to the 20 s value.
2026-09-30 15:36:27 -06:00
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 0a86995738 Update enemy ship sprite sheet 2026-09-28 19:06:34 -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 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 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 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 335eccde70 Add escort-specific status labels to job board
- Introduce `escortActive` and `escortActivate` strings in sidequests.json so escort contracts display distinct copy instead of the generic patrol text.
- Update JobBoardWindow status and action buttons to branch on `category === 'escort'`, falling back to existing bounty/patrol labels.
2026-09-27 11:24:03 -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 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 4456ee00f0 Add classes speech and video assets 2026-09-26 13:21:55 -06:00
Brian Fertig c5cb03e236 Update Trestle assets and add class images
- Refresh Trestle character image and video
- Add new classes.png and its source classes.psd
2026-09-26 12:36:28 -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 098b41b2fc Fix UI camera split going stale after a scene restart
A jump cut destroys all cameras and rebuilds a fresh main, leaving the stored split pointing at dead cameras. Worse, `main.ignore()` on the old main sets the id bit that the new main reuses, so objects assigned to the UI pass get ignored by the only live pass — rendering nowhere (the "clip plays in the background" bug).

- Add `liveSplit()` which validates the stored split against the scene's live camera list and clears it when stale
- Route `ensureUiCameras`, `assignUi`, and `assignWorld` through `liveSplit()` so a stale split is dropped and the pipeline falls back to a single pass
2026-09-26 11:40:43 -06:00
Brian Fertig 332431ecb5 Add arid tracks and swap in new spacestation music
- Add three arid theme tracks (arid-01/02/03) and a new spacestation-03 track
- Point station 15-17 at the new arid tracks instead of reusing lava
- Add spacestation variant 2 and reorder existing station frame mappings
2026-09-26 11:09:01 -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