Commit Graph

152 Commits

Author SHA1 Message Date
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 7aba525aec Gate the EXPLORE slot behind the Special Delivery quest
- Deck-locks the explore action until the 'Special Delivery' story quest is granted, reading the same shared QuestState ledger as the trade unlock so loaded saves see consistent state.
- Adds `_exploreUnlocked()` and `_syncExploreLock()` helpers; re-syncs on quest claim so granting the quest lifts the gate live.
- Refuses direct presses with a console note when the story gate is still closed.
2026-09-22 17:59:55 -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
Brian Fertig 919bcc4c4e Add isHomeSystem where-clause to hide Job Board in the home system
- The player's home system is safe space (no enemies), so no work should be posted on its board.
- worldContext now exposes isHomeSystem from GameScene; matchesWhere handles boolean values with list any-match.
- data/actionbar.json gives jobBoard a { isHomeSystem: false } clause instead of showing everywhere.
- explore.test.mjs covers home/non-home worlds, stations, and boolean-clause matching.
- Added the jobboard.mp4 bar clip asset; updated SurfaceScene/GameScene comments to reflect the new rule.
2026-09-17 18:35:52 -06:00
Brian Fertig d61f2d6934 Add combat comms, block jump/landing during fights, and retime pirate am
- Add OAC "enemies detected" / "enemies defeated" visual comms with random voice pools, fired on CombatSystem active-flag flips
- Deny jumping or landing while a fight is live so the wave run can't be escaped or hidden on a surface
- Move the 'Pirate Problems' ambush trigger to the midpoint of the entry-gate → capital-world stretch, persisting lastJumpFrom through saves for a stable midpoint
2026-09-17 17:18:53 -06:00
Brian Fertig 28fdfdb741 Simplify pirate ambush spawn to off-screen around player
- Remove the gate-to-capital midpoint origin logic (_pirateAmbushOrigin) and
  the startWaves opts.origin seam; wave ships now materialize off-screen
  around the player from a random direction, matching the ?combat pack.
- Update combat.json, comms.json, quests.json comments to reflect the new
  spawn behavior (off-screen around player, streaming in on chase AI).
2026-09-16 20:35:03 -06:00
Brian Fertig 9d86f5a481 Suppress compass nav chips during active combat
- While `this.combat.active` is true, call `compass.refresh([])` and
  return early so edge arrows and name boxes step aside.
- Combat chrome (threat markers, off-screen hostile arrows, kill feed)
  owns the screen edge while the player is shooting; nav chips would
  only compete with it.
- The target set is recomputed every frame, so nothing is lost — a
  mid-fight discovery still pings above, and the first non-combat frame
  re-feeds the compass entries.
2026-09-16 20:19:47 -06:00
Brian Fertig 78b280d7db Add 'Pirate Problems' quest with combat ambush and save-state ledgers
- New main-story quest granted after 'Special Delivery' is claimed AND its payoff comms are ACKed all the way through (unlocks.acked gate)
- Three-step checklist: speak with Trestle (trestleQ4 warning beats), defend from a rolling wave of six raiders at the gate-to-capital midpoint, return to the Neutral Capital World
- CombatSystem.startWaves gains an origin option so ambush enemies materialize in a ring around a specified point rather than off-screen around the ship
- CommsHub fires a new onAck hook before onEnd so the scene can record acknowledgments and run its grant pass in the same tick
- QuestState.isUnlockMet / grantUnlocked accept a gates.acked set; GameScene supplies it from the comms acked-ledger
- SaveData captures/restores comms.acked, combat.done, and story.capitalReturned; resetRunState clears all three on New Game
- Combat is now built for every scene (not just ?combat demo); the hostile pack spawns only in the sandbox boot
- Test suites updated: quest unlock gating, ambush wave shape, roster count 25→28, save round-trip of new ledgers
2026-09-16 20:15:21 -06:00
Brian Fertig c352e089e8 Rework combat bar into bottom-centered orb strip
- Replace vertical gauges with draining hull/shield/energy orbs
  (hull with overlapping shield on the left, energy on the right).
- Share the main game's CombatBar in the combat range and add
  range energy tracking with optional regen.
- Make gauge hit flashes shape-aware: circle orbs get a soft bloom,
  rects keep the legacy white pulse.
- Update combat data and assets for the new orb-strip storyboard.
2026-09-16 16:14:35 -06:00
Brian Fertig 11f315dbf0 Add enemy wave scripts, typed hostiles, and off-screen spawns
- Waves.js: pure wave runner (rolling/fixed) + CombatSystem.startWaves
  for quest-step combat; named scripts (probe/skirmish/raid) ship in
  combat.json, driven from the console until the quest hook lands
- EnemyTypes.js: named hostiles (raider/bomber) deep-merged onto the
  base craft, with per-type textures preloaded via enemySheetList()
- Hostiles now materialize off-screen (camera edge + game.spawnLead)
  and fly in on their chase AI instead of popping into view mid-fight
- Wave ships are run-owned: no self-respawn, kills feed rolling
  replacements, and the combat bar stays up through the lead and gaps
