hockey-manager/src/engine/game-state.js
2025-09-16 13:08:41 +02:00

236 lines
6.7 KiB
JavaScript

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 }
};
console.log('Faceoff state set to:', this.faceoff);
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 }
};
}
}