dcrey7's picture
Update static/js/game.js
a24fc4c verified
raw
history blame
14.3 kB
// Initialize Socket.IO connection with proper configuration
const socket = io({
reconnection: true,
reconnectionAttempts: 5,
reconnectionDelay: 1000,
transports: ['websocket', 'polling'] // Try WebSocket first, fallback to polling
});
class Game {
constructor() {
// Initialize core game state
this.state = {
gameId: null,
currentPhase: 'landing',
players: [],
currentQuestion: null,
isRecording: false,
recordingTime: 30,
listeningTime: 60,
votingTime: 60,
recordings: new Map(),
votes: new Map(),
impostor: null,
maxPlayers: 5,
minPlayers: 3,
isConnected: false,
errorDisplayed: false
};
// Initialize game when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => this.initialize());
} else {
this.initialize();
}
}
initialize() {
console.log('Initializing game components...');
this.bindUIElements();
this.initializeEventListeners();
this.initializeSocketHandlers();
this.showPage('landing');
}
bindUIElements() {
console.log('Binding UI elements...');
// Cache all UI elements for quick access
this.ui = {
gameContainer: document.getElementById('game-container'),
pages: {
landing: document.getElementById('landing-page'),
setup: document.getElementById('setup-page'),
recording: document.getElementById('recording-page'),
listening: document.getElementById('listening-page'),
voting: document.getElementById('voting-page'),
results: document.getElementById('results-page')
},
buttons: {
play: document.getElementById('play-button'),
addPlayer: document.getElementById('add-player-button'),
start: document.getElementById('start-button'),
record: document.getElementById('record-button')
},
displays: {
timer: document.getElementById('timer-display'),
question: document.getElementById('question-display'),
playerList: document.getElementById('player-list')
}
};
// Log UI element bindings for debugging
console.log('UI Elements bound:', {
landing: !!this.ui.pages.landing,
setup: !!this.ui.pages.setup,
playButton: !!this.ui.buttons.play
});
}
initializeEventListeners() {
console.log('Setting up event listeners...');
// Button click handlers
if (this.ui.buttons.play) {
this.ui.buttons.play.addEventListener('click', () => this.handlePlayButton());
}
if (this.ui.buttons.addPlayer) {
this.ui.buttons.addPlayer.addEventListener('click', () => this.addPlayer());
}
if (this.ui.buttons.start) {
this.ui.buttons.start.addEventListener('click', () => this.startGame());
}
if (this.ui.buttons.record) {
this.ui.buttons.record.addEventListener('click', () => this.toggleRecording());
}
// Window resize handler for responsive layout
window.addEventListener('resize', () => this.handleResize());
}
initializeSocketHandlers() {
// Connection events
socket.on('connect', () => {
console.log('Connected to server');
this.state.isConnected = true;
this.clearError();
});
socket.on('disconnect', () => {
console.log('Disconnected from server');
this.state.isConnected = false;
this.handleError('Connection lost. Attempting to reconnect...');
});
socket.on('connect_error', (error) => {
console.error('Connection error:', error);
this.handleError('Unable to connect to server. Please check your connection.');
});
// Game events
socket.on('connection_success', (data) => {
console.log('Connection successful:', data);
});
socket.on('game_created', (data) => {
console.log('Game created:', data);
if (data.status === 'success') {
this.state.gameId = data.gameId;
this.clearError();
this.showPage('setup');
this.addDefaultPlayers();
} else {
this.handleError('Failed to create game');
}
});
socket.on('game_error', (data) => {
console.error('Game error:', data);
this.handleError(data.error || 'An error occurred');
});
socket.on('player_joined', (data) => {
console.log('Player joined:', data);
if (data.status === 'success') {
// Update player list and UI
const newPlayer = {
id: data.playerId,
name: data.playerName
};
this.state.players.push(newPlayer);
this.updatePlayerList();
this.updatePlayerControls();
}
});
socket.on('round_started', (data) => {
console.log('Round started:', data);
this.handleRoundStart(data);
});
socket.on('recording_submitted', (data) => {
console.log('Recording submitted:', data);
this.updateRecordingStatus(data);
});
socket.on('round_result', (data) => {
console.log('Round result:', data);
this.handleRoundResult(data);
});
}
async handlePlayButton() {
console.log('Play button clicked');
try {
if (!this.state.isConnected) {
throw new Error('Not connected to server');
}
this.clearError();
await this.createGame();
} catch (error) {
console.error('Error handling play button:', error);
this.handleError('Failed to start game. Please try again.');
}
}
createGame() {
return new Promise((resolve, reject) => {
console.log('Creating new game...');
// Reset game state
this.state.gameId = null;
this.state.players = [];
// Emit create game event
socket.emit('create_game');
// Set timeout for response
const timeout = setTimeout(() => {
reject(new Error('Game creation timeout'));
}, 5000);
// Handle response
socket.once('game_created', (data) => {
clearTimeout(timeout);
if (data.status === 'success') {
console.log('Game created successfully:', data);
resolve(data);
} else {
reject(new Error(data.error || 'Failed to create game'));
}
});
// Handle error
socket.once('game_error', (data) => {
clearTimeout(timeout);
reject(new Error(data.error || 'Failed to create game'));
});
});
}
addDefaultPlayers() {
console.log('Adding default players');
if (!this.state.gameId) {
console.error('No game ID available');
return;
}
// Add two default players
this.addPlayer('Player 1');
this.addPlayer('Player 2');
}
addPlayer(defaultName = null) {
if (this.state.players.length >= this.state.maxPlayers) {
this.handleError('Maximum players reached');
return;
}
const playerName = defaultName || `Player ${this.state.players.length + 1}`;
console.log('Adding player:', playerName);
socket.emit('join_game', {
gameId: this.state.gameId,
playerName: playerName
});
}
async startGame() {
console.log('Starting game...');
try {
const response = await fetch('/api/start_game', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
gameId: this.state.gameId
})
});
const data = await response.json();
if (data.status === 'success') {
this.handleRoundStart(data);
} else {
throw new Error(data.error || 'Failed to start game');
}
} catch (error) {
console.error('Error starting game:', error);
this.handleError('Failed to start game');
}
}
handleRoundStart(data) {
console.log('Round starting:', data);
this.state.currentQuestion = data.question;
this.state.currentPhase = 'recording';
this.showPage('recording');
this.updateQuestionDisplay();
this.startTimer(this.state.recordingTime);
}
updateQuestionDisplay() {
if (this.ui.displays.question && this.state.currentQuestion) {
this.ui.displays.question.textContent = this.state.currentQuestion;
}
}
updatePlayerList() {
if (!this.ui.displays.playerList) return;
const playerList = this.ui.displays.playerList;
playerList.innerHTML = '';
const gridContainer = document.createElement('div');
gridContainer.className = 'player-grid';
this.state.players.forEach(player => {
const playerCard = document.createElement('div');
playerCard.className = 'player-card';
const circle = document.createElement('div');
circle.className = 'player-circle';
circle.textContent = player.id;
const name = document.createElement('div');
name.className = 'player-name';
name.textContent = player.name;
playerCard.appendChild(circle);
playerCard.appendChild(name);
gridContainer.appendChild(playerCard);
});
playerList.appendChild(gridContainer);
}
updatePlayerControls() {
if (this.ui.buttons.addPlayer) {
this.ui.buttons.addPlayer.disabled =
this.state.players.length >= this.state.maxPlayers;
}
if (this.ui.buttons.start) {
this.ui.buttons.start.disabled =
this.state.players.length < this.state.minPlayers;
}
}
handleError(message) {
console.error('Error:', message);
// Prevent multiple error messages
if (this.state.errorDisplayed) {
return;
}
this.state.errorDisplayed = true;
const errorElement = document.createElement('div');
errorElement.className = 'error-message';
errorElement.textContent = message;
if (this.ui.gameContainer) {
this.ui.gameContainer.appendChild(errorElement);
setTimeout(() => {
errorElement.remove();
this.state.errorDisplayed = false;
}, 3000);
}
}
clearError() {
const errorMessages = document.querySelectorAll('.error-message');
errorMessages.forEach(msg => msg.remove());
this.state.errorDisplayed = false;
}
showPage(pageName) {
console.log('Showing page:', pageName);
Object.values(this.ui.pages).forEach(page => {
if (page) {
page.classList.remove('active');
}
});
const pageToShow = this.ui.pages[pageName];
if (pageToShow) {
pageToShow.classList.add('active');
this.state.currentPhase = pageName;
} else {
console.error(`Page ${pageName} not found`);
}
}
startTimer(duration) {
let timeLeft = duration;
this.updateTimerDisplay(timeLeft);
this.timer = setInterval(() => {
timeLeft--;
this.updateTimerDisplay(timeLeft);
if (timeLeft <= 0) {
clearInterval(this.timer);
this.handlePhaseEnd();
}
}, 1000);
}
updateTimerDisplay(timeLeft) {
if (this.ui.displays.timer) {
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
this.ui.displays.timer.textContent =
`${minutes}:${seconds.toString().padStart(2, '0')}`;
}
}
handlePhaseEnd() {
switch (this.state.currentPhase) {
case 'recording':
this.showPage('listening');
this.startTimer(this.state.listeningTime);
break;
case 'listening':
this.showPage('voting');
this.startTimer(this.state.votingTime);
break;
case 'voting':
this.showPage('results');
break;
}
}
cleanup() {
// Clear timers
if (this.timer) clearInterval(this.timer);
// Remove event listeners
window.removeEventListener('resize', this.handleResize);
// Reset game state
this.state = {
gameId: null,
currentPhase: 'landing',
players: [],
currentQuestion: null,
isRecording: false,
isConnected: false,
errorDisplayed: false
};
}
handleResize() {
if (window.innerWidth < 768) {
this.ui.gameContainer.classList.add('mobile');
} else {
this.ui.gameContainer.classList.remove('mobile');
}
}
}
// Initialize the game when the DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
console.log('Initializing game...');
window.game = new Game();
});