dcrey7's picture
Upload 9 files
e30257d verified
raw
history blame
10.6 kB
// Initialize Socket.IO connection with automatic reconnection
const socket = io({
reconnection: true,
reconnectionAttempts: 5,
reconnectionDelay: 1000
});
class Game {
constructor() {
// Core game state
this.state = {
gameId: null,
currentPhase: 'landing',
playerId: null,
players: [],
currentQuestion: null,
isRecording: false,
recordingTime: 30, // seconds
listeningTime: 60, // seconds
votingTime: 60, // seconds
recordings: new Map(),
votes: new Map(),
impostor: null
};
// Audio recording configuration
this.audioConfig = {
mediaRecorder: null,
audioChunks: [],
stream: null
};
// Initialize the game
this.initializeEventListeners();
this.bindUIElements();
}
// Initialize all event listeners for the game
initializeEventListeners() {
// Socket event listeners
socket.on('connect', () => this.handleConnection());
socket.on('game_created', (data) => this.handleGameCreated(data));
socket.on('player_joined', (data) => this.handlePlayerJoined(data));
socket.on('round_start', (data) => this.handleRoundStart(data));
socket.on('round_result', (data) => this.handleRoundResult(data));
// UI event listeners
document.getElementById('start-button')?.addEventListener('click', () => this.startGame());
document.getElementById('add-player-button')?.addEventListener('click', () => this.addPlayer());
}
// Bind UI elements and initialize their event handlers
bindUIElements() {
// Bind all necessary UI elements
const uiElements = {
gameContainer: document.getElementById('game-container'),
landingPage: document.getElementById('landing-page'),
setupPage: document.getElementById('setup-page'),
gamePage: document.getElementById('game-page'),
questionDisplay: document.getElementById('question-display'),
timerDisplay: document.getElementById('timer-display'),
recordButton: document.getElementById('record-button'),
playerList: document.getElementById('player-list'),
voteButtons: document.querySelectorAll('.vote-button')
};
// Store UI elements in the class
this.ui = uiElements;
}
// Handle initial connection to the server
handleConnection() {
console.log('Connected to server');
this.showPage('landing');
}
// Create a new game session
async createGame() {
try {
socket.emit('create_game');
} catch (error) {
this.handleError('Failed to create game');
}
}
// Handle successful game creation
handleGameCreated(data) {
this.state.gameId = data.gameId;
this.showPage('setup');
}
// Add a new player to the game
async addPlayer() {
if (this.state.players.length >= 5) {
this.handleError('Maximum player limit reached');
return;
}
const playerName = prompt('Enter player name:');
if (!playerName) return;
socket.emit('join_game', {
gameId: this.state.gameId,
playerName: playerName
});
}
// Handle new player joining the game
handlePlayerJoined(data) {
this.state.players.push({
id: data.playerId,
name: data.playerName
});
this.updatePlayerList();
}
// Update the player list in the UI
updatePlayerList() {
if (!this.ui.playerList) return;
this.ui.playerList.innerHTML = '';
this.state.players.forEach(player => {
const playerElement = document.createElement('div');
playerElement.className = 'player-avatar';
playerElement.textContent = player.id;
this.ui.playerList.appendChild(playerElement);
});
}
// Start the game
async startGame() {
if (this.state.players.length < 3) {
this.handleError('Need at least 3 players to start');
return;
}
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);
}
} catch (error) {
this.handleError('Failed to start game');
}
}
// Handle the start of a new round
handleRoundStart(data) {
this.state.currentQuestion = data.question;
this.state.currentPhase = 'recording';
this.showPage('game');
this.updateQuestionDisplay();
this.startTimer(this.state.recordingTime, () => this.endRecordingPhase());
}
// Start audio recording
async startRecording() {
try {
this.audioConfig.stream = await navigator.mediaDevices.getUserMedia({ audio: true });
this.audioConfig.mediaRecorder = new MediaRecorder(this.audioConfig.stream);
this.audioConfig.audioChunks = [];
this.audioConfig.mediaRecorder.ondataavailable = (event) => {
this.audioConfig.audioChunks.push(event.data);
};
this.audioConfig.mediaRecorder.onstop = () => {
this.submitRecording();
};
this.audioConfig.mediaRecorder.start();
this.state.isRecording = true;
this.updateRecordButton();
} catch (error) {
this.handleError('Failed to start recording');
}
}
// Stop audio recording
stopRecording() {
if (this.audioConfig.mediaRecorder && this.state.isRecording) {
this.audioConfig.mediaRecorder.stop();
this.audioConfig.stream.getTracks().forEach(track => track.stop());
this.state.isRecording = false;
this.updateRecordButton();
}
}
// Submit recording to server
async submitRecording() {
const audioBlob = new Blob(this.audioConfig.audioChunks, { type: 'audio/wav' });
const formData = new FormData();
formData.append('audio', audioBlob);
formData.append('gameId', this.state.gameId);
formData.append('playerId', this.state.playerId);
try {
const response = await fetch('/api/submit_recording', {
method: 'POST',
body: formData
});
const data = await response.json();
if (data.status === 'success') {
this.state.recordings.set(this.state.playerId, data.recordingUrl);
}
} catch (error) {
this.handleError('Failed to submit recording');
}
}
// Start the timer for a game phase
startTimer(duration, callback) {
let timeLeft = duration;
this.updateTimerDisplay(timeLeft);
this.timer = setInterval(() => {
timeLeft--;
this.updateTimerDisplay(timeLeft);
if (timeLeft <= 0) {
clearInterval(this.timer);
if (callback) callback();
}
}, 1000);
}
// Update the timer display
updateTimerDisplay(timeLeft) {
if (this.ui.timerDisplay) {
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
this.ui.timerDisplay.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
}
}
// End the recording phase and move to listening phase
endRecordingPhase() {
if (this.state.isRecording) {
this.stopRecording();
}
this.state.currentPhase = 'listening';
this.startListeningPhase();
}
// Start the listening phase
startListeningPhase() {
this.showPage('listening');
this.loadRecordings();
this.startTimer(this.state.listeningTime, () => this.startVotingPhase());
}
// Load all player recordings
async loadRecordings() {
// Implementation for loading and playing recordings
// This would integrate with the audio playback UI
}
// Start the voting phase
startVotingPhase() {
this.state.currentPhase = 'voting';
this.showPage('voting');
this.startTimer(this.state.votingTime, () => this.endVotingPhase());
}
// Submit a vote
submitVote(votedPlayerId) {
socket.emit('submit_vote', {
gameId: this.state.gameId,
voterId: this.state.playerId,
votedPlayerId: votedPlayerId
});
}
// Handle the round results
handleRoundResult(data) {
this.showResults(data);
// Trigger dramatic background effect
window.gameBackground?.addDramaticEffect('impostor_reveal');
}
// Show the results page
showResults(data) {
this.showPage('results');
// Implementation for displaying round results
}
// Switch between game pages
showPage(pageName) {
const pages = document.querySelectorAll('.game-page');
pages.forEach(page => {
page.classList.remove('active');
if (page.id === `${pageName}-page`) {
page.classList.add('active');
}
});
}
// Handle errors
handleError(message) {
const errorElement = document.createElement('div');
errorElement.className = 'error-message';
errorElement.textContent = message;
this.ui.gameContainer.appendChild(errorElement);
setTimeout(() => errorElement.remove(), 3000);
}
}
// Initialize the game when the DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
const game = new Game();
// Expose game instance for debugging
window.game = game;
});
export default Game;