class GameState { constructor() { this.reset(); this.eventListeners = {}; } reset() { this.period = 1; this.timeRemaining = 20 * 60; // 20 minutes in seconds this.homeScore = 0; this.awayScore = 0; this.gameSpeed = 1; this.isPaused = false; this.gameOver = false; this.stats = { home: { shots: 0, saves: 0, hits: 0, faceoffWins: 0 }, away: { shots: 0, saves: 0, hits: 0, faceoffWins: 0 } }; this.gameEvents = []; this.lastEventTime = 0; this.rink = { width: 1000, height: 600, centerX: 500, centerY: 300, goalWidth: 120, goalHeight: 40, corners: { radius: 80 }, faceoffDots: [ { x: 200, y: 150 }, // Home left { x: 200, y: 450 }, // Home right { x: 500, y: 300 }, // Center { x: 800, y: 150 }, // Away left { x: 800, y: 450 } // Away right ] }; this.faceoff = { isActive: false, phase: 'none', // 'setup', 'ready', 'drop', 'complete' timeRemaining: 0, location: { x: 500, y: 300 }, // Center ice by default participants: { home: null, away: null } }; } formatTime(seconds) { const minutes = Math.floor(seconds / 60); const remainingSeconds = Math.floor(seconds % 60); return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`; } getPeriodName() { switch (this.period) { case 1: return '1st'; case 2: return '2nd'; case 3: return '3rd'; default: return 'OT'; } } updateTime(deltaTime) { if (this.isPaused || this.gameOver) return; this.timeRemaining -= deltaTime * this.gameSpeed; if (this.timeRemaining <= 0) { this.timeRemaining = 0; this.endPeriod(); } } endPeriod() { this.period++; if (this.period > 3 && this.homeScore !== this.awayScore) { this.gameOver = true; this.emit('game-end', { winner: this.homeScore > this.awayScore ? 'home' : 'away' }); } else if (this.period <= 3) { this.timeRemaining = 20 * 60; this.emit('period-end', { period: this.period - 1 }); } else { this.timeRemaining = 5 * 60; // Overtime this.emit('overtime-start'); } } addGoal(team) { if (team === 'home') { this.homeScore++; } else { this.awayScore++; } this.addEvent(`GOAL - ${team.toUpperCase()}!`); this.emit('goal', { team, homeScore: this.homeScore, awayScore: this.awayScore }); } addShot(team) { this.stats[team].shots++; this.emit('shot', { team, shots: this.stats[team].shots }); } addSave(team) { this.stats[team].saves++; this.emit('save', { team, saves: this.stats[team].saves }); } addEvent(description) { const event = { time: this.formatTime(20 * 60 - this.timeRemaining), period: this.period, description, timestamp: Date.now() }; this.gameEvents.unshift(event); if (this.gameEvents.length > 50) { this.gameEvents.pop(); } } togglePause() { this.isPaused = !this.isPaused; this.emit('pause-toggle', { isPaused: this.isPaused }); } setSpeed(speed) { this.gameSpeed = Math.max(0.1, Math.min(5, speed)); this.emit('speed-change', { speed: this.gameSpeed }); } on(event, callback) { if (!this.eventListeners[event]) { this.eventListeners[event] = []; } this.eventListeners[event].push(callback); } emit(event, data) { if (this.eventListeners[event]) { this.eventListeners[event].forEach(callback => callback(data)); } } startFaceoff(location = { x: 500, y: 300 }) { console.log('Starting faceoff at location:', location); this.faceoff = { isActive: true, phase: 'setup', timeRemaining: 3.0, // 3 seconds for setup location: location, participants: { home: null, away: null } }; this.emit('faceoff-start', { location }); } updateFaceoff(deltaTime) { if (!this.faceoff.isActive) return; this.faceoff.timeRemaining -= deltaTime; if (this.faceoff.timeRemaining <= 0) { switch (this.faceoff.phase) { case 'setup': this.faceoff.phase = 'ready'; this.faceoff.timeRemaining = 2.0; // 2 seconds ready phase this.emit('faceoff-ready'); break; case 'ready': this.faceoff.phase = 'drop'; this.faceoff.timeRemaining = 0.5; // Brief drop phase this.emit('faceoff-drop'); break; case 'drop': this.completeFaceoff(); break; } } } completeFaceoff() { this.faceoff.isActive = false; this.faceoff.phase = 'complete'; this.emit('faceoff-complete', { winner: this.determineFaceoffWinner(), location: this.faceoff.location }); } determineFaceoffWinner() { const { home, away } = this.faceoff.participants; if (!home || !away) return 'home'; // Default fallback // Simple skill-based determination with some randomness const homeSkill = home.attributes.puckHandling + home.attributes.awareness; const awaySkill = away.attributes.puckHandling + away.attributes.awareness; const homeProbability = homeSkill / (homeSkill + awaySkill); const winner = Math.random() < homeProbability ? 'home' : 'away'; this.stats[winner].faceoffWins++; return winner; } getGameState() { return { period: this.period, timeRemaining: this.timeRemaining, homeScore: this.homeScore, awayScore: this.awayScore, stats: { ...this.stats }, isPaused: this.isPaused, gameSpeed: this.gameSpeed, gameOver: this.gameOver, faceoff: { ...this.faceoff } }; } }