Commit Graph

165 Commits

Author SHA1 Message Date
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 5890e93cde Enforce on-screen rule for combat engagement and hits
- Add inView() pure geometry helper to test if a craft's visual circle is at least partly within the camera's visible rect
- Gate enemy fire, auto-fire target selection, currentTarget lock, and bullet collision checks behind craftVisible() so off-screen hostiles hold lances, can't be engaged, and are immune to tracers until they fly into view
- Extend pickTarget() with an optional qualifies predicate to narrow the candidate pool (used for the on-screen rule)
- Document the rule in data/combat.json and CombatSystem class comments
2026-09-17 18:12:58 -06:00
Brian Fertig 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 63bb6508bb Fix white-flash sticking bug with overlap guard and periodic tint purge
- Prevent overlapping hit flashes from stacking, which captured the in-flash
  state as the restore value and left crafts permanently white
- Track flashed crafts in a Set so the expiry path can always settle them
- Add purgeCraftTints(), called every ~250 ms from update(), that re-asserts
  normal colors on any craft (tracked or roster) not holding a live flash
- Wire purgeRoster in both CombatScene and CombatSystem so the purge covers
  the full player + enemy pack, not just Fx's own flashes
- Prune destroyed crafts from the tracking set to avoid leaked references
- Add tests covering overlap extension, kill-seam settle, periodic purge,
  roster coverage, and dead-craft pruning
2026-09-17 16:50:33 -06:00
Brian Fertig db75222ca2 Fix enemy ships reviving with stuck white hit-flash tint
- Add Fx.releaseCraft() to settle a craft's pending white tintFlash
  immediately on kill/destroy, so sprites never die mid-flash
- Add exported settleCraftTint() helper that resets tint mode and tint
  to identity; call it on revive (EnemyShip), respawn, and reuse seams
- Guard restore callbacks against destroyed sprites with try/catch so a
  half-restored craft can't leak white state
- Update combat screenshots
2026-09-17 08:13:47 -06:00
Brian Fertig 716f9270ae Overhaul combat FX: laser bolts, impact reads, shield-sphere strike, and
- Replace dot tracers with elongated glowing bolts (trail band + core streak + head bloom)
  and a directional muzzle flare in both the range and main-game contexts.
- Add enemy impact reads: core flash, directional sparks along the travel vector,
  cyan arc ripple on shield hits vs orange shards on hull hits, white craft hit-flash,
  and a polished damage-number pop.
- Add player shield-sphere strike effect: full-sphere outline + bright arc centered on
  the laser's incoming bearing, radial ripple, spark burst, and a shield-collapse cue
  when shields break to hull.
- Add subtle world-camera shake on player hits, enemy kills, and shield-break while
  keeping the HUD/combat bar perfectly still via the fx-ui camera split.
- Expose new visual knobs (bolt size/trail, muzzle, arc span, shake intensity) in
  data/combat.json; fix Fx.update destroying ships on tintFlash expiry.
2026-09-17 07:55:48 -06:00
Brian Fertig 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 c411a170dd Combat feedback parity: README combat section, screenshot evidence, CDP shot harness 2026-09-16 02:58:55 -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 8fd94ce48c Combat SFX layer: seven distinct event voices, shared across both contexts
- js/combat/CombatSfx.js (new): ONE event→voice map + playback path for
  both contexts, using ONLY existing data/sfx.json files:
    fire=scan · impact=ui_click · shield=solar_buzz · hull=mining_split
    kill=thunder_01..04 (one at random) · down=deconstruct ·
    respawn=discovery
  plus combatSfxEnemyHit() (shield/hull decision in one place: a hit
  that breaks through to the hull plays both — the shield-pop read),
  loadCombatSfx() for the range's preload (the main game already loads
  all of sfx.json), and a capped dev log on window.__COMBAT_SFX__ so
  the wiring is verifiable headless (no audio manager) in both.
- js/combat/CombatScene.js: fire/enemy-fire/wall-clip/ram/lance/kill/
  down/respawn sites call the shared path; the player-hit voices
  (impact + shield/hull from applyDamage) funnel through damagePlayer.
- js/combat/CombatSystem.js: the same seven call sites, identical
  wiring (no per-context copies).
