import Phaser from '../vendor/phaser.js'; import { config } from '../config/Config.js'; import { ActionBar } from '../ui/ActionBar.js'; import { MenuSubBar } from '../ui/MenuSubBar.js'; import { SavePanel } from '../ui/SavePanel.js'; import { SaveManager } from '../save/SaveManager.js'; /** * SURFACE — the planet's surface, started ON TOP of the sleeping * GameScene (GameScene.startLanding → launch('SurfaceScene') + sleep): * * 1. LANDING — the full-screen one-shot clip (data/landing.json → * videos[planets.png frame] → `land`), cover-scaled over an opaque * plate. No UI during the flight down. * 2. SURFACE — the clip is swapped for the looping world clip (`surface`), * and the command deck appears: the GameScene deck with Research * replaced by SHOP (a seam for now) and TAKE OFF left of MENU * (data/actionbar.json → surface.buttons). Menu keeps the GameScene's * save sub-bar — Save/Load operate on the paused GameScene's state * (SavePanel `stateScene`). * * TAKE OFF stops this scene — the paused GameScene resumes exactly * where it was (ship, tethers, discovery, standing, everything). * * Video selection is by the planet's planets.png SHEET FRAME (the same * index its sprite uses — frames 0..2 the terran worlds, 3..5 the gas * giants, per data/planets.json → frames), so a planet always lands with * the clip matching the art it is drawn with. Missing clips fall back to * the existing stubs in data/landing.json; a broken/missing file skips * straight to the surface instead of stranding the player. */ export class SurfaceScene extends Phaser.Scene { constructor() { super('SurfaceScene'); } init(data = {}) { this.planetFrame = Number(data.frame ?? 0); // planets.png frame (video key) this.planetName = String(data.name ?? ''); this.phase = 'landing'; // 'landing' → 'surface' this.gameScene = null; // the paused GameScene beneath us (save state lives there) this.backdrop = null; this.landVideo = null; this.surfaceVideo = null; this.actionBar = null; this.menuSubBar = null; this.savePanel = null; this.saveManager = null; this.landKey = null; this.surfaceKey = null; } /** Pick this world's clips (data/landing.json) and queue them. */ preload() { const entry = (config.get('landing.videos') ?? [])[this.planetFrame] ?? {}; this.landKey = `__surf_land_${this.planetFrame}`; this.surfaceKey = `__surf_loop_${this.planetFrame}`; this.landUrl = this.resolveUrl(entry.land ?? null); this.surfaceUrl = this.resolveUrl(entry.surface ?? null); if (this.landUrl) this.load.video(this.landKey, this.landUrl); if (this.surfaceUrl) this.load.video(this.surfaceKey, this.surfaceUrl); } create() { // The run's state (ship, tethers, discovery, …) stays parked in the // paused GameScene — the save panel reads/writes it through there. this.gameScene = this.scene.get('GameScene') ?? null; const W = this.scale.width; const H = this.scale.height; // Launched over the flight world — the first thing painted must be an // opaque full-screen plate, or GameScene bleeds through the seams. this.backdrop = this.add.rectangle(W / 2, H / 2, W, H, 0x04060d).setScrollFactor(0).setDepth(0); if (this.hasVideo(this.landKey)) { this.playLanding(); } else { this.startSurface(); // no landing clip for this frame — straight to the deck } } // ------------------------------------------------------------------ // Stage 1 — the one-shot landing clip // ------------------------------------------------------------------ playLanding() { // v4: add.video(x, y, key) — the key is the LAST argument (it loads the // cached clip and attaches the