206 lines
6.3 KiB
JavaScript
206 lines
6.3 KiB
JavaScript
import { GameState } from '../state/GameState.js';
|
|
|
|
const TOP_BAR_HEIGHT = 100;
|
|
const BOTTOM_BAR_HEIGHT = 110;
|
|
const BOTTOM_BAR_Y = 900 - BOTTOM_BAR_HEIGHT;
|
|
|
|
// Box centers (x) for the three top fund displays
|
|
const BOX_WIDTH = 1600 / 3;
|
|
const PLAYER_BOX_X = BOX_WIDTH * 0 + BOX_WIDTH / 2;
|
|
const LORD_BOX_X = BOX_WIDTH * 1 + BOX_WIDTH / 2;
|
|
const SIN_BOX_X = BOX_WIDTH * 2 + BOX_WIDTH / 2;
|
|
const BOX_CENTER_Y = TOP_BAR_HEIGHT / 2;
|
|
|
|
export default class UIScene extends Phaser.Scene {
|
|
constructor() {
|
|
super({ key: 'UIScene' });
|
|
}
|
|
|
|
create() {
|
|
this._buildTopBar();
|
|
this._buildBottomBar();
|
|
this._buildSpinButton();
|
|
this._bindEvents();
|
|
this._updateFundDisplays();
|
|
}
|
|
|
|
_buildTopBar() {
|
|
const g = this.add.graphics();
|
|
|
|
// Background
|
|
g.fillStyle(0x12082a, 1);
|
|
g.fillRect(0, 0, 1600, TOP_BAR_HEIGHT);
|
|
g.lineStyle(2, 0xffd700, 0.8);
|
|
g.strokeRect(0, 0, 1600, TOP_BAR_HEIGHT);
|
|
|
|
// Dividers
|
|
g.lineStyle(1, 0xffd700, 0.3);
|
|
g.beginPath();
|
|
g.moveTo(BOX_WIDTH, 8);
|
|
g.lineTo(BOX_WIDTH, TOP_BAR_HEIGHT - 8);
|
|
g.strokePath();
|
|
g.beginPath();
|
|
g.moveTo(BOX_WIDTH * 2, 8);
|
|
g.lineTo(BOX_WIDTH * 2, TOP_BAR_HEIGHT - 8);
|
|
g.strokePath();
|
|
|
|
// Box labels
|
|
const labelStyle = {
|
|
fontSize: '13px',
|
|
fontFamily: 'Georgia, serif',
|
|
color: '#c8a87e',
|
|
alpha: 0.8
|
|
};
|
|
this.add.text(PLAYER_BOX_X, 14, 'YOUR FUNDS', labelStyle).setOrigin(0.5, 0);
|
|
this.add.text(LORD_BOX_X, 14, 'THE LORD', labelStyle).setOrigin(0.5, 0);
|
|
this.add.text(SIN_BOX_X, 14, 'SIN', labelStyle).setOrigin(0.5, 0);
|
|
|
|
// Fund value texts
|
|
const valueStyle = {
|
|
fontSize: '28px',
|
|
fontFamily: 'Georgia, serif',
|
|
color: '#ffd700',
|
|
stroke: '#2a0a4e',
|
|
strokeThickness: 3
|
|
};
|
|
this.playerText = this.add.text(PLAYER_BOX_X, 55, '$1000', valueStyle).setOrigin(0.5, 0.5);
|
|
this.lordText = this.add.text(LORD_BOX_X, 55, '$0', valueStyle).setOrigin(0.5, 0.5);
|
|
this.sinText = this.add.text(SIN_BOX_X, 55, '$0', { ...valueStyle, color: '#ff4444' }).setOrigin(0.5, 0.5);
|
|
}
|
|
|
|
_buildBottomBar() {
|
|
const g = this.add.graphics();
|
|
|
|
g.fillStyle(0x12082a, 1);
|
|
g.fillRect(0, BOTTOM_BAR_Y, 1600, BOTTOM_BAR_HEIGHT);
|
|
g.lineStyle(2, 0xffd700, 0.8);
|
|
g.strokeRect(0, BOTTOM_BAR_Y, 1600, BOTTOM_BAR_HEIGHT);
|
|
|
|
this.messageText = this.add.text(700, BOTTOM_BAR_Y + BOTTOM_BAR_HEIGHT / 2, 'Press SPIN or SPACE to begin', {
|
|
fontSize: '22px',
|
|
fontFamily: 'Georgia, serif',
|
|
color: '#e8d8b0',
|
|
align: 'center',
|
|
wordWrap: { width: 1100 }
|
|
}).setOrigin(0.5, 0.5);
|
|
|
|
// Secondary redemption message (hidden by default)
|
|
this.redeemText = this.add.text(700, BOTTOM_BAR_Y + BOTTOM_BAR_HEIGHT / 2 + 30, '', {
|
|
fontSize: '16px',
|
|
fontFamily: 'Georgia, serif',
|
|
color: '#ff9944',
|
|
align: 'center'
|
|
}).setOrigin(0.5, 0.5).setAlpha(0);
|
|
}
|
|
|
|
_buildSpinButton() {
|
|
const btnX = 1420;
|
|
const btnY = BOTTOM_BAR_Y + BOTTOM_BAR_HEIGHT / 2;
|
|
const btnW = 140;
|
|
const btnH = 60;
|
|
|
|
this.spinBtnGfx = this.add.graphics();
|
|
this._drawSpinBtn(false);
|
|
|
|
this.spinBtnHitArea = this.add.zone(btnX, btnY, btnW, btnH)
|
|
.setInteractive({ useHandCursor: true });
|
|
|
|
this.spinBtnLabel = this.add.text(btnX, btnY, 'SPIN', {
|
|
fontSize: '26px',
|
|
fontFamily: 'Georgia, serif',
|
|
color: '#1a0a2e',
|
|
fontStyle: 'bold'
|
|
}).setOrigin(0.5, 0.5);
|
|
|
|
this.spinBtnHitArea.on('pointerdown', () => {
|
|
this.game.events.emit('spin');
|
|
});
|
|
|
|
this.spinBtnHitArea.on('pointerover', () => this._drawSpinBtn(true));
|
|
this.spinBtnHitArea.on('pointerout', () => this._drawSpinBtn(false));
|
|
|
|
// Store button center for layout reference
|
|
this._btnX = btnX;
|
|
this._btnY = btnY;
|
|
this._btnW = btnW;
|
|
this._btnH = btnH;
|
|
}
|
|
|
|
_drawSpinBtn(hover) {
|
|
const btnX = 1420;
|
|
const btnY = BOTTOM_BAR_Y + BOTTOM_BAR_HEIGHT / 2;
|
|
const btnW = 140;
|
|
const btnH = 60;
|
|
|
|
this.spinBtnGfx.clear();
|
|
this.spinBtnGfx.fillStyle(hover ? 0xffe066 : 0xffd700, 1);
|
|
this.spinBtnGfx.fillRoundedRect(btnX - btnW / 2, btnY - btnH / 2, btnW, btnH, 12);
|
|
this.spinBtnGfx.lineStyle(3, hover ? 0xffa500 : 0xc8a000, 1);
|
|
this.spinBtnGfx.strokeRoundedRect(btnX - btnW / 2, btnY - btnH / 2, btnW, btnH, 12);
|
|
}
|
|
|
|
_bindEvents() {
|
|
this.game.events.on('win', ({ playerGain, lordGain, symbol }) => {
|
|
this._updateFundDisplays();
|
|
this.messageText.setText(
|
|
`✝ The Blessings of the Slots ✝\n+$${playerGain} to you | +$${lordGain} to The Lord (${symbol.label})`
|
|
);
|
|
this.messageText.setColor('#ffd700');
|
|
this.redeemText.setAlpha(0);
|
|
}, this);
|
|
|
|
this.game.events.on('loss', () => {
|
|
this._updateFundDisplays();
|
|
this.messageText.setText('Thou Hath Sinned.');
|
|
this.messageText.setColor('#ff6666');
|
|
|
|
this.redeemText.setText('Redeem Yourself!');
|
|
this.redeemText.setAlpha(1);
|
|
|
|
// Pulse the redeem text
|
|
this.tweens.add({
|
|
targets: this.redeemText,
|
|
alpha: { from: 1, to: 0.3 },
|
|
duration: 700,
|
|
yoyo: true,
|
|
repeat: 4
|
|
});
|
|
}, this);
|
|
|
|
this.game.events.on('spin-complete', () => {
|
|
// Re-enable button visually (it was never disabled, just state-guarded)
|
|
}, this);
|
|
|
|
this.game.events.on('funds-updated', () => {
|
|
this._updateFundDisplays();
|
|
}, this);
|
|
|
|
this.game.events.on('insufficient-funds', () => {
|
|
this.messageText.setText('Insufficient funds to spin! You have been consumed by Sin.');
|
|
this.messageText.setColor('#ff4444');
|
|
}, this);
|
|
}
|
|
|
|
_updateFundDisplays() {
|
|
this.playerText.setText(`$${GameState.playerFunds}`);
|
|
this.lordText.setText(`$${GameState.lordFunds}`);
|
|
this.sinText.setText(`$${GameState.sinTotal}`);
|
|
|
|
// Flash update on change
|
|
[this.playerText, this.lordText, this.sinText].forEach(t => {
|
|
this.tweens.add({
|
|
targets: t,
|
|
scaleX: { from: 1.15, to: 1 },
|
|
scaleY: { from: 1.15, to: 1 },
|
|
duration: 200,
|
|
ease: 'Bounce.easeOut'
|
|
});
|
|
});
|
|
}
|
|
|
|
// Called by GameScene to position animations toward the right box
|
|
getPlayerBoxCenter() { return { x: PLAYER_BOX_X, y: BOX_CENTER_Y }; }
|
|
getLordBoxCenter() { return { x: LORD_BOX_X, y: BOX_CENTER_Y }; }
|
|
getSinBoxCenter() { return { x: SIN_BOX_X, y: BOX_CENTER_Y }; }
|
|
}
|