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(800, 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(800, 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, 800, 490); this.winAnim = new WinAnimation(); this.lossAnim = new LossAnimation(); // ── Right section: The Reckoning vials ────────────────────────────────── const sectionBg = this.add.graphics(); sectionBg.fillStyle(0x0c0620, 0.65); sectionBg.fillRoundedRect(1157, 138, 434, 498, 14); sectionBg.lineStyle(1, 0xffd700, 0.3); sectionBg.strokeRoundedRect(1157, 138, 434, 498, 14); this.add.text(1374, 157, 'THE RECKONING', { fontSize: '13px', fontFamily: 'Georgia, serif', color: '#c8a87e', letterSpacing: 5, }).setOrigin(0.5, 0.5); this.add.text(1374, 175, 'First to $2,000 wins', { fontSize: '10px', fontFamily: 'Georgia, serif', color: '#4a5a6a', }).setOrigin(0.5, 0.5); this.add.text(1374, 378, 'VS', { fontSize: '20px', fontFamily: 'Georgia, serif', fontStyle: 'bold', color: '#2a1a4a', stroke: '#000000', strokeThickness: 3, }).setOrigin(0.5, 0.5); this.lordVial = new VialDisplay(this, 1268, 190, 'The Lord', 0xffd700, 0xc8a87e); this.sinVial = new VialDisplay(this, 1478, 190, '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; GameState.playerFunds += playerGain; GameState.lordFunds += lordGain; this.game.events.emit('win', { playerGain, lordGain, symbol: symbols[0] }); this.game.events.emit('funds-updated'); // 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], () => { this.lordVial.animateUpdate(GameState.lordFunds, lordBox.x, 115, () => { GameState.spinning = false; this.game.events.emit('spin-complete'); }); } ); } else { GameState.sinTotal += GameState.spinCost; this.game.events.emit('loss', { sinAdded: GameState.spinCost }); this.game.events.emit('funds-updated'); const uiScene = this.scene.get('UIScene'); const sinBox = uiScene ? uiScene.getSinBoxCenter() : { x: 1333, y: 60 }; this.lossAnim.play( this, this.slotMachine.getCenterX(), this.slotMachine.getCenterY(), sinBox, () => { this.sinVial.animateUpdate(GameState.sinTotal, sinBox.x, 115, () => { GameState.spinning = false; this.game.events.emit('spin-complete'); }); } ); } } }