- dev/combat.test.mjs: sfx block — seven events mapped, voices exist in
  data/sfx.json, pairwise distinct, vol scaling, thunder pick, the
  shield/hull decision, the dev log, the master switch (0 failures).
- dev/combat.sfxcheck.mjs (new): CDP check that drives real events in
  /combat AND index.html?combat and asserts all seven events pass
  through the shared path — PASS in both.
2026-09-16 00:47:45 -06:00
Brian Fertig 5949bac861 Sharpen range flight feel: smoothed turn, ramped throttle, instant brake
- js/combat/Combat.js: new pure handleFeel() step (smoothed turn
  velocity, ramped/decaying throttle, instant brake) with EXACT
  analytic integration of the eased turn — frame-rate independent by
  construction (dev/combat.test.mjs: feel block, 0 failures).
- data/combat.json: combat.player tuning surface — thrust 2000, drag
  1.8 (now actually overrides ship.drag in the range), rotSpeed 6.4,
  rotApproach 20, thrustRamp 18, thrustDecay 30, brake 0.9.
- js/combat/CombatScene.js: updatePlayer runs the shared feel step.
  ALSO FIXES WASD: this Phaser build's addKeys() object form only
  resolves single KeyCodes names, so {up:'W,UP',...} put every name on
  ONE shared Key with keyCode undefined and isDown polling could never
  see a key-press (WASD was dead). Now keys drive off the working
  keydown-/keyup- event API (the main game's pattern) with real Key
  objects registered for capture; window-blur clears held keys.
- dev/combat-flycheck.mjs: CDP flight-feel check — thrust 60% in ~300
  ms, 30° turn in ~150 ms, sustained spin ≥ rotSpeed, stop from full
  in ~300 ms (headless ~24 fps budgets; PASS).
2026-09-16 00:17:35 -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 4b8ddae8d9 Halve trade margins: distance bands -> x0.75/x1.15/x1.85/x2.75/x3.75
f' = 0.25 + f/2 on every band (x1.0/1.8/3.2/5.0/7.0): with the origin buy
fixed at x0.5, per-unit profit lands at exactly ~half the original in every
band (30->17, 67->35, 130->66, 218->110, 382->192 cr for the average
product) while distance stays the dominant term and the curve's shape is
preserved. Data-only change (economy.json); docs/ECONOMY.md table + example
updated. All invariants re-verified (anti-loop, monotone distance, far pays
more than buy, 1-hop viable) and the economy/cargo/shop-UI/scout suites
pass. dev/economics-report.mjs gains an ECON_BANDS=... A/B override and
dev/economy-bands-audit.mjs sweeps the band invariants on any band set.
2026-09-15 11:38:57 -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 8810ec1b3d Rework BUY-list rows: title line carries name · inventory · price · step
- Split each row into a title line (name, "N left"/"SOLD OUT" strip, price,
  stepper with `stock.gap` padding between blocks) and a full-width desc line.
- Add `buyList.stock` config (w, gap, fontSize, color) for the inventory strip
  and use it for sizing/truncation so the name never collides with the strip.
- Update `_btnXs`, `_nameMaxW`, `_descMaxW` layout helpers to place the right
  block on the title line and give line 2 the row's full text width.
- Refresh shop-ui tests to assert title-line padding, truncation budget, and
  desc wrap within the full row width.
2026-09-14 18:56:27 -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 30b62fc57f Add dev/run-all runner and self-contained test setup
- Every `dev/*.test.mjs` now imports the Phaser stub loader, so plain
  `node dev/<name>.test.mjs` works without `--import`.
- New `dev/run-all.mjs` runs the whole suite in isolated Node processes
  with filtering, concurrency, timeouts, and aggregated failure output.
- Update README and adjust dynamic imports/paths for headless test reliability.
2026-09-14 10:35:55 -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 afa7a00c29 Add Tier-3 mining research nodes and premium ship upgrades
- Add `premium_arm` and `advanced_storage` research nodes (Tier 3) to the mining tech tree, each gated by its Tier-2 parent.
- Add `mining-arm-premium` (4.0 minerals/s) and `mining-storage-advanced` (500 capacity) ship builds in data/builds.json.
- Update existing Tier-2 nodes to declare their new children in `unlocks.research`.
- Extend test suites for the two new research nodes, their build wiring, cost/affordability, DAG layout (3 rows), and availability gating.
2026-09-13 23:20:00 -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
Brian Fertig 5408616bbf Add engine flare and exhaust trail to ship
- New `js/visuals/EngineFlare.js` drives nozzle glow, ignition burst, and a short fading exhaust plume from the two side-pod engines, driven by the ship's live throttle so braking dims it and Shift+click lights it up full
- Configurable via new `thrustFx` block in `data/ship.json` (enabled flag, core/plume colors)
- Hooked into GameScene lifecycle: created per scene restart, updated from the scene clock, cleaned up on landing sleep and scene destroy so nothing survives frozen mid-glow
- Add dev harnesses for visual verification: `dev/engine-flare-test.mjs` (headless Chromium CDP walk through idle → ignite → burn → coast → settled with screenshots) and `dev/particle-probe.{html,mjs}` to confirm particle velocity follows the emitter's heading
- Add new arid planet art asset
2026-09-13 17:43:14 -06:00
Brian Fertig cedee9f58e Add galaxy-wide planet-class balance via a shared deck deal
- New `js/galaxy/PlanetClasses.js` deals non-home planets from one shared deck so every class lands at ~1/N of the galaxy while star type still flavors local systems
- Config knob `systems.planetClassBalance` (enabled + bias) blends an even split with the star-mix preference; bias 0 = exactly even, 1 = old free preference
- Stamp `galaxy.planetClasses` is read back by content generator, frame pass, economy, and type preview so lazy/eager generation agree
- `rollSystemComposition` accepts optional stamped classes; falls back to per-type weighted draws when balance is off or stamp absent
- Fix arid-surface video path in landing.json (was pointing at ice surface)
- Update shop-ui and trade-scout tests to pick rows the current system actually wants, and handle home-world exit resolution
2026-09-13 16:23:31 -06:00
Brian Fertig 8ff7f0ee12 Add 'Special Delivery' payoff comms chain on surface departure
- Register Carol as a comm sender with her video clip and role
- Add four ack beats (carol-delivered-1/2/3, oac-delivered-1) under the new 'deliveredLaunch' trigger, fired when the player launches after selling the package
- Arm the chain in GameScene.onSurfaceDeparture once story.delivered is set, with the once-ledger keeping it a one-shot
- Update comms tests to cover Carol's registration, the four-beat sequence ordering, and re-arm deduplication
2026-09-13 15:29:21 -06:00
Brian Fertig f38b3c0b86 Add special-delivery launch comms assets and seam test
- Stage the payoff audio (carol q3 05-07, oac q3 01) and video assets
  (arid-surface-02, carol character) backing the launch-after-sale beat.
- Add dev/delivery-launch.test.mjs pinning GameScene.onSurfaceDeparture:
  arms 'deliveredLaunch' once on post-delivery departure from any world,
  stays quiet pre-delivery, preserves the capital leg (launchedFromCapital,
  home routing, quest refresh), and keeps the arm firing on the defensive
  already-home no-op.
2026-09-13 15:29:00 -06:00
Brian Fertig 942de11dde Move the surface shop gate from the deck slot to the Buy/Sell tab
The Shop button on the deck is now always live — the shop sub-bar opens on every world. The "Special Delivery" capital-landing witness now gates only the shop's Buy/Sell tab (ShopView reads GameScene.isTradeUnlocked, which replaces isShopUnlocked), so trading stays dark until the player lands on the neutral capital while the rest of the shop UI is reachable everywhere.

- Rename GameScene.isShopUnlocked → isTradeUnlocked and update all references (SaveData, SurfaceScene, quests.json comments)
- Remove the deck-slot enable guard in SurfaceScene.toggleShop / setBarLocked; the slot now re-arms with the rest of the deck
- Add ShopView.isTradeUnlocked + gate checks in setTab / _onTabTap; gray the Buy/Sell tab via _refreshTabs until the gate lifts
- Update dev probes (scout-*, shop-*) to lift gs.story.capitalLanded so the trade panel is reachable
- Extend shop-ui.test.mjs with a TRADE GATE case covering closed/lifted states
2026-09-13 14:46:59 -06:00
Brian Fertig 55db379a93 Move Special Delivery package handoff to the bar scene's DONE beat
- Ship now starts with cargoHold 0; the bar vignette's DONE press builds
  out 4 bays (effects.cargoHold) and then places the special-package,
  making the parcel aboard the "cargoUpgraded" step's witness instead of
  a build-state check.
- Remove the old capital-landing handoff and SurfaceScene package flag;
  onVignetteComplete in GameScene applies effects + handoff in order and
  refreshes the quest checklist.
- Persist cargoHold through saves and gate jumps so the upgraded hold and
  its goods survive a scene restart or load (old saves fall back to the
  carried goods count).
- Add arid-land/takeoff 02 assets, fix landing.json slot 3 mapping, and
  extend dev tests (jump-cargo probe, saves round-trip, vignette flow) to
  cover the new beat ordering and persistence.
2026-09-13 14:09:16 -06:00
Brian Fertig bd8b161584 Add planet vignette dialogue system with bar meeting scene
- Introduce VignetteHub/VignetteWindow/VignetteModel to play quest-gated
  dialogue scenes over location clips on the surface (bar/shop)
- Ship first scene: Special Delivery step 1 (Trestle + Carol at the bar),
  replacing the old capitalLanded check with a vignettePlayed witness
- Add q3 speech clips for Carol and Trestle; remove retired q2 Carol clips
- Wire save/restore of the played ledger, deck lock while a scene holds,
  per-sound music ducking under voices, and window sizing to each line
- Add dev flow test (dev/vignette-flow.html/mjs) and model unit tests
- Point landing.json ice-land-01 entry at the new arid-* video assets
2026-09-13 13:13:51 -06:00
Brian Fertig fb4c8d1d3c Add Explore drawer with Bar service and monotonic quest checklist steps
- Replace the surface deck's SHIP slot with an EXPLORE drawer that folds up a full-width secondary bar (MenuSubBar span mode) carrying per-world service items resolved by a `where` clause matcher (world / planetClass / faction, case-insensitive, list any-match)
- Add the neutral capital's BAR clip toggle: swaps the surface loop for the world's bar video, relabels the slot to LEAVE BAR, and locks every other deck + drawer button until pressed back
- Record quest checklist steps as DONE on the ledger (QuestState.completed set) so a step that stands on the home world keeps standing after the ship leaves; save/load round-trips the witness and old saves degrade cleanly
- QuestWindow fresh open now lands on the run's priority quest (tracker HUD seam) with its tab activated, falling back to the active tab's first quest when no priority is set
- MenuButton.setLabel re-fits the panel and hit rect in place for the LEAVE BAR relabel; ExploreBar.js provides the pure worldContext / matchesWhere / resolveItems item logic
- Add dev tests: explore.test.mjs (deck, items, where-clause, span, relabel) and quest-window.test.mjs (open landing + priority), plus updates to quests/research-builds tests for the new surface deck layout
2026-09-13 10:45:57 -06:00
Brian Fertig 1e3beccb22 Add "Special Delivery" quest with package handoff and shop gating
- New main quest granted after claiming "Leaving the Nest": land at the neutral capital (the bar), upgrade cargo bays, then deliver the sealed package to the home world.
- Introduce a non-tradeable `special-package` good (sheet frame 114) that no shop stocks or buys back; `Economy.isTradeable` filters it out of every floor and the sell grid refuses to queue it.
- Record story witnesses (`capitalLanded`, `launchedFromCapital`, `lastLandedWorld`, `delivered`) in a registry-backed ledger, persisted via SaveData so progress survives load and reset clears it.
- Gate the surface shop behind the capital landing: deck slot disabled and `toggleShop` refuses until `isShopUnlocked`.
- On capital departure, set the destination back to the home world so the compass marks the delivery leg; delivery proof is monotonic once recorded.
- Surface console note announces the package handoff when it first lands in the hold.
- Extend economy and quest test suites to cover the new check types, non-tradeable exclusion, unlock chain, and save/load of the story ledger.
2026-09-13 08:49:50 -06:00
Brian Fertig efaa559efa Rework "Leaving the Nest" arrival to the capital world with voice
- Split trestle-arrive into an arrive-1/2 ack pair, both voiced (new q2-04/q2-05 clips)
- Quest now completes on reaching the neutral capital planet instead of entering the neighboring system; add _atCapitalWorld proximity check and arm arrival in _checkDestinationReached
- Gate capital-world landing until the quest reward is claimed, with a new capitalDenyToast denial
- Update tests, save-data comments, and fix lava-shop-02 asset reference
2026-09-12 23:16:49 -06:00
Brian Fertig b6523808fa Name scout exits by their world, not just the system
- TradeScout now resolves each exit's specific object (planet of that class, deep-space station, or home world) and exposes objectId/objectName so the destination points at the route's final stop rather than a bare system.
- SurfaceScene dialog and toast show "WORLD · SYSTEM" and pass the object id to GameScene.setDestination; null falls back to system level.
- ScoutPanel exit rows display the world name + hops instead of the class label, drop the per-section tap hint and footer (they collided with the last row in a short panel), and rename "SIGHT ON" to "SOUGHT ON".
- Save/restore now carries ship.credits and ship.cargo across jumps; GameScene restores both and refreshes the credits HUD. Saves tests cover capture, prepare, and legacy records without these fields.
- trade-scout tests assert every exit has objectId/objectName and that resolved ids are real objects of the exit class; scout-dest test verifies the confirmed destination names a real world.
- Add dev/scout-overlap probe (html + mjs) to screenshot the scout panel and numerically report any overlapping text boxes.
- Ship lava-planet-03 art and lava-land-03 video assets.
2026-09-12 20:53:16 -06:00
Brian Fertig 7549c91df1 Add set-destination from trade scout, shop HUD, and green sell selection
- Trade scout best-exit rows now open a "SET DESTINATION" confirm dialog
  (SurfaceScene.askSetDestination + ConfirmOverlay), confirming sets the
  GameScene destination; local-system exits are rejected with a note.
- Surface scene builds a corner credits/minerals readout over the shop clip,
  refreshed after trades and torn down on leave.
- Sell-bay selection restyled: green stroke + tinted fill + upper-left
  checkmark badge (data/shopui.json slot config), replacing the cyan ring.
- Add lava/capital-neutral music, videos, and landing data entries; remove
  tresle.png in favor of capital-neutral-surface.png.
- New dev probes: scout-dest, shop-hud, shop-select-shot, map-dest-shot,
  cdp-cast; extend shop-ui.test.mjs for selection visuals and exit taps.
2026-09-12 19:12:16 -06:00
Brian Fertig 141cbdb156 Add route-target pulse halo to gates, planets, and stations
- New shared RoutePulse entity draws a steady ring + two radar ripples
  scaled to the host's keepout radius, with configurable alpha/period
- JumpGate wears the full pulse (route.pulse) when it is the route's next
  stop; Planet and Station wear the subdued variant (route.objectPulse)
  when they are the destination object in the destination system
- GameScene marks exactly one entity per frame via setRouteTarget() and
  drives its update loop; all others are cleared
- Add data/gates.json sections for pulse and objectPulse geometry/alpha
- Rename laval-surface-02.png to lava-surface-02.png and add the
  corresponding video asset
- Extend jumpgate, planet, and station test stubs with Phaser Display.Color
  and recording graphics; add dev/route-pulse.test.mjs unit tests and
  dev/route-pulse-live.{html,mjs} browser integration checks
2026-09-12 16:35:05 -06:00
Brian Fertig 338a45e782 Add dual-shimmer rim to READY quest rows in the tracker HUD
- New js/ui/PerimeterShimmer.js: constant amber base rim + glow, and two comets in counter-streams (amber CW, neon cyan CCW) tracing the cut-corner plate perimeter; pure arc math (perimeterPath / arcPoint / arcPolyline) exported for bare-Node tests
- QuestTrackerHud promotes READY rows to the console's cut-corner plate (CyberShape.rectPoints), drives PerimeterShimmer per-frame on the absolute clock so repaints never reset comet phase, and fires a one-shot rim flash when a row is claimed; shimmers are torn down with each paint
- data/quests.json gains tracker.shimmer config (enabled, notch, periodMs, dashFrac, phaseOffsetPerRow, claimFlashMs, base, comets)
- CyberShape.rectPoints added for absolute-coord cut-corner polygons
- New dev/perimeter-shimmer.test.mjs covering arc math, counter-streams, absolute-phase invariance, degenerate configs; quest-tracker tests extended for shimmer lifecycle and claim flash
2026-09-12 11:42:56 -06:00
Brian Fertig 54989d3a3a Make BUY/SELL a toggle and save pending trade across panel close
- Pressing the open tab now closes the panel (back to entry state) instead of
  rebuilding it; pressing again re-opens with the selection restored, so hiding
  the panel no longer loses the player's pending trade.
- Save the SELL/BUY maps onto ShopView (_pendingSell/_pendingBuy) on toggle-close
  and tab switch; _restorePendingSelection() re-applies them when the panel is
  rebuilt. The sub-bar stays live while closed: NET shows the delta, Clear
  cancels the saved trade, Confirm executes it.
- preview()/clear()/_afterTrade()/_hasAnySelection() now treat a null tab with a
  pending selection as an active trade, so the buyback path is no longer shadowed.
- Rename ScoutPanel.data to report: Phaser's GameObject reserves .data (its Data
  manager), and a plain object there crashed GameObject.destroy(). Update all
  callers in ShopView, tests, and dev/shop-shot.mjs.
