diff --git a/js/scenes/SurfaceScene.js b/js/scenes/SurfaceScene.js index 7ee5bc7..3c1ac69 100644 --- a/js/scenes/SurfaceScene.js +++ b/js/scenes/SurfaceScene.js @@ -1185,6 +1185,12 @@ export class SurfaceScene extends Phaser.Scene { // ledger's capitalLanded, 'Special Delivery' step 1) sits on the // shop's Buy/Sell tab (ShopView reads it from the GameScene's ledger, // not this scene, so a save/load can't bypass it). + // The EXPLORE slot stays in deck-locked standby until the 'Special + // Delivery' story quest has begun (granted — the bar's scene is the + // quest's step 1, so the drawer that reaches it waits for the quest; + // the same shared-ledger seam as isTradeUnlocked, so a loaded save + // reads the same truth). + this._syncExploreLock(); // The MENU button keeps the GameScene's save system. The run's state // is in the paused GameScene beneath us — SavePanel captures from it @@ -1362,6 +1368,15 @@ export class SurfaceScene extends Phaser.Scene { return; } if (id === 'explore') { + // The story gate (data/quests.json → special_delivery): the slot is + // deck-locked until the quest is granted, and this check is the + // second door — a press that sneaks past the standby paint still + // lands here and is refused in the console's voice. + if (!this._exploreUnlocked()) { + this.consoleNote('EXPLORATION OFFLINE — THE STORY COMES FIRST'); + this.playSfx('ui_close'); + return; + } this.toggleExplore(); return; } @@ -1393,6 +1408,27 @@ export class SurfaceScene extends Phaser.Scene { this.menuSubBar.open(); } + /** + * Is the EXPLORE slot's story gate open? The 'Special Delivery' quest + * (data/quests.json → special_delivery) granted — QuestState.isGranted + * on the shared registry ledger (the isTradeUnlocked seam), so a + * loaded save reads the same truth as a live run. + */ + _exploreUnlocked() { + return this.gameScene?.questState?.isGranted?.('special_delivery') === true; + } + + /** + * Apply the EXPLORE slot's story gate to the deck — standby until the + * 'Special Delivery' quest has begun, live from its grant. Idempotent + * (setSlotEnabled's contract), safe to re-run on a surface claim (the + * grant chain lands on claimQuest, and the surface's QUESTS slot can + * be the one that pays out 'Leaving the Nest'). + */ + _syncExploreLock() { + this.actionBar?.setSlotEnabled?.('explore', this._exploreUnlocked()); + } + /** * The EXPLORE button: fold the full-width service drawer up / fold it * back down (the shop tab bar's span, the same MenuSubBar drawer in @@ -1518,6 +1554,7 @@ export class SurfaceScene extends Phaser.Scene { isSide ? gid.sideQuestState?.isClaimed?.(id) ?? false : gid.questState?.isClaimed?.(id) ?? false; const before = claimedOf(g); g.claimQuest(id); // the scene's seam (the side dispatch for sq- ids) + this._syncExploreLock(); // the claim chain can GRANT 'Special Delivery' — lift the EXPLORE gate const now = claimedOf(g); if (!before && now) { const t = isSide ? config.get('sidequests.ui.claimToast', {}) : config.get('quests.claimToast', {});