dcrey7 commited on
Commit
95109ca
·
verified ·
1 Parent(s): 0f05b68

Update static/js/game.js

Browse files
Files changed (1) hide show
  1. static/js/game.js +148 -203
static/js/game.js CHANGED
@@ -1,53 +1,56 @@
1
-
2
- // Initialize Socket.IO connection with automatic reconnection
3
- const socket = io({
4
- reconnection: true,
5
- reconnectionAttempts: 5,
6
- reconnectionDelay: 1000
7
- });
8
-
9
  class Game {
10
  constructor() {
11
- // Core game state with all necessary properties
12
  this.state = {
13
  gameId: null,
14
  currentPhase: 'landing',
15
  players: [],
16
  currentQuestion: null,
17
  isRecording: false,
18
- recordingTime: 30, // seconds
19
- listeningTime: 60, // seconds
20
- votingTime: 60, // seconds
21
  recordings: new Map(),
22
  votes: new Map(),
23
- impostor: null,
24
- maxPlayers: 5,
25
- minPlayers: 3,
26
- socketConnected: false // Track socket connection status
27
  };
28
 
29
- // Initialize if DOM is ready, otherwise wait for it
30
- if (document.readyState === 'loading') {
31
- document.addEventListener('DOMContentLoaded', () => this.initialize());
32
- } else {
33
- this.initialize();
34
- }
35
  }
36
 
37
- initialize() {
38
- console.log('Initializing game components...');
39
- this.bindUIElements();
40
  this.initializeEventListeners();
41
- this.initializeSocketHandlers();
42
- this.initializeAudioManager();
43
- this.showPage('landing');
44
  }
45
 
46
- bindUIElements() {
47
- console.log('Binding UI elements...');
48
- // Cache all UI elements for quick access
49
- this.ui = {
50
- gameContainer: document.getElementById('game-container'),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  pages: {
52
  landing: document.getElementById('landing-page'),
53
  setup: document.getElementById('setup-page'),
@@ -65,120 +68,102 @@ class Game {
65
  displays: {
66
  timer: document.getElementById('timer-display'),
67
  question: document.getElementById('question-display'),
68
- playerList: document.getElementById('player-list')
 
69
  }
70
  };
71
  }
72
 
73
  initializeEventListeners() {
74
- console.log('Setting up event listeners...');
75
-
76
- // Button event listeners
77
- if (this.ui.buttons.play) {
78
- this.ui.buttons.play.addEventListener('click', () => this.handlePlayButton());
79
- }
80
-
81
- if (this.ui.buttons.addPlayer) {
82
- this.ui.buttons.addPlayer.addEventListener('click', () => this.addPlayer());
83
- }
84
-
85
- if (this.ui.buttons.start) {
86
- this.ui.buttons.start.addEventListener('click', () => this.startGame());
87
- }
88
 
89
- if (this.ui.buttons.record) {
90
- this.ui.buttons.record.addEventListener('click', () => this.toggleRecording());
91
- }
92
-
93
- // Handle window resize for responsive layout
94
  window.addEventListener('resize', () => this.handleResize());
95
  }
96
 
97
- initializeSocketHandlers() {
98
- // Server connection events
99
- socket.on('connect', () => {
100
- this.state.socketConnected = true;
101
- console.log('Connected to server');
102
- });
103
-
104
- socket.on('disconnect', () => {
105
- this.state.socketConnected = false;
106
- console.log('Disconnected from server');
107
- this.handleError('Connection lost. Please refresh the page.');
108
- });
109
-
110
- // Game events
111
- socket.on('game_created', (data) => {
112
- if (data.status === 'success') {
113
- this.state.gameId = data.gameId;
114
- console.log('Game created:', data);
115
- this.showPage('setup');
116
- this.addDefaultPlayers();
117
- } else {
118
- this.handleError(data.error || 'Failed to create game');
119
- }
120
- });
121
 
122
- socket.on('player_joined', (data) => {
123
- if (data.status === 'success') {
124
- this.state.players.push({
125
- id: data.player.id,
126
- name: data.player.name
127
- });
128
- this.updatePlayerList();
129
- this.updatePlayerControls();
130
- }
131
- });
132
 
133
- socket.on('round_start', (data) => this.handleRoundStart(data));
134
- socket.on('round_result', (data) => this.handleRoundResult(data));
 
 
 
135
  }
136
 
137
- initializeAudioManager() {
138
- // Initialize audio context for recording
139
- this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
 
 
 
 
 
 
 
 
140
  }
141
 
142
- async handlePlayButton() {
143
- console.log('Play button clicked');
 
 
 
 
144
  try {
145
- await this.createGame();
 
 
146
  } catch (error) {
147
- console.error('Error starting game:', error);
148
- this.handleError('Failed to start game');
149
  }
150
  }
151
 
152
- createGame() {
153
  return new Promise((resolve, reject) => {
154
- console.log('Creating new game...');
155
- socket.emit('create_game', {}, (response) => {
156
  if (response.status === 'success') {
 
157
  resolve(response);
158
  } else {
159
  reject(new Error(response.error || 'Game creation failed'));
160
  }
161
  });
162
 
163
- setTimeout(() => reject(new Error('Game creation timeout')), 5000);
 
 
164
  });
165
  }
166
 
167
  addDefaultPlayers() {
168
- // Add two default players to start
169
  this.addPlayer('Player 1');
170
  this.addPlayer('Player 2');
171
- this.updatePlayerControls();
172
  }
173
 
174
  addPlayer(defaultName = null) {
175
- if (this.state.players.length >= this.state.maxPlayers) {
176
- this.handleError('Maximum players reached');
177
  return;
178
  }
179
 
180
  const playerName = defaultName || `Player ${this.state.players.length + 1}`;
181
- socket.emit('join_game', {
 
182
  gameId: this.state.gameId,
183
  playerName: playerName
184
  }, (response) => {
@@ -188,25 +173,24 @@ class Game {
188
  name: response.player.name
189
  });
190
  this.updatePlayerList();
191
- this.updatePlayerControls();
192
  } else {
193
- this.handleError(response.error || 'Failed to add player');
194
  }
195
  });
196
  }
197
 
198
  updatePlayerList() {
199
- if (!this.ui.displays.playerList) return;
 
200
 
201
- const playerList = this.ui.displays.playerList;
202
- playerList.innerHTML = '';
203
-
204
- const gridContainer = document.createElement('div');
205
- gridContainer.className = 'player-grid';
206
 
207
  this.state.players.forEach(player => {
208
- const playerCard = document.createElement('div');
209
- playerCard.className = 'player-card';
210
 
211
  const circle = document.createElement('div');
212
  circle.className = 'player-circle';
@@ -216,36 +200,31 @@ class Game {
216
  name.className = 'player-name';
217
  name.textContent = player.name;
218
 
219
- playerCard.appendChild(circle);
220
- playerCard.appendChild(name);
221
- gridContainer.appendChild(playerCard);
222
  });
223
 
224
- playerList.appendChild(gridContainer);
225
  }
226
 
227
- updatePlayerControls() {
228
- if (this.ui.buttons.addPlayer) {
229
- this.ui.buttons.addPlayer.disabled =
230
- this.state.players.length >= this.state.maxPlayers;
231
- }
232
 
233
- if (this.ui.buttons.start) {
234
- this.ui.buttons.start.disabled =
235
- this.state.players.length < this.state.minPlayers;
236
- }
237
  }
238
 
239
  async startGame() {
240
- console.log('Starting game...');
241
  try {
242
  const response = await fetch('/api/start_game', {
243
  method: 'POST',
244
- headers: {
245
- 'Content-Type': 'application/json'
246
- },
247
  body: JSON.stringify({
248
- gameId: this.state.gameId
 
249
  })
250
  });
251
 
@@ -256,106 +235,72 @@ class Game {
256
  throw new Error(data.error || 'Failed to start game');
257
  }
258
  } catch (error) {
259
- console.error('Error starting game:', error);
260
- this.handleError('Failed to start game');
261
  }
262
  }
263
 
264
  handleRoundStart(data) {
265
- console.log('Round starting:', data);
266
  this.state.currentQuestion = data.question;
267
  this.state.currentPhase = 'recording';
268
  this.showPage('recording');
269
  this.updateQuestionDisplay();
270
- this.startTimer(this.state.recordingTime);
 
 
 
 
 
 
 
271
  }
272
 
273
  updateQuestionDisplay() {
274
- if (this.ui.displays.question && this.state.currentQuestion) {
275
- this.ui.displays.question.textContent = this.state.currentQuestion;
276
  }
277
  }
278
 
279
  startTimer(duration) {
280
- let timeLeft = duration;
281
- this.updateTimerDisplay(timeLeft);
282
-
283
- this.timer = setInterval(() => {
284
- timeLeft--;
285
- this.updateTimerDisplay(timeLeft);
286
-
287
- if (timeLeft <= 0) {
288
- clearInterval(this.timer);
289
  this.handlePhaseEnd();
290
  }
291
  }, 1000);
292
  }
293
 
294
- updateTimerDisplay(timeLeft) {
295
- if (this.ui.displays.timer) {
296
- const minutes = Math.floor(timeLeft / 60);
297
- const seconds = timeLeft % 60;
298
- this.ui.displays.timer.textContent =
299
- `${minutes}:${seconds.toString().padStart(2, '0')}`;
300
- }
301
  }
302
 
303
- showPage(pageName) {
304
- console.log('Showing page:', pageName);
305
-
306
- Object.values(this.ui.pages).forEach(page => {
307
- if (page) {
308
- page.classList.remove('active');
309
- }
310
- });
311
-
312
- const pageToShow = this.ui.pages[pageName];
313
- if (pageToShow) {
314
- pageToShow.classList.add('active');
315
- this.state.currentPhase = pageName;
316
- } else {
317
- console.error(`Page ${pageName} not found`);
318
- }
319
- }
320
-
321
- handleError(message) {
322
- console.error('Error:', message);
323
  const errorElement = document.createElement('div');
324
  errorElement.className = 'error-message';
325
  errorElement.textContent = message;
326
- this.ui.gameContainer.appendChild(errorElement);
327
- setTimeout(() => errorElement.remove(), 3000);
 
 
 
328
  }
329
 
330
  handleResize() {
331
- // Update any size-dependent UI elements
332
- if (window.innerWidth < 768) {
333
- this.ui.gameContainer.classList.add('mobile');
334
- } else {
335
- this.ui.gameContainer.classList.remove('mobile');
336
- }
337
- }
338
-
339
- cleanup() {
340
- // Clear all intervals and timeouts
341
- if (this.timer) clearInterval(this.timer);
342
-
343
- // Remove event listeners
344
- window.removeEventListener('resize', this.handleResize);
345
-
346
- // Reset game state
347
- this.state = {
348
- gameId: null,
349
- currentPhase: 'landing',
350
- players: [],
351
- currentQuestion: null,
352
- isRecording: false
353
- };
354
  }
355
  }
356
 
357
- // Initialize the game when the DOM is loaded
358
  document.addEventListener('DOMContentLoaded', () => {
359
- console.log('Initializing game...');
360
  window.game = new Game();
361
  });
 
 
1
+ [file name]: game.js
2
+ [file content begin]
 
 
 
 
 
 
3
  class Game {
4
  constructor() {
5
+ // Enhanced game state with connection tracking
6
  this.state = {
7
  gameId: null,
8
  currentPhase: 'landing',
9
  players: [],
10
  currentQuestion: null,
11
  isRecording: false,
 
 
 
12
  recordings: new Map(),
13
  votes: new Map(),
14
+ connectionStatus: 'disconnected',
15
+ connectionRetries: 0,
16
+ socket: null,
17
+ uiElements: null
18
  };
19
 
20
+ this.initializeGame();
 
 
 
 
 
21
  }
22
 
23
+ initializeGame() {
24
+ this.initializeSocket();
25
+ this.initializeUI();
26
  this.initializeEventListeners();
 
 
 
27
  }
28
 
29
+ initializeSocket() {
30
+ console.log('Initializing WebSocket connection...');
31
+ this.state.socket = io({
32
+ reconnection: true,
33
+ reconnectionAttempts: 5,
34
+ reconnectionDelay: 3000,
35
+ transports: ['websocket'],
36
+ autoConnect: false
37
+ });
38
+
39
+ // Socket event handlers
40
+ this.state.socket.on('connect', () => this.handleSocketConnect());
41
+ this.state.socket.on('disconnect', () => this.handleSocketDisconnect());
42
+ this.state.socket.on('connect_error', (error) => this.handleSocketError(error));
43
+ this.state.socket.on('game_created', (data) => this.handleGameCreated(data));
44
+ this.state.socket.on('player_joined', (data) => this.handlePlayerJoined(data));
45
+ this.state.socket.on('round_start', (data) => this.handleRoundStart(data));
46
+ this.state.socket.on('round_result', (data) => this.handleRoundResult(data));
47
+
48
+ // Manually connect after setting up handlers
49
+ this.state.socket.connect();
50
+ }
51
+
52
+ initializeUI() {
53
+ this.state.uiElements = {
54
  pages: {
55
  landing: document.getElementById('landing-page'),
56
  setup: document.getElementById('setup-page'),
 
68
  displays: {
69
  timer: document.getElementById('timer-display'),
70
  question: document.getElementById('question-display'),
71
+ playerList: document.getElementById('player-list'),
72
+ connectionStatus: document.getElementById('connection-status')
73
  }
74
  };
75
  }
76
 
77
  initializeEventListeners() {
78
+ // Button click handlers
79
+ this.state.uiElements.buttons.play?.addEventListener('click', () => this.handlePlay());
80
+ this.state.uiElements.buttons.addPlayer?.addEventListener('click', () => this.addPlayer());
81
+ this.state.uiElements.buttons.start?.addEventListener('click', () => this.startGame());
82
+ this.state.uiElements.buttons.record?.addEventListener('click', () => this.toggleRecording());
 
 
 
 
 
 
 
 
 
83
 
84
+ // Window resize handler
 
 
 
 
85
  window.addEventListener('resize', () => this.handleResize());
86
  }
87
 
88
+ handleSocketConnect() {
89
+ console.log('WebSocket connected:', this.state.socket.id);
90
+ this.state.connectionStatus = 'connected';
91
+ this.updateConnectionStatus();
92
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
+ handleSocketDisconnect() {
95
+ console.log('WebSocket disconnected');
96
+ this.state.connectionStatus = 'disconnected';
97
+ this.updateConnectionStatus();
98
+ this.showError('Disconnected from server. Reconnecting...');
99
+ }
 
 
 
 
100
 
101
+ handleSocketError(error) {
102
+ console.error('WebSocket error:', error);
103
+ this.state.connectionStatus = 'error';
104
+ this.updateConnectionStatus();
105
+ this.showError(`Connection error: ${error.message}`);
106
  }
107
 
108
+ updateConnectionStatus() {
109
+ if (!this.state.uiElements.displays.connectionStatus) return;
110
+
111
+ const statusMap = {
112
+ connected: '🟢 Connected',
113
+ disconnected: '🔴 Disconnected',
114
+ error: '⚠️ Connection Error'
115
+ };
116
+
117
+ this.state.uiElements.displays.connectionStatus.textContent =
118
+ statusMap[this.state.connectionStatus] || '❓ Unknown';
119
  }
120
 
121
+ async handlePlay() {
122
+ if (this.state.connectionStatus !== 'connected') {
123
+ this.showError('Not connected to server');
124
+ return;
125
+ }
126
+
127
  try {
128
+ await this.createNewGame();
129
+ this.showPage('setup');
130
+ this.addDefaultPlayers();
131
  } catch (error) {
132
+ this.showError(error.message);
 
133
  }
134
  }
135
 
136
+ createNewGame() {
137
  return new Promise((resolve, reject) => {
138
+ this.state.socket.emit('create_game', (response) => {
 
139
  if (response.status === 'success') {
140
+ this.state.gameId = response.gameId;
141
  resolve(response);
142
  } else {
143
  reject(new Error(response.error || 'Game creation failed'));
144
  }
145
  });
146
 
147
+ setTimeout(() => {
148
+ reject(new Error('Game creation timed out'));
149
+ }, 10000);
150
  });
151
  }
152
 
153
  addDefaultPlayers() {
 
154
  this.addPlayer('Player 1');
155
  this.addPlayer('Player 2');
 
156
  }
157
 
158
  addPlayer(defaultName = null) {
159
+ if (this.state.players.length >= 5) {
160
+ this.showError('Maximum players reached (5)');
161
  return;
162
  }
163
 
164
  const playerName = defaultName || `Player ${this.state.players.length + 1}`;
165
+
166
+ this.state.socket.emit('join_game', {
167
  gameId: this.state.gameId,
168
  playerName: playerName
169
  }, (response) => {
 
173
  name: response.player.name
174
  });
175
  this.updatePlayerList();
176
+ this.updateGameControls();
177
  } else {
178
+ this.showError(response.error || 'Failed to add player');
179
  }
180
  });
181
  }
182
 
183
  updatePlayerList() {
184
+ const container = this.state.uiElements.displays.playerList;
185
+ if (!container) return;
186
 
187
+ container.innerHTML = '';
188
+ const grid = document.createElement('div');
189
+ grid.className = 'player-grid';
 
 
190
 
191
  this.state.players.forEach(player => {
192
+ const card = document.createElement('div');
193
+ card.className = 'player-card';
194
 
195
  const circle = document.createElement('div');
196
  circle.className = 'player-circle';
 
200
  name.className = 'player-name';
201
  name.textContent = player.name;
202
 
203
+ card.append(circle, name);
204
+ grid.appendChild(card);
 
205
  });
206
 
207
+ container.appendChild(grid);
208
  }
209
 
210
+ updateGameControls() {
211
+ // Update add player button
212
+ this.state.uiElements.buttons.addPlayer.disabled =
213
+ this.state.players.length >= 5;
 
214
 
215
+ // Update start game button
216
+ this.state.uiElements.buttons.start.disabled =
217
+ this.state.players.length < 3;
 
218
  }
219
 
220
  async startGame() {
 
221
  try {
222
  const response = await fetch('/api/start_game', {
223
  method: 'POST',
224
+ headers: {'Content-Type': 'application/json'},
 
 
225
  body: JSON.stringify({
226
+ gameId: this.state.gameId,
227
+ players: this.state.players.map(p => p.id)
228
  })
229
  });
230
 
 
235
  throw new Error(data.error || 'Failed to start game');
236
  }
237
  } catch (error) {
238
+ this.showError(error.message);
 
239
  }
240
  }