- Simplify the scout price line to credits only ("9 CR") and drop the now-unused
  priceSell style from shopui.json; update goods.png/psd.
- Add setSellMap()/setBuyMap() to TradeGrid/BuyList to restore a saved selection
  onto freshly rebuilt panels, and cover the toggle flow in shop-ui.test.mjs plus
  a toggle probe in dev/shop-shot.mjs.
2026-09-12 11:19:39 -06:00
Brian Fertig 564b086c6a Add trade scout panel to inspect goods and charted exits in shop
- Tap a BUY-list row's box (outside the stepper) to swap the cargo grid for a strategy report showing the good's identity, producer/demand classes, and top-N charted exits ranked by sell price with per-unit profit.
- New pure TradeScout module computes the report from galaxy data and visited systems; new ScoutPanel renders it over the SELL grid area.
- BuyList gains tap detection (distinguishing taps from drags), a focus glow on the scouted row, and an onRowTap hook.
- ShopView wires open/close/swap semantics: same box toggles off, different box swaps report, dead-space or panel tap closes and restores cargo grid; sell-grid input is disabled while scout is shown.
- Add Trestle character art, Carol Q2 speech lines, shopui.json scout config section, and tests for both the pure logic and UI integration.
2026-09-12 10:28:13 -06:00
Brian Fertig 071f3ac0e4 Align shop sub-bar to span full action deck width
- Widen the tab sub-bar from a button-width pill to the full deck span (left/right edges of the main action bar), centering it on that span.
- Pack tabs at the left edge and Clear/Confirm + NET readout at the right edge, compressing gaps rather than button widths when the deck is narrow.
- Right-anchor the NET value (origin 1,0.5) and reserve its widest slot so growing totals never shift the label or overflow the bar.
- Extend SurfaceScene.shopAnchor to expose left/right edges for the sub-bar layout.
- Add sub-bar probe assertions in dev/shop-shot.mjs and unit tests in dev/shop-ui.test.mjs covering span, alignment, non-overlap, and NET math.
- Add carol character image and lava-land video assets.
2026-09-12 09:44:30 -06:00
Brian Fertig e9eda003b4 Fix shop buy-list clipping and add a scrollbar
- Rows that spill past the viewport edge used to render whole, ghosting over
  the BUY title or onto the Confirm bar; now only elements fully inside the
  window render (this Phaser build can't crop glyphs), and row plates draw
  just their in-window slice for a clean cut.
- Clipped-out steppers are no longer tappable: hit-testing gates on element
  containment, not just row visibility.
- Add a right-edge scrollbar (track + drag/jump thumb) that tracks scroll
  position and hides when content fits the viewport.
- Extend shop-shot/dev tests to verify the clip invariant, dead-click
  behavior, and scrollbar geometry; add a geckodriver screenshot helper.
2026-09-12 09:21:05 -06:00