- SaveManager: 10-slot localStorage bank (put/get/clear/list/export), corrupt-bank recovery, failed-write surfacing, bulk JSON export
- SaveData: captureState / prepareLoad / consumeRestore / resetRunState — seed + player state round-trip through a shared registry
- MenuSubBar: fold-up drawer off the deck's MENU button (Save Game / Load Game / Return to Main Menu) with animated open/close
- SavePanel: 10-slot pop-up in save or load mode; overwrite + load confirm dialogs; "download all" bulk export; toast feedback
- ConfirmOverlay: split-open confirm dialog for destructive actions
- SlotCard, Toast: per-slot cards and inline feedback strips
- MenuScene: Load Game button (grayed while bank is empty) opens the same panel in load mode
- GameScene: menu sub-bar + save panel wired into deck action, ESC handling, input blocking while modal, applyRestore on scene boot
- Tether HUD moved from above the MENU button to upper-right corner, stacking downward
- MenuButton: disabled (grayed) state and screenFixed option for scrolling-camera UI
- New SFX assets (ui-click, ui-hover) + updated existing FX; gasgiant planet image
- Dev tooling: cdp-firefox.mjs, wdshot.mjs (headless Firefox via geckodriver), saves-ui-test.html/.mjs (integration driver), saves.test.mjs (Node unit tests), system-hud test updates
- Add sound effects (construct, deconstruct, discovery) with config in data/sfx.json; play on dossier arrival/fold and new-object discovery; silently no-op when disabled or asset missing
- Rework the system dossier into a full lifecycle: open-by-default on arrival, one-shot auto-fold at 10 s, click-to-toggle (name + caret), reverse deconstruction order, state caret that swings with the open/closed state
- Add ScrambleDecode `reverse` mode so lines can deconstruct (value → static → '') for the fold animation
- Reposition the tether readout to bottom-right above the deck's MENU button, right-aligned and bottom-anchored; fall back to left column when the deck is disabled
- Expand decode and system-hud tests to cover reverse decode, the full dossier lifecycle (toggle, auto-fold cancellation, mid-animation guard), tether HUD layout, and SFX wiring
- New `js/utils/Decode.js` with a pure `ScrambleDecode` class plus
`DECODE_CHARS`, `DECODE_DURATION`, and a per-line `decodeDur()` helper,
so one effect serves both scenes.
- MenuScene now delegates its seed scramble to the shared utility instead
of inlining the reveal math and alphabet.
- GameScene's system dossier types itself in on arrival: each line starts
empty and decodes left-to-right, staggered top-to-bottom, with the
timeline anchored on the first update frame; state self-clears once all
lines settle so `update()` pays nothing afterward.
- Add Node dev tests for the shared effect contract (`dev/decode.test.mjs`)
and for the real GameScene dossier decode against actual galaxy/report
data with a stubbed scene (`dev/system-hud.test.mjs`).
- Document both new test commands in the README and update the planets
PSD asset.
- Generate 4–8 rock groups per system with seeded placement, spacing, and slow tumble/drift motion driven by data/asteroids.json
- Add AsteroidCluster entity (Phaser container) with halo, dust motes, per-rock spins, and ship keep-out collision reusing Planet.resolve
- Integrate clusters into GameScene: load spritesheet, constrain ship post-physics, include in click-to-fly/autopilot/discovery/compass
- Synthesise unique cluster names via NameGenerator.asteroid (syllables + field suffix)
- Add dev test suite and ?seed= param for deterministic galaxy generation
- Load `assets/images/ships-player.png` as a Phaser spritesheet via
`data/ship.json` (`texture`, `frameWidth`, `frameHeight`, `frame`)
- Add `artFacing` config; Ship applies a constant `artOffset` so heading
math is unchanged while the frame art (facing north) renders correctly
- Scale sprite as `(size * scale) / frameWidth` to preserve the 46 px world
hull size and collision radius regardless of frame dimensions
- Fall back to the built-in procedural dart when the sheet is missing or
fails to load, with a console warning in GameScene.create()
- Update ship-behavior tests to account for `artOffset` in heading checks
- Add source art (`ships-player.psd`, landed-ship reference) and a demo
video; document the new pattern in README and PROJECT_NOTES
- DiscoveryCompass name tags are now interactive buttons with hover/press feedback; firing onSelect hands the target id to the scene, which retargets the ship via GameScene.autopilotTo (aim at the keep-out rim on the approach side).
- Compass layout reserves the command-deck strip (reserveBottom) so chips/arrows are never buried under the deck.
- Click-to-fly now ignores clicks that land on a compass chip (DiscoveryCompass.contains), keeping autopilot and fly-here from colliding.
- Tests cover the chip hit test, onSelect seam, and reserveBottom clamping; docs updated to describe the new autopilot behavior.
- New ActionBar component (js/ui/ActionBar.js) renders a procedural cyberpunk
bottom bar with six slots (Research, Build, Ship, two reserved, Menu),
boot flicker, rail comet, glitch bursts, and RGB shimmer; fully driven by
data/actionbar.json
- Data contracts for the player's progression loop: research.json (time-based,
one project at a time, gates builds/research) and builds.json (credits +
minerals, ship/planet/station categories), both with _template docs and
empty maps; registered in manifest.json
- GameScene wires the deck in, routes clicks over it away from fly-to, pins
the hint above the bar, and drives per-frame update/shutdown
- New dev/research-builds.test.mjs pins the data contract (manifest entries,
rule knobs, template fields, six-slot order)
- cdpshot.mjs: cross-platform temp dir + --no-sandbox for container/CI
- README and PROJECT_NOTES updated with the new loop, deck docs, and a Phaser
v4 scrollFactor-on-screen-fixed-container quirk
- Allow planet kinds to specify multiple sheet frames and pick one with a seed-derived Rng so the same galaxy always gets the same home world.
- Add Ethnocentric and Centauri font assets.
Adds the player's home planet to the starting system, rendered from the new
planets.png spritesheet. The planet is a solid disc: the ship may come within
shipClearance of its rim but can never cross it, and clicks on the planet
clamp to the keep-out rim so the ship always has a reachable destination.
- js/entities/Planet.js: solid-disc entity with a circle keep-out constraint
(constrainShip/resolve, pure and scene-free) plus edgePoint/aimPoint helpers
- data/planets.json (+ manifest entry): texture, frame size, scale, homePlanet,
shipClearance, spawnDistanceFromEdge
- Ship.js: exposes a radius (half the hull width) for the keep-out math
- GameScene.js: loads the spritesheet, spawns the home world at the origin,
starts the ship ~150 px off its rim in a seed-derived direction, and applies
the constraint each frame after the ship moves
- dev/planet.test.mjs: Node tests of the constraint against real config;
dev/test-game.html gains a <base href> so asset paths resolve from /dev