2026-09-16 15:20:53 -06:00
Brian Fertig 0d51184daa Readability layer: lock reticle, threat cues, off-screen arrows, kill feed, bar gauge flash
A shared readability layer (js/combat/CombatHud.js) for both combat
contexts — the training range and the main game's ?combat:

- Target/lock indicator: rotating brackets on the current target (the
  auto-fire target — one rule, one target, js/combat/Combat.js#pickTarget).
- Threat cues: pulsing markers above each live hostile.
- Off-screen arrows: edge arrows toward hostiles outside the camera
  (zoom-aware via a context-supplied worldToScreen).
- Kill feed: newest-first fading entries, top-right.
- Gauge flash: the combat bar's shields/hull gauge flashes on the hit
  it took (CombatBar.gaugeRect feeds the shared CombatHud.flash).

Pure rules stay in Combat.js (pickTarget, feedPush, offscreenArrow),
covered by dev/combat.test.mjs. The main game re-anchors the pack
around the ship once its start position settles (CombatSystem
.resyncAroundShip + a short settle window in GameScene), so the demo
pack sits in auto-fire range instead of kilometers off.

All combat rules tests pass (0 failures); CDP-verified in both
/combat and index.html?combat.
2026-09-16 02:02:55 -06:00
Brian Fertig 1d0622a3d5 Integrate combat into the main game with auto-fire and a combat bar
- Add CombatSystem: owns the hostile pack, tracers, player armor/energy,
  auto-fire (nearest-in-range), swept-hit collisions, rams, respawn — all
  driven by data/combat.json `game.*` / `energy` sections
- Add CombatBar (Diablo 4 style): vertical shields + hull gauges on the
  left, energy gauge on the right, eight skill seats (keys 1–8) in the
  middle; shown in place of the action deck while combat is live
- `?combat` now boots the normal game with the pack spawning when the
  player starts (mouse steers, cannon auto-fires); the standalone WASD
  range remains at `/combat` / `combat.html`
- GameScene wires the system into its update loop, solid constraints, and
  click-through gate; exposes `window.__ORBIT_COMBAT__` for DevTools
- Extend combat tests to cover CombatSystem lifecycle (spawn, auto-fire,
  hits, rams, armor, respawn, cast stub, destroy)
2026-09-15 22:03:07 -06:00
Brian Fertig 15fe08a926 Add combat sandbox: standalone arena to tune combat before it ships
- New `combat.html` + `/combat` dev-server route + `index.html?combat`
  boot a fixed arena where the player ship fights a pack of enemy craft;
  all tuning lives in `data/combat.json` (thrust, hulls, shields, lance,
  rams, respawns).
- Add `js/combat/` modules: CombatScene, EnemyShip (standoff-ring AI +
  lance), Bullet, Fx, StarDrift, and pure rules in Combat.js (armor
  damage, orbit step) exercised by the new `dev/combat.test.mjs`.
- Extract shared `awaitFonts` into `js/utils/Fonts.js` so both the game
  and sandbox boots use one copy.
- Preload the enemy spritesheet in GameScene so the texture is ready when
  combat ships in; add `ships-enemy.png` (+ `.psd`) art.
- Expose `window.__COMBAT__` (reset, spawnEnemy, player, kills) for
  devtools tuning; R resets the range, P freezes it.
2026-09-15 20:54:33 -06:00
Brian Fertig 15eaf26c03 Change cargo bay builds to cost credits + minerals
- Rebalance the three cargo hold upgrades (8/12/16) from a credits-only sink to a mixed cost of credits and minerals, so expanding the hold draws on both trade income and the mining economy.
- Update research descriptions, build data, tests, docs, and code comments to reflect the new two-resource costs.
2026-09-14 19:27:55 -06:00
Brian Fertig 138096cd2b Add Cargo research category with quest and build-completion gates
- Introduce data/research/cargo.json: a 3-node chain (8 → 12 → 16 bays) where each node is a blueprint that unlocks the matching ship-hold build
- Add `build` gate field to ResearchModel.js (attachBuildGates) so a node can require a named build to be completed before it becomes researchable, composing with the existing quest gate
- Wire both gates in GameScene and ResearchWindow so node availability, lock notes, and the RESEARCH button reflect quest + build state
- Gate cargo_bay_8 on the special_delivery quest; cargo_bay_12/16 on Improved/Advanced Mining Storage builds
- Update builds.json to require the corresponding research nodes for cargo-bay-8/-12/-16
- Add comprehensive tests covering the cargo tree structure, pure DAG availability, live gate composition, and build-side lock-step
2026-09-14 19:13:56 -06:00
Brian Fertig 828a5b8c38 Implement economy model v2: one world, one good with daily batch stock
- Replace producer/demand class pricing with strict 1:1 origin assignment;
  each settled system sells exactly its signature good at a discounted origin
  price, and the catalog remainder becomes a hub pool resold by stations/capitals
- Make banded hop distance the dominant sell-price factor (x1.0 to x7.0),
  with planet-type demand as a secondary +10% modifier, and cap every shop's
  sell price at its own buy price to eliminate the v1 infinite-credit loop
- Add Stock.js for the time-gated daily batch (6 units per origin per wall-clock
  day) — the economy's only stateful piece; wire it through GameScene,
  SaveData capture/restore/reset, and the shop UI stepper clamp + SOLD OUT strip
- Update Economy.js with assignOrigins pass, new buyPrice/sellPrice/shopFor
  logic, tradePreview stock-cap backstop (buyback exempt), and distanceFactor
- Rework shop UI: BuyList shows "N left"/"SOLD OUT" per batch-limited row and
  clamps the stepper; ScoutPanel shows MADE IN (origin system) instead of GROWN ON;
  ShopView wires stockRemaining/stockCapsFor/spendStock into preview and confirm
- Update all dev tests (economy, cargo, shop-ui, saves) to assert model v2
  invariants including the anti-loop sweep, batch clamp/backstop, and save round-trip
2026-09-14 18:22:21 -06:00
Brian Fertig f0578f1a38 Gate mining Tier-3 research on Special Delivery quest claim
- Add an optional node quest field so research techs can require a named quest's reward to be claimed before becoming available.
- Wire the scene's live questClaimed predicate into ResearchModel.attachQuestGates and the research tree loaders so node states, the RESEARCH button, and LOCKED readouts all honor the gate.
- Gate mining premium_arm and advanced_storage on the Special Delivery quest, and add data/test coverage for the quest-gate behavior.
2026-09-14 15:12:07 -06:00
Brian Fertig 54d2886c91 Remove waypoint kind; allow up to two deep-space stations per system
- Waypoints were comms-only beacons with no landing or trade; a second
  deep-space station is a more useful destination in their place
- The old waypoint chance in systems.json becomes
  deepSpaceStation.second, so a system can roll 0–2 stations
- StationFrames now assigns a distinct sprite variant per station
  (Galaxy.stationFrames is id → [frame, …]) and a two-station system
  always wears two different variants
- Rename the route-gate pulse from "destination's waypoint" to
  "route's next stop" so the term no longer refers to a removed kind
2026-09-14 10:25:19 -06:00
Brian Fertig 58aab347c2 Add OAC "new research" audio beat on research sign-off end
- Arm `oac-research-new` (trigger `researchNew`) from `_commEnded` when the `oac-research-complete` sign-off finishes and `researchAnyAvailable()` is true, so the hub FIFO plays it right behind the sign-off
- Register the new audio-only comm in `data/comms.json` and move `researchNew` out of the reserved trigger list
- Update roster count to 25 and add node checks for the beat's fields, clip presence, and zero delay in `dev/comms.test.mjs`
- Add headless browser harness (`dev/new-research-shot.html`/`.mjs`) that verifies the seam, gating (in-flight vs available), and FIFO ordering through the real `onEnd` hook
- Ship `assets/videos/arid-shop-03.mp4`
2026-09-13 20:52:28 -06:00
Brian Fertig 1f39c25c2f Add READY-RESEARCH attention cue on deck slot and console tabs
- New ShimmerCue module: pure shimmerPhase/drawShimmer cycle (yellow band sweeps face, comet runs cut-corner edge), shared by both surfaces
- ActionBar gains setShimmer + a dedicated additive layer; the RESEARCH slot wears the full face+edge recipe while a tech can start and the console is closed
- ResearchWindow._paintCues paints the edge-only shimmer on every category tab whose tree still holds an available tech, plus the ACK pulse on the "Unlock Jumpgates" plate (or SYSTEM tab when off-screen)
- GameScene._applyResearchCue switches one cue at a time: deck slot when console closed, console cues when open; rests while a project runs (maxConcurrent 1), under standby, or under the tutorial finger
- ResearchModel.hasAvailable and SystemCategory.gatesReady/gatesNodeId encode the ready rules
- data: actionbar.json animation.shimmer, research.json fx.tabShimmer (#ffd84d, comm-pulse yellow family)
- dev: Node test (research-cue.test.mjs) pinning the pure cycle model + slot/tab behavior; browser verify page (research-cue-shot.html/.mjs) with CDP capture poses
- assets: arid-surface-03 / arid-takeoff-03 videos
2026-09-13 19:48:53 -06:00
Brian Fertig 4334d8cc93 Add tutorial "pointing finger" highlight for starter quest
- New TutorialGuide.js beat table picks which control to pulse based on live comms/research records, so a loaded run resumes the same beat
- ActionBar and ResearchWindow gain setHighlight() with pulsing rims (ACKNOWLEDGE-style sonar rings in the console) that hide under deck standby/locked slots/closed window
- GameScene applies the highlight per frame before surfaces draw, tracks scanPressed one-shot, and reads targets from data/quests.json → tutorial
- Add tutorial section to quests.json plus tests for beat table, ActionBar rim gating, and data consistency (slot ids, console node matches starter research check)
2026-09-13 18:48:06 -06:00