236 lines
6.7 KiB
JavaScript
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 }
|
|
};
|
|
}
|
|
} |