diff --git a/assets/images/originals/ss-01-int-01.png b/assets/images/originals/ss-01-int-01.png new file mode 100644 index 0000000..da4c6dd Binary files /dev/null and b/assets/images/originals/ss-01-int-01.png differ diff --git a/assets/images/originals/ss-01-int-02.png b/assets/images/originals/ss-01-int-02.png new file mode 100644 index 0000000..4060258 Binary files /dev/null and b/assets/images/originals/ss-01-int-02.png differ diff --git a/assets/images/originals/ss-01-int-03.png b/assets/images/originals/ss-01-int-03.png new file mode 100644 index 0000000..ab0f07a Binary files /dev/null and b/assets/images/originals/ss-01-int-03.png differ diff --git a/data/actionbar.json b/data/actionbar.json index 872e6e4..fafcbd2 100644 --- a/data/actionbar.json +++ b/data/actionbar.json @@ -1,5 +1,5 @@ { - "_comment": "COMMAND DECK — the cyberpunk bar across the bottom of the screen (rendered by js/ui/ActionBar.js). Seven evenly spaced slots: `buttons` is the ORDERED list — give a slot an `id` (the action it will fire) + `label` + `accent`, or leave both `null` for a reserved slot. Buttons currently fire `onAction` (see GameScene); the behavior behind them comes next. Everything visual is procedural (no image assets) and tunable here: layout in `height`/`margin`/`padding`/`button`, palette in `colors`, CRT scanlines in `scanline`, motion in `animation` (boot flicker, the rail comet, the glitch bursts, the RGB pull-apart).", + "_comment": "COMMAND DECK — the cyberpunk bar across the bottom of the screen (rendered by js/ui/ActionBar.js). Six evenly spaced slots: `buttons` is the ORDERED list — give a slot an `id` (the action it will fire) + `label` + `accent`, or leave both `null` for a reserved slot. Buttons currently fire `onAction` (see GameScene); the behavior behind them comes next. Everything visual is procedural (no image assets) and tunable here: layout in `height`/`margin`/`padding`/`button`, palette in `colors`, CRT scanlines in `scanline`, motion in `animation` (boot flicker, the rail comet, the glitch bursts, the RGB pull-apart).", "enabled": true, "height": 56, "margin": { "left": 20, "right": 20, "bottom": 12 }, @@ -17,7 +17,6 @@ { "id": "ship", "label": "Ship", "accent": "#7ce8a4" }, { "id": "quests", "label": "Quests", "accent": "#ffc94d" }, { "id": "map", "label": "Map", "accent": "#c084fc" }, - { "id": null, "label": null }, { "id": "menu", "label": "Menu", "accent": "#ff2d6f" } ], "surface": { @@ -27,7 +26,6 @@ { "id": "build", "label": "Build", "accent": "#ffc94d" }, { "id": "ship", "label": "Ship", "accent": "#7ce8a4" }, { "id": "quests", "label": "Quests", "accent": "#ffc94d" }, - { "id": null, "label": null }, { "id": "takeoff", "label": "Take Off", "accent": "#7ce8a4" }, { "id": "menu", "label": "Menu", "accent": "#ff2d6f" } ] diff --git a/dev/research-builds.test.mjs b/dev/research-builds.test.mjs index 1ee96ea..01cd965 100644 --- a/dev/research-builds.test.mjs +++ b/dev/research-builds.test.mjs @@ -284,27 +284,27 @@ check('builds._template.repeatable is a boolean', typeof bt.repeatable === 'bool check('builds._template.requires is an array', Array.isArray(bt.requires)); // ---------------------------------------------------------------------- -// 6. Command deck — seven evenly spaced slots, right order -// (Quests joined the flight deck to the right of Ship — the +// 6. Command deck — six evenly spaced slots, right order +// (Quests sits on the flight deck to the right of Ship — the // mission dossier; the surface deck carries the same slot.) // ---------------------------------------------------------------------- const slots = actionbar.buttons ?? []; -check('actionbar: exactly seven slots', slots.length === 7); -check('actionbar: slot ids in order (Research, Scan, Ship, Quests, Map, ·, Menu)', JSON.stringify(slots.map((s) => s.id)) === JSON.stringify(['research', 'scan', 'ship', 'quests', 'map', null, 'menu'])); -check('actionbar: labels (Research / Scan / Ship / Quests / Map / · / Menu)', JSON.stringify(slots.map((s) => s.label)) === JSON.stringify(['Research', 'Scan', 'Ship', 'Quests', 'Map', null, 'Menu'])); +check('actionbar: exactly six slots', slots.length === 6); +check('actionbar: slot ids in order (Research, Scan, Ship, Quests, Map, Menu)', JSON.stringify(slots.map((s) => s.id)) === JSON.stringify(['research', 'scan', 'ship', 'quests', 'map', 'menu'])); +check('actionbar: labels (Research / Scan / Ship / Quests / Map / Menu)', JSON.stringify(slots.map((s) => s.label)) === JSON.stringify(['Research', 'Scan', 'Ship', 'Quests', 'Map', 'Menu'])); check('actionbar: the QUESTS slot sits right of the SHIP slot', (() => { const iShip = slots.findIndex((s) => s.id === 'ship'); return iShip >= 0 && slots[iShip + 1]?.id === 'quests'; })()); check('actionbar: the QUESTS slot carries a label + accent', (() => { const q = slots.find((s) => s.id === 'quests'); return q && typeof q.label === 'string' && hex.test(q.accent ?? ''); })()); check('actionbar: the MAP slot sits right of the QUESTS slot', (() => { const iQ = slots.findIndex((s) => s.id === 'quests'); return iQ >= 0 && slots[iQ + 1]?.id === 'map'; })()); check('actionbar: live slots carry hex accents', slots.filter((s) => s.id).every((s) => hex.test(s.accent ?? ''))); -check('actionbar: reserved slots stay null', slots.filter((s) => s.id === null).every((s) => s.label === null)); +check('actionbar: no reserved (empty) slots — every slot is a button', slots.every((s) => s.id !== null && typeof s.label === 'string')); check('actionbar: CRT scanlines configured (pitch + alpha)', typeof actionbar.scanline?.pitch === 'number' && typeof actionbar.scanline?.alpha === 'number'); check('actionbar: RGB pull-apart configured (offsets + alphas)', typeof actionbar.animation?.rgb?.idleOffset === 'number' && typeof actionbar.animation?.rgb?.burstOffset === 'number' && typeof actionbar.animation?.rgb?.idleAlpha === 'number' && typeof actionbar.animation?.rgb?.burstAlpha === 'number'); check('actionbar: sane geometry (height 40–200 px)', typeof actionbar.height === 'number' && actionbar.height > 40 && actionbar.height < 200); // the surface deck (SurfaceScene) — Shop for Research, Build, Take Off const sSlots = actionbar.surface?.buttons ?? []; -check('actionbar: surface deck has seven slots', sSlots.length === 7); -check('actionbar: surface slot ids in order (Shop, Build, Ship, Quests, ·, Take Off, Menu)', JSON.stringify(sSlots.map((s) => s.id)) === JSON.stringify(['shop', 'build', 'ship', 'quests', null, 'takeoff', 'menu'])); +check('actionbar: surface deck has six slots', sSlots.length === 6); +check('actionbar: surface slot ids in order (Shop, Build, Ship, Quests, Take Off, Menu)', JSON.stringify(sSlots.map((s) => s.id)) === JSON.stringify(['shop', 'build', 'ship', 'quests', 'takeoff', 'menu'])); check('actionbar: the QUESTS slot is on the surface deck (right of SHIP)', (() => { const iShip = sSlots.findIndex((s) => s.id === 'ship'); return iShip >= 0 && sSlots[iShip + 1]?.id === 'quests'; })()); check('actionbar: the BUILD slot is on the surface deck', sSlots.some((s) => s.id === 'build')); check('actionbar: the BUILD slot carries a label + accent', (() => { const b = sSlots.find((s) => s.id === 'build'); return b && typeof b.label === 'string' && hex.test(b.accent ?? ''); })()); diff --git a/js/scenes/GameScene.js b/js/scenes/GameScene.js index 8610ce9..ab4bb89 100644 --- a/js/scenes/GameScene.js +++ b/js/scenes/GameScene.js @@ -620,9 +620,10 @@ export class GameScene extends Phaser.Scene { // The command deck — the cyberpunk action bar across the bottom of the // screen (config: data/actionbar.json). Six evenly spaced slots: - // Research, Scan, Ship, two reserved, Menu. The slots are the seams + // Research, Scan, Ship, Quests, Map, Menu. The slots are the seams // for the player's loop — research (time-based, one at a time, see - // data/research.json) and scan (its behavior and panel come next). + // data/research.json), scan, the ship, the quest dossier (see + // data/quests.json) and the star map. this.actionBar = deckEnabled ? new ActionBar(this, { onAction: (id) => this.deckAction(id),