From fdd0b4900c2c741a62cb86bc063480ed2bdf2574 Mon Sep 17 00:00:00 2001 From: Brian Fertig Date: Sun, 27 Sep 2026 10:49:34 -0600 Subject: [PATCH] Fix skill window close button racing with fly-here handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Stamp `closedByButtonAt` in `SkillWindow.close()` so the scene can tell a console ✕ click apart from a world click on the same frame. - Add a 20ms guard in `GameScene` that suppresses the fly-here action when the close just fired, matching the mining menu and comms panel contract. --- js/scenes/GameScene.js | 12 ++++++++++++ js/ui/SkillWindow.js | 2 ++ 2 files changed, 14 insertions(+) diff --git a/js/scenes/GameScene.js b/js/scenes/GameScene.js index df435e9..800a184 100644 --- a/js/scenes/GameScene.js +++ b/js/scenes/GameScene.js @@ -1990,6 +1990,18 @@ export class GameScene extends Phaser.Scene { if (this.shipWindow && this.shipWindow.isOpen) return; // The skill tree console (depth 80) — the same full-screen contract. if (this.skillWindow && this.skillWindow.isOpen) return; + // A click the console's OWN ✕ just handled (the two handlers race on + // event order — close() lands first, so the guard above can miss it): + // it is the console's click, never a fly-here. Same-frame test, the + // mining menu's and the comms panel's contract. The console is full- + // screen, so no footprint check — any click that frame is its own. + if ( + this.skillWindow && + this.skillWindow.closedByButtonAt !== null && + this.time.now - this.skillWindow.closedByButtonAt < 20 + ) { + return; + } // Sub-bar OPEN: a click INSIDE it is its own (the panel or one of // its buttons — the buttons fire on their own pointerdown). ANY // click OUTSIDE — world, deck, HUD, compass — folds it back down diff --git a/js/ui/SkillWindow.js b/js/ui/SkillWindow.js index 2a2b5be..70de473 100644 --- a/js/ui/SkillWindow.js +++ b/js/ui/SkillWindow.js @@ -127,6 +127,7 @@ export class SkillWindow extends Phaser.GameObjects.Container { this.onClearSeat = typeof o.onClearSeat === 'function' ? o.onClearSeat : null; this.onClose = typeof o.onClose === 'function' ? o.onClose : null; this.spec = windowSpec(); + this.closedByButtonAt = null; // stamped when a BUTTON fires (race guard, MiningPopup's contract) this._hits = []; // every button's rect (see button()); reset on each rebuild this.openState = 'closed'; this._sig = ''; @@ -166,6 +167,7 @@ export class SkillWindow extends Phaser.GameObjects.Container { /** Closing is COMMITTING: the scene signs the session off here (see GameScene). */ close(o = {}) { if (!this.isOpen) return; + this.closedByButtonAt = this.scene.time.now; // this click is the console's (the scene's guard reconciles) this.openState = 'closed'; this.setVisible(false); this._disarmInput();