123 lines
3.9 KiB
JavaScript
123 lines
3.9 KiB
JavaScript
import Phaser from '../vendor/phaser.js';
|
|
import { config } from '../config/Config.js';
|
|
import { toColor, toCss } from '../utils/Color.js';
|
|
import { fontStack } from '../utils/Theme.js';
|
|
|
|
/**
|
|
* CREDITS HUD — the trade-currency readout, UPPER-RIGHT, stacked ABOVE the
|
|
* minerals HUD (data/creditshud.json). One right-aligned line:
|
|
*
|
|
* CREDITS 1,250
|
|
*
|
|
* `set(n)` counts the number up when credits land (a single tween, gen-token
|
|
* guarded so quick successive updates can't snap it back). Gold (theme 'amber')
|
|
* reads as value and stays distinct from the cyan minerals line below it.
|
|
*
|
|
* The scene stacks the two readouts: it builds this first (top), then builds
|
|
* the MineralHud with `topOffset = creditsHud.height + creditsHud.gap`, so
|
|
* the comms box (which anchors to the minerals bar's bottom) keeps clear of
|
|
* both.
|
|
*
|
|
* Component usage (scenes own one instance, like MineralHud):
|
|
*
|
|
* this.creditsHud = new CreditsHud(this);
|
|
* this.creditsHud.set(ship.credits);
|
|
* // …whenever credits change:
|
|
* this.creditsHud.set(ship.credits);
|
|
* shutdown() { this.creditsHud.destroy(); }
|
|
*/
|
|
export class CreditsHud extends Phaser.GameObjects.Container {
|
|
/** @param {Phaser.Scene} scene */
|
|
constructor(scene) {
|
|
super(scene, 0, 0);
|
|
this.scene.add.existing(this);
|
|
this.setScrollFactor(0); // UI — pinned to the screen, not the world
|
|
this.setDepth(30); // with the minerals HUD and the other corner readouts
|
|
|
|
const hud = config.section('creditshud', {});
|
|
this.pad = hud.pad ?? 16;
|
|
this.gap = Math.max(0, hud.gap ?? 8); // space the scene leaves below this line
|
|
const lab = hud.label ?? {};
|
|
const cu = hud.countUp ?? {};
|
|
this.countUpMs = Math.max(0, cu.durationMs ?? 420);
|
|
this.countUpEase = cu.ease ?? 'Sine.easeOut';
|
|
this.int = toColor(hud.color, 0xffc94d);
|
|
|
|
const W = scene.scale.width;
|
|
const right = W - this.pad;
|
|
const fam = fontStack('body');
|
|
|
|
this.label = scene.add
|
|
.text(0, 0, '', {
|
|
fontFamily: fam,
|
|
fontSize: `${lab.fontSize ?? 12}px`,
|
|
color: toCss(this.int),
|
|
letterSpacing: lab.letterSpacing ?? 1,
|
|
})
|
|
.setOrigin(1, 0) // right edge sticks to the corner margin
|
|
.setDepth(30)
|
|
.setScrollFactor(0);
|
|
this.label.setPosition(right, this.pad);
|
|
|
|
// State: `value` is settled (where the animation lands). A set() with the
|
|
// same value is a no-op (no re-count).
|
|
this.value = 0;
|
|
this._gen = 0; // supersede guard: a new set() voids any in-flight tween
|
|
|
|
this.label.setText(this.format(0));
|
|
}
|
|
|
|
/** The rendered line height (the scene uses this to stack the minerals HUD). */
|
|
get height() {
|
|
return this.label.height;
|
|
}
|
|
|
|
/** The label text for a settled value. */
|
|
format(n) {
|
|
return `CREDITS ${Math.max(0, Math.round(n)).toLocaleString('en-US')}`;
|
|
}
|
|
|
|
/**
|
|
* Set the credit ledger: `n` credits aboard. Counts the number up from the
|
|
* last settled value (so it always reads from where the player last saw it).
|
|
* A no-op when unchanged.
|
|
*
|
|
* @returns {boolean} true when the value actually changed
|
|
*/
|
|
set(n) {
|
|
const v = Math.max(0, Math.round(Number(n) || 0));
|
|
if (v === this.value) return false;
|
|
const from = this.value;
|
|
this.value = v;
|
|
if (this.countUpMs <= 0) {
|
|
this.label.setText(this.format(v));
|
|
return true;
|
|
}
|
|
const gen = ++this._gen;
|
|
const live = () => gen === this._gen;
|
|
const holder = { v: from };
|
|
this.scene.tweens.add({
|
|
targets: holder,
|
|
v,
|
|
duration: this.countUpMs,
|
|
ease: this.countUpEase,
|
|
onUpdate: () => {
|
|
if (!live()) return;
|
|
this.label.setText(this.format(holder.v));
|
|
},
|
|
onComplete: () => {
|
|
if (!live()) return;
|
|
this.label.setText(this.format(v)); // land exactly, whatever the clock did
|
|
},
|
|
});
|
|
return true;
|
|
}
|
|
|
|
/** Tear the readout down (scene shutdown). */
|
|
destroy() {
|
|
this.label?.destroy();
|
|
this.label = null;
|
|
super.destroy();
|
|
}
|
|
}
|