refactor(mastervega): consolidate HUD buttons into Empire dropdown and fix ghost button styling
- Replace four separate HUD buttons (Research, Diplomacy, Council, Leaders) with a single "Empire" dropdown button, freeing HUD space for future buttons like "Colonies" - Add toggleEmpireMenu/openEmpireMenu/closeEmpireMenu with a popover panel containing the four options, click-outside-to-dismiss, and ▸/▾ indicator on the button label - Fix ghost button glow washout by introducing GHOST_ALPHA (0.85) constant and using it instead of hardcoded 0.3 alpha for ghost button backgrounds - Change ghost button hover text color from glow color to textDark for consistent readability - Remove unused glowHex from SCHEMES config
This commit is contained in:
parent
e1b5ae7277
commit
4c3b17610a
|
|
@ -47,6 +47,17 @@ const SAVE_KEY = 'mastervega-save';
|
|||
const SAVE_SLOT_COUNT = 10;
|
||||
const saveSlotKey = (i) => `mastervega-save-slot-${i}`;
|
||||
|
||||
// The Empire button's own leading open/closed indicator — swapped by
|
||||
// open/closeEmpireMenu, same ▸/▾ convention VegaTurnReportScreen.js uses
|
||||
// per-row, applied here to a button label instead. The button's label is
|
||||
// one centered Phaser Text (VegaButton.js has no separate icon slot), so
|
||||
// the only way to nudge the triangle further from "Empire" without a new
|
||||
// component is padding the string itself — the extra spaces push the
|
||||
// triangle left and "Empire" right by equal amounts as the whole centered
|
||||
// block widens.
|
||||
const EMPIRE_CLOSED_LABEL = '▸ Empire';
|
||||
const EMPIRE_OPEN_LABEL = '▾ Empire';
|
||||
|
||||
export default class MasterOfVegaGame extends Phaser.Scene {
|
||||
constructor() { super('MasterOfVegaGame'); }
|
||||
|
||||
|
|
@ -648,21 +659,18 @@ export default class MasterOfVegaGame extends Phaser.Scene {
|
|||
this.hudText = this.add.text(24, 18, '', { fontFamily: FONT, fontSize: '20px', color: '#cfe8ff' });
|
||||
hud.add(this.hudText);
|
||||
|
||||
const mk = (label, x, fn) => {
|
||||
const b = new Button(this, x, 31, label, this.uiClick(fn), { width: 150, height: 42, fontSize: 18 });
|
||||
hud.add(b);
|
||||
return b;
|
||||
};
|
||||
mk('Research', GAME_WIDTH - 830, () => this.openModal((done) =>
|
||||
openResearchScreen(this, this.rules, this.state, this.state.humanIndex, this.art, done)));
|
||||
mk('Diplomacy', GAME_WIDTH - 670, () => this.openModal((done) =>
|
||||
openDiplomacyScreen(this, this.rules, this.state, this.state.humanIndex, this.art, done,
|
||||
() => this.refreshAll())));
|
||||
mk('Council', GAME_WIDTH - 510, () => this.openModal((done) =>
|
||||
openCouncilScreen(this, this.rules, this.state, done)));
|
||||
mk('Leaders', GAME_WIDTH - 350, () => this.openModal((done) =>
|
||||
openLeaderScreen(this, this.rules, this.state, this.state.humanIndex, this.art, done,
|
||||
() => this.refreshHud())));
|
||||
// Research/Diplomacy/Leaders/Council used to be four separate HUD
|
||||
// buttons; consolidated into one "Empire" dropdown (openEmpireMenu,
|
||||
// near the game menu below) to leave room in the bar for more empire-
|
||||
// management buttons later (Brian's plan: "Colonies" next) without the
|
||||
// row running out of width. Parked immediately left of End Turn, same
|
||||
// width, so the two read as a pair — the leading ▸/▾ (EMPIRE_CLOSED_LABEL/
|
||||
// EMPIRE_OPEN_LABEL, swapped by open/closeEmpireMenu) is this button's
|
||||
// own open/closed indicator, not the same glyph VegaTurnReportScreen.js
|
||||
// uses per-row, just the same visual convention.
|
||||
this.empireBtn = new Button(this, GAME_WIDTH - 340, 31, EMPIRE_CLOSED_LABEL,
|
||||
this.uiClick(() => this.toggleEmpireMenu()), { width: 190, height: 42, fontSize: 18 });
|
||||
hud.add(this.empireBtn);
|
||||
|
||||
// Not this.uiClick — onEndTurn plays its own vega-endturn cue (shared with
|
||||
// combat's Next round button) rather than the generic click, and a no-op
|
||||
|
|
@ -749,6 +757,72 @@ export default class MasterOfVegaGame extends Phaser.Scene {
|
|||
this.gameMenuLayer = null;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------- empire menu
|
||||
|
||||
/** Empire toggles a small popover dropping down from directly below it:
|
||||
* Research, Diplomacy, Leaders, Council, in that order (Brian's ask).
|
||||
* Same shape as the game menu above — click-anywhere-to-dismiss veil,
|
||||
* ghost-variant item buttons — just anchored under the HUD bar instead of
|
||||
* above the ☰ button. Gated by modalOpen like every other HUD button. */
|
||||
toggleEmpireMenu() {
|
||||
if (this.modalOpen) return;
|
||||
if (this.empireMenuLayer) { this.closeEmpireMenu(); return; }
|
||||
this.openEmpireMenu();
|
||||
}
|
||||
|
||||
openEmpireMenu() {
|
||||
this.empireBtn.setLabel(EMPIRE_OPEN_LABEL);
|
||||
const layer = this.add.container(0, 0).setDepth(D.modal);
|
||||
this.empireMenuLayer = layer;
|
||||
|
||||
const catcher = this.add.rectangle(0, 0, GAME_WIDTH, GAME_HEIGHT, 0x000000, 0.001)
|
||||
.setOrigin(0, 0).setInteractive();
|
||||
catcher.on('pointerup', () => this.closeEmpireMenu());
|
||||
layer.add(catcher);
|
||||
|
||||
const items = [
|
||||
['Research', () => this.openModal((done) =>
|
||||
openResearchScreen(this, this.rules, this.state, this.state.humanIndex, this.art, done))],
|
||||
['Diplomacy', () => this.openModal((done) =>
|
||||
openDiplomacyScreen(this, this.rules, this.state, this.state.humanIndex, this.art, done,
|
||||
() => this.refreshAll()))],
|
||||
['Leaders', () => this.openModal((done) =>
|
||||
openLeaderScreen(this, this.rules, this.state, this.state.humanIndex, this.art, done,
|
||||
() => this.refreshHud()))],
|
||||
['Council', () => this.openModal((done) =>
|
||||
openCouncilScreen(this, this.rules, this.state, done))],
|
||||
];
|
||||
|
||||
const BTN_W = 200;
|
||||
const BTN_H = 42;
|
||||
const GAP = 8;
|
||||
const PAD = 14;
|
||||
const panelH = PAD * 2 + items.length * BTN_H + (items.length - 1) * GAP;
|
||||
// Left-aligned under the (190-wide) Empire button, regardless of the
|
||||
// dropdown panel's own width — reads empireBtn's actual width rather
|
||||
// than a second hardcoded constant that could drift if either changes.
|
||||
const panelCx = this.empireBtn.x - this.empireBtn.options.width / 2 + BTN_W / 2;
|
||||
const panelTopY = 31 + 21 + 12; // just below the Empire button (centred at y=31, height 42)
|
||||
const panelCy = panelTopY + panelH / 2;
|
||||
|
||||
layer.add(this.add.rectangle(panelCx, panelCy, BTN_W + PAD * 2, panelH, 0x0b1220, 0.97)
|
||||
.setStrokeStyle(1.5, 0x6fc4ff, 0.55));
|
||||
|
||||
let by = panelCy - panelH / 2 + PAD + BTN_H / 2;
|
||||
for (const [label, fn] of items) {
|
||||
const btn = new Button(this, panelCx, by, label, this.uiClick(() => { this.closeEmpireMenu(); fn(); }),
|
||||
{ width: BTN_W, height: BTN_H, fontSize: 16, variant: 'ghost' });
|
||||
layer.add(btn);
|
||||
by += BTN_H + GAP;
|
||||
}
|
||||
}
|
||||
|
||||
closeEmpireMenu() {
|
||||
this.empireBtn.setLabel(EMPIRE_CLOSED_LABEL);
|
||||
this.empireMenuLayer?.destroy();
|
||||
this.empireMenuLayer = null;
|
||||
}
|
||||
|
||||
// Both destinations auto-save to the single "Resume Game" slot on the way
|
||||
// out, same safety net the old ← Menu button always had — independent of,
|
||||
// and in addition to, the 10 manual slots below.
|
||||
|
|
|
|||
|
|
@ -16,14 +16,24 @@ import * as Phaser from 'phaser';
|
|||
const CHAMFER = 14;
|
||||
|
||||
const SCHEMES = {
|
||||
cyan: { glow: 0x2fe3ff, glowHex: '#2fe3ff', textDark: '#04121e' },
|
||||
magenta: { glow: 0xff2fd0, glowHex: '#ff2fd0', textDark: '#1a0414' },
|
||||
red: { glow: 0xff3355, glowHex: '#ff3355', textDark: '#1a0406' },
|
||||
green: { glow: 0x2bff9e, glowHex: '#2bff9e', textDark: '#04160e' },
|
||||
cyan: { glow: 0x2fe3ff, textDark: '#04121e' },
|
||||
magenta: { glow: 0xff2fd0, textDark: '#1a0414' },
|
||||
red: { glow: 0xff3355, textDark: '#1a0406' },
|
||||
green: { glow: 0x2bff9e, textDark: '#04160e' },
|
||||
};
|
||||
|
||||
const PANEL = 0x0b1220;
|
||||
const TEXT = '#e8f4ff';
|
||||
// Every button carries an always-on postFX glow on its border (see
|
||||
// bgRect.postFX.addGlow below), meant to read as "dark button, glowing
|
||||
// edge." Solid buttons (fill alpha 1) stay dark under that glow because
|
||||
// the opaque fill visually dominates it. Ghost buttons don't have that
|
||||
// luxury — at a low fill alpha there isn't enough opaque dark color to
|
||||
// resist the glow, so the whole button washes out to a flat bright cyan
|
||||
// instead of reading as translucent-with-a-glowing-edge. 0.85 gives ghost
|
||||
// buttons enough opacity to hold their own against the glow while staying
|
||||
// visibly lighter/less final than a solid button.
|
||||
const GHOST_ALPHA = 0.85;
|
||||
|
||||
export class Button extends Phaser.GameObjects.Container {
|
||||
constructor(scene, x, y, label, onClick, options = {}) {
|
||||
|
|
@ -54,7 +64,7 @@ export class Button extends Phaser.GameObjects.Container {
|
|||
this.scanline.setMask(new Phaser.Display.Masks.GeometryMask(scene, this.fxMaskShape));
|
||||
this.brackets = scene.add.graphics().setAlpha(0);
|
||||
|
||||
this._drawBg(bg, isGhost ? 0.3 : 1);
|
||||
this._drawBg(bg, isGhost ? GHOST_ALPHA : 1);
|
||||
|
||||
this.text = scene.add.text(0, 0, label, {
|
||||
fontFamily: '"Julius Sans One"',
|
||||
|
|
@ -88,7 +98,7 @@ export class Button extends Phaser.GameObjects.Container {
|
|||
const { scheme: s, variant: v } = this.options;
|
||||
if (v === 'ghost') {
|
||||
this._drawBg(s.glow, 0.22);
|
||||
this.text.setColor(s.glowHex);
|
||||
this.text.setColor(s.textDark);
|
||||
} else {
|
||||
this._drawBg(s.glow, 1);
|
||||
this.text.setColor(s.textDark);
|
||||
|
|
@ -99,7 +109,7 @@ export class Button extends Phaser.GameObjects.Container {
|
|||
const onOut = () => {
|
||||
if (this._active) return;
|
||||
const { bg: b, textColor: tc, variant: v } = this.options;
|
||||
this._drawBg(b, v === 'ghost' ? 0.3 : 1);
|
||||
this._drawBg(b, v === 'ghost' ? GHOST_ALPHA : 1);
|
||||
this.text.setColor(tc);
|
||||
this._stopScanline();
|
||||
this._drawBrackets(false);
|
||||
|
|
@ -211,7 +221,7 @@ export class Button extends Phaser.GameObjects.Container {
|
|||
this._drawBg(scheme.glow, 1);
|
||||
this.text.setColor(scheme.textDark);
|
||||
} else {
|
||||
this._drawBg(bg, variant === 'ghost' ? 0.3 : 1);
|
||||
this._drawBg(bg, variant === 'ghost' ? GHOST_ALPHA : 1);
|
||||
this.text.setColor(textColor);
|
||||
}
|
||||
return this;
|
||||
|
|
|
|||
Loading…
Reference in New Issue