Virtue-Slots/scenes/GameScene.js

190 lines
6.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { GameState } from '../state/GameState.js';
import { SlotMachine } from '../objects/SlotMachine.js';
import { WinAnimation } from '../objects/WinAnimation.js';
import { LossAnimation } from '../objects/LossAnimation.js';
import { VialDisplay } from '../objects/VialDisplay.js';
export default class GameScene extends Phaser.Scene {
constructor() {
super({ key: 'GameScene' });
}
create() {
// Background gradient-ish
const bg = this.add.graphics();
bg.fillGradientStyle(0x1a0a2e, 0x1a0a2e, 0x2a0a4e, 0x2a0a4e, 1);
bg.fillRect(0, 0, 1600, 900);
// Decorative stars — floating, twinkling, pulsing
const starColors = [0xffffff, 0xffffff, 0xffffff, 0xffe8a0, 0xd0c8ff];
for (let i = 0; i < 70; i++) {
const x = Phaser.Math.Between(0, 1600);
const y = Phaser.Math.Between(120, 760);
const r = Phaser.Math.Between(1, 3);
const baseAlpha = Phaser.Math.FloatBetween(0.2, 0.75);
const color = starColors[Math.floor(Math.random() * starColors.length)];
const starGfx = this.add.graphics();
starGfx.fillStyle(color, 1);
starGfx.fillCircle(0, 0, r);
starGfx.setPosition(x, y);
starGfx.setAlpha(baseAlpha);
// Gentle drift — each star wanders a small random distance
this.tweens.add({
targets: starGfx,
x: x + Phaser.Math.Between(-18, 18),
y: y + Phaser.Math.Between(-12, 12),
duration: Phaser.Math.Between(3500, 8000),
yoyo: true,
repeat: -1,
ease: 'Sine.easeInOut',
delay: Phaser.Math.Between(0, 5000),
});
// Twinkle — alpha fades in and out independently
this.tweens.add({
targets: starGfx,
alpha: { from: baseAlpha * 0.15, to: baseAlpha },
duration: Phaser.Math.Between(600, 2800),
yoyo: true,
repeat: -1,
ease: 'Sine.easeInOut',
delay: Phaser.Math.Between(0, 3000),
});
// Scale pulse — grows and shrinks on its own rhythm
this.tweens.add({
targets: starGfx,
scale: Phaser.Math.FloatBetween(1.4, 3.2),
duration: Phaser.Math.Between(1200, 4500),
yoyo: true,
repeat: -1,
ease: 'Sine.easeInOut',
delay: Phaser.Math.Between(0, 4000),
});
}
// Title above the machine
this.add.text(710, 150, 'VIRTUE SLOTS', {
fontSize: '42px',
fontFamily: 'Georgia, serif',
color: '#ffd700',
stroke: '#5a3000',
strokeThickness: 4,
shadow: { offsetX: 2, offsetY: 2, color: '#000', blur: 6, fill: true }
}).setOrigin(0.5, 0.5);
this.add.text(710, 195, '✝ May Fortune Favor the Faithful ✝', {
fontSize: '18px',
fontFamily: 'Georgia, serif',
color: '#c8a87e',
alpha: 0.8
}).setOrigin(0.5, 0.5);
// Slot machine at center of play area
this.slotMachine = new SlotMachine(this, 710, 490);
this.winAnim = new WinAnimation();
this.lossAnim = new LossAnimation();
// ── Right section: The Reckoning vials ──────────────────────────────────
// Panel vertically centered in play area (y=100790), with ~87px right margin
const sectionBg = this.add.graphics();
sectionBg.fillStyle(0x0c0620, 0.65);
sectionBg.fillRoundedRect(1085, 202, 428, 488, 14);
sectionBg.lineStyle(1, 0xffd700, 0.3);
sectionBg.strokeRoundedRect(1085, 202, 428, 488, 14);
this.add.text(1299, 220, 'THE RECKONING', {
fontSize: '13px', fontFamily: 'Georgia, serif',
color: '#c8a87e', letterSpacing: 5,
}).setOrigin(0.5, 0.5);
this.add.text(1299, 238, 'First to $2,000 wins', {
fontSize: '10px', fontFamily: 'Georgia, serif', color: '#4a5a6a',
}).setOrigin(0.5, 0.5);
this.add.text(1295, 443, 'VS', {
fontSize: '20px', fontFamily: 'Georgia, serif', fontStyle: 'bold',
color: '#2a1a4a', stroke: '#000000', strokeThickness: 3,
}).setOrigin(0.5, 0.5);
this.lordVial = new VialDisplay(this, 1193, 258, 'The Lord', 0xffd700, 0xc8a87e);
this.sinVial = new VialDisplay(this, 1398, 258, 'Sin', 0xff4444, 0xff6666);
// Keyboard: Space to spin
this.input.keyboard.on('keydown-SPACE', () => this._triggerSpin());
// Listen for spin button events from UIScene via global event bus
this.game.events.on('spin', () => this._triggerSpin(), this);
}
_triggerSpin() {
if (GameState.spinning) return;
if (GameState.playerFunds < GameState.spinCost) {
this.game.events.emit('insufficient-funds');
return;
}
GameState.playerFunds -= GameState.spinCost;
GameState.spinning = true;
this.game.events.emit('funds-updated');
this.slotMachine.spin((result) => this._handleResult(result));
}
_handleResult({ win, symbols, payout }) {
if (win) {
const playerGain = Math.round(payout * 0.6);
const lordGain = payout - playerGain;
this.game.events.emit('win', { playerGain, lordGain, symbol: symbols[0] });
// Resolve UI box positions from UIScene
const uiScene = this.scene.get('UIScene');
const playerBox = uiScene ? uiScene.getPlayerBoxCenter() : { x: 267, y: 60 };
const lordBox = uiScene ? uiScene.getLordBoxCenter() : { x: 800, y: 60 };
this.winAnim.play(
this,
this.slotMachine.getCenterX(),
this.slotMachine.getCenterY(),
playerBox,
lordBox,
symbols[0],
() => {
GameState.playerFunds += playerGain;
GameState.lordFunds += lordGain;
this.game.events.emit('funds-updated');
this.lordVial.animateUpdate(GameState.lordFunds, lordBox.x, 115, () => {
GameState.spinning = false;
this.game.events.emit('spin-complete');
});
}
);
} else {
this.game.events.emit('loss', { sinAdded: GameState.spinCost });
const uiScene = this.scene.get('UIScene');
const sinBox = uiScene ? uiScene.getSinBoxCenter() : { x: 1333, y: 60 };
this.lossAnim.play(
uiScene || this,
this.slotMachine.getCenterX(),
this.slotMachine.getCenterY(),
sinBox,
() => {
GameState.sinTotal += GameState.spinCost;
this.game.events.emit('funds-updated');
this.sinVial.animateUpdate(GameState.sinTotal, sinBox.x, 115, () => {
GameState.spinning = false;
this.game.events.emit('spin-complete');
});
}
);
}
}
}