241
 
242
  handleRoundStart(data) {
 
243
  this.state.currentQuestion = data.question;
244
  this.state.currentPhase = 'recording';
245
  this.showPage('recording');
246
  this.updateQuestionDisplay();
247
+ this.startTimer(30);
248
+ }
249
+
250
+ showPage(pageName) {
251
+ Object.values(this.state.uiElements.pages).forEach(page => {
252
+ page?.classList.remove('active');
253
+ });
254
+ this.state.uiElements.pages[pageName]?.classList.add('active');
255
  }
256
 
257
  updateQuestionDisplay() {
258
+ if (this.state.uiElements.displays.question && this.state.currentQuestion) {
259
+ this.state.uiElements.displays.question.textContent = this.state.currentQuestion;
260
  }
261
  }
262
 
263
  startTimer(duration) {
264
+ let remaining = duration;
265
+ this.updateTimerDisplay(remaining);
266
+
267
+ const timer = setInterval(() => {
268
+ remaining--;
269
+ this.updateTimerDisplay(remaining);
270
+
271
+ if (remaining <= 0) {
272
+ clearInterval(timer);
273
  this.handlePhaseEnd();
274
  }
275
  }, 1000);
276
  }
277
 
278
+ updateTimerDisplay(seconds) {
279
+ if (!this.state.uiElements.displays.timer) return;
280
+ const mins = Math.floor(seconds / 60);
281
+ const secs = seconds % 60;
282
+ this.state.uiElements.displays.timer.textContent =
283
+ `${mins}:${secs.toString().padStart(2, '0')}`;
 
284
  }
285
 
286
+ showError(message, duration = 5000) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
  const errorElement = document.createElement('div');
288
  errorElement.className = 'error-message';
289
  errorElement.textContent = message;
290
+ document.body.appendChild(errorElement);
291
+
292
+ setTimeout(() => {
293
+ errorElement.remove();
294
+ }, duration);
295
  }
296
 
297
  handleResize() {
298
+ // Add responsive layout handling if needed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
  }
300
  }
301
 
302
+ // Initialize game when DOM loads
303
  document.addEventListener('DOMContentLoaded', () => {
 
304
  window.game = new Game();
305
  });
306
+ [file content end]