|
class GameUI {
|
|
constructor() {
|
|
|
|
this.state = {
|
|
currentPage: 'landing',
|
|
isAnimating: false,
|
|
darkMode: true,
|
|
animationDuration: 300
|
|
};
|
|
|
|
|
|
this.elements = {
|
|
pages: {},
|
|
buttons: {},
|
|
containers: {},
|
|
overlays: {}
|
|
};
|
|
|
|
|
|
this.initialize();
|
|
}
|
|
|
|
async initialize() {
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', () => this.setupUI());
|
|
} else {
|
|
this.setupUI();
|
|
}
|
|
}
|
|
|
|
setupUI() {
|
|
|
|
this.cacheElements();
|
|
|
|
this.setupEventListeners();
|
|
|
|
this.showPage('landing');
|
|
}
|
|
|
|
cacheElements() {
|
|
|
|
['landing', 'setup', 'game', 'recording', 'listening', 'voting', 'results'].forEach(pageId => {
|
|
this.elements.pages[pageId] = document.getElementById(`${pageId}-page`);
|
|
});
|
|
|
|
|
|
this.elements.buttons = {
|
|
play: document.getElementById('play-button'),
|
|
addPlayer: document.getElementById('add-player-button'),
|
|
start: document.getElementById('start-button'),
|
|
record: document.getElementById('record-button'),
|
|
settings: document.getElementById('settings-button')
|
|
};
|
|
|
|
|
|
this.elements.containers = {
|
|
playerList: document.getElementById('player-list'),
|
|
questionDisplay: document.getElementById('question-display'),
|
|
timerDisplay: document.getElementById('timer-display'),
|
|
recordingVisualizer: document.getElementById('recording-visualizer'),
|
|
votingOptions: document.getElementById('voting-options')
|
|
};
|
|
|
|
|
|
this.elements.overlays = {
|
|
loading: document.getElementById('loading-overlay'),
|
|
error: document.getElementById('error-overlay'),
|
|
settings: document.getElementById('settings-overlay')
|
|
};
|
|
}
|
|
|
|
setupEventListeners() {
|
|
|
|
Object.entries(this.elements.buttons).forEach(([key, button]) => {
|
|
if (button) {
|
|
button.addEventListener('click', () => this.handleButtonClick(key));
|
|
}
|
|
});
|
|
|
|
|
|
document.addEventListener('keydown', (e) => this.handleKeyPress(e));
|
|
|
|
|
|
if (this.elements.buttons.settings) {
|
|
this.elements.buttons.settings.addEventListener('click', () => this.toggleSettings());
|
|
}
|
|
}
|
|
|
|
handleButtonClick(buttonType) {
|
|
switch (buttonType) {
|
|
case 'play':
|
|
this.transitionToPage('setup');
|
|
break;
|
|
case 'addPlayer':
|
|
window.game.addPlayer();
|
|
break;
|
|
case 'start':
|
|
window.game.startGame();
|
|
break;
|
|
case 'record':
|
|
this.toggleRecording();
|
|
break;
|
|
default:
|
|
console.warn(`Unhandled button type: ${buttonType}`);
|
|
}
|
|
}
|
|
|
|
async transitionToPage(pageName) {
|
|
if (this.state.isAnimating || this.state.currentPage === pageName) return;
|
|
|
|
this.state.isAnimating = true;
|
|
|
|
|
|
const currentPage = this.elements.pages[this.state.currentPage];
|
|
if (currentPage) {
|
|
await this.animateElement(currentPage, 'fadeOut');
|
|
currentPage.classList.remove('active');
|
|
}
|
|
|
|
|
|
this.state.currentPage = pageName;
|
|
const newPage = this.elements.pages[pageName];
|
|
if (newPage) {
|
|
newPage.classList.add('active');
|
|
await this.animateElement(newPage, 'fadeIn');
|
|
}
|
|
|
|
this.state.isAnimating = false;
|
|
}
|
|
|
|
async animateElement(element, animation) {
|
|
return new Promise(resolve => {
|
|
element.classList.add(animation);
|
|
setTimeout(() => {
|
|
element.classList.remove(animation);
|
|
resolve();
|
|
}, this.state.animationDuration);
|
|
});
|
|
}
|
|
|
|
updatePlayerList(players) {
|
|
if (!this.elements.containers.playerList) return;
|
|
|
|
const playerList = this.elements.containers.playerList;
|
|
playerList.innerHTML = '';
|
|
|
|
players.forEach(player => {
|
|
const playerElement = document.createElement('div');
|
|
playerElement.className = 'player-avatar';
|
|
|
|
|
|
const avatarCircle = document.createElement('div');
|
|
avatarCircle.className = 'avatar-circle';
|
|
avatarCircle.textContent = player.id;
|
|
|
|
|
|
const playerName = document.createElement('div');
|
|
playerName.className = 'player-name';
|
|
playerName.textContent = player.name;
|
|
|
|
playerElement.appendChild(avatarCircle);
|
|
playerElement.appendChild(playerName);
|
|
playerList.appendChild(playerElement);
|
|
});
|
|
}
|
|
|
|
updateTimer(timeLeft) {
|
|
if (!this.elements.containers.timerDisplay) return;
|
|
|
|
const minutes = Math.floor(timeLeft / 60);
|
|
const seconds = timeLeft % 60;
|
|
this.elements.containers.timerDisplay.textContent =
|
|
`${minutes}:${seconds.toString().padStart(2, '0')}`;
|
|
|
|
|
|
if (timeLeft <= 10) {
|
|
this.elements.containers.timerDisplay.classList.add('warning');
|
|
}
|
|
}
|
|
|
|
showQuestion(question) {
|
|
if (!this.elements.containers.questionDisplay) return;
|
|
|
|
const questionElement = this.elements.containers.questionDisplay;
|
|
questionElement.textContent = question;
|
|
|
|
|
|
this.animateElement(questionElement, 'slideIn');
|
|
}
|
|
|
|
toggleRecording(isRecording) {
|
|
if (!this.elements.buttons.record) return;
|
|
|
|
const recordButton = this.elements.buttons.record;
|
|
recordButton.classList.toggle('recording', isRecording);
|
|
recordButton.textContent = isRecording ? 'Stop Recording' : 'Start Recording';
|
|
}
|
|
|
|
showLoadingOverlay(show, message = 'Loading...') {
|
|
if (!this.elements.overlays.loading) return;
|
|
|
|
const overlay = this.elements.overlays.loading;
|
|
if (show) {
|
|
overlay.querySelector('.loading-message').textContent = message;
|
|
overlay.classList.add('active');
|
|
} else {
|
|
overlay.classList.remove('active');
|
|
}
|
|
}
|
|
|
|
showError(message, duration = 3000) {
|
|
if (!this.elements.overlays.error) return;
|
|
|
|
const errorOverlay = this.elements.overlays.error;
|
|
const errorMessage = errorOverlay.querySelector('.error-message');
|
|
|
|
errorMessage.textContent = message;
|
|
errorOverlay.classList.add('active');
|
|
|
|
setTimeout(() => {
|
|
errorOverlay.classList.remove('active');
|
|
}, duration);
|
|
}
|
|
|
|
updateVotingOptions(players, currentPlayer) {
|
|
if (!this.elements.containers.votingOptions) return;
|
|
|
|
const votingContainer = this.elements.containers.votingOptions;
|
|
votingContainer.innerHTML = '';
|
|
|
|
players.forEach(player => {
|
|
if (player.id !== currentPlayer) {
|
|
const voteButton = document.createElement('button');
|
|
voteButton.className = 'vote-button';
|
|
voteButton.dataset.playerId = player.id;
|
|
|
|
const playerCircle = document.createElement('div');
|
|
playerCircle.className = 'player-circle';
|
|
playerCircle.textContent = player.id;
|
|
|
|
voteButton.appendChild(playerCircle);
|
|
votingContainer.appendChild(voteButton);
|
|
|
|
voteButton.addEventListener('click', () => {
|
|
window.game.submitVote(player.id);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
showResults(results) {
|
|
const resultsPage = this.elements.pages.results;
|
|
if (!resultsPage) return;
|
|
|
|
|
|
resultsPage.innerHTML = '';
|
|
|
|
|
|
const content = document.createElement('div');
|
|
content.className = 'results-content';
|
|
|
|
|
|
const impostorReveal = document.createElement('div');
|
|
impostorReveal.className = 'impostor-reveal';
|
|
impostorReveal.textContent = `The impostor was Player ${results.impostor}!`;
|
|
|
|
|
|
const votingResults = document.createElement('div');
|
|
votingResults.className = 'voting-results';
|
|
Object.entries(results.votes).forEach(([player, vote]) => {
|
|
const voteEntry = document.createElement('div');
|
|
voteEntry.className = 'vote-entry';
|
|
voteEntry.textContent = `Player ${player} voted for Player ${vote}`;
|
|
votingResults.appendChild(voteEntry);
|
|
});
|
|
|
|
content.appendChild(impostorReveal);
|
|
content.appendChild(votingResults);
|
|
resultsPage.appendChild(content);
|
|
|
|
this.transitionToPage('results');
|
|
}
|
|
|
|
handleKeyPress(event) {
|
|
|
|
switch (event.key) {
|
|
case 'Escape':
|
|
this.closeAllOverlays();
|
|
break;
|
|
case 'r':
|
|
if (this.state.currentPage === 'recording') {
|
|
this.toggleRecording();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
closeAllOverlays() {
|
|
Object.values(this.elements.overlays).forEach(overlay => {
|
|
if (overlay) {
|
|
overlay.classList.remove('active');
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
cleanup() {
|
|
|
|
Object.values(this.elements.buttons).forEach(button => {
|
|
if (button) {
|
|
button.replaceWith(button.cloneNode(true));
|
|
}
|
|
});
|
|
|
|
|
|
this.closeAllOverlays();
|
|
}
|
|
}
|
|
|
|
|
|
export default GameUI; |