' + description + '
';
document.body.appendChild(toast);
setTimeout(function() {
toast.classList.remove('translate-x-full');
}, 100);
setTimeout(function() {
toast.classList.add('translate-x-full');
setTimeout(function() {
document.body.removeChild(toast);
}, 300);
}, 3000);
}
function showScreen(screenName) {
const screens = ['mainScreen', 'joinScreen', 'gameRoom', 'raceScreen'];
screens.forEach(function(screen) {
document.getElementById(screen).classList.add('hidden');
});
document.getElementById(screenName).classList.remove('hidden');
gameState.currentScreen = screenName;
}
function generateClientId() {
const stored = localStorage.getItem('game_client_id');
if (stored) return stored;
const id = 'player_' + Math.random().toString(36).slice(2, 9);
localStorage.setItem('game_client_id', id);
return id;
}
// Create Room
async function createRoom() {
const playerName = prompt('請輸入你的暱稱(作為主持人):');
if (!playerName || playerName.trim() === '') {
showToast('錯誤', '請輸入有效的暱稱!', 'error');
return;
}
const roomId = Math.random().toString(36).substr(2, 6).toUpperCase();
const roomRef = db.ref('rooms/' + roomId);
try {
const snapshot = await roomRef.once('value');
if (snapshot.exists()) {
createRoom();
return;
}
gameState.roomId = roomId;
gameState.isHost = true;
gameState.playerName = playerName.trim();
gameState.selectedAnimal = null;
gameState.isReady = false;
gameState.players = {};
await roomRef.set({
host: gameState.playerName,
createdAt: firebase.database.ServerValue.TIMESTAMP,
raceActive: false,
raceResults: null
});
await db.ref('rooms/' + roomId + '/players/' + gameState.playerName).set({
name: gameState.playerName,
animal: null,
ready: false,
isHost: true
});
setupRoomListeners();
showGameRoom();
showToast('成功', '房間創建成功: ' + roomId, 'success');
} catch (error) {
console.error('Create room error:', error);
showToast('錯誤', '創建房間失敗,請重試', 'error');
}
}
// Join Room
async function joinRoom() {
const roomId = document.getElementById('roomIdInput').value.trim().toUpperCase();
const playerName = document.getElementById('nicknameInput').value.trim();
if (!roomId || !playerName) {
showToast('錯誤', '請填寫房間號碼和暱稱!', 'error');
return;
}
try {
const roomSnapshot = await db.ref('rooms/' + roomId).once('value');
if (!roomSnapshot.exists()) {
showToast('錯誤', '房間不存在!', 'error');
return;
}
const roomData = roomSnapshot.val();
if (roomData.raceActive) {
showToast('錯誤', '比賽進行中,無法加入!', 'error');
return;
}
const playersSnapshot = await db.ref('rooms/' + roomId + '/players').once('value');
const players = playersSnapshot.val() || {};
if (Object.keys(players).length >= 20) {
showToast('錯誤', '房間已滿!', 'error');
return;
}
if (players[playerName]) {
showToast('錯誤', '暱稱已被使用!', 'error');
return;
}
gameState.roomId = roomId;
gameState.isHost = false;
gameState.playerName = playerName;
gameState.selectedAnimal = null;
gameState.isReady = false;
gameState.players = {};
await db.ref('rooms/' + roomId + '/players/' + playerName).set({
name: playerName,
animal: null,
ready: false,
isHost: false
});
setupRoomListeners();
showGameRoom();
showToast('成功', '成功加入房間: ' + roomId, 'success');
} catch (error) {
console.error('Join room error:', error);
showToast('錯誤', '加入房間失敗,請重試', 'error');
}
}
// Fixed joinOrUpdate function - corrected variable assignment
function joinOrUpdate() {
const name = document.getElementById('player-name-input').value.trim();
const animal = document.getElementById('animal-select').value;
const isHost = document.getElementById('host-checkbox').checked;
if (!name) {
showToast('錯誤', '請輸入你的名稱才能加入房間', 'error');
return;
}
localStorage.setItem('game_name', name);
localStorage.setItem('game_animal', animal);
gameState.playerName = name;
gameState.selectedAnimal = animal;
gameState.isHost = isHost;
const playerData = {
id: generateClientId(),
name: name,
animal: animal,
joinedAt: Date.now()
};
// Fixed: Proper object initialization and assignment
const updates = {};
updates['players/' + playerData.id] = playerData;
if (isHost && !gameState.hostId) {
updates.hostId = playerData.id;
}
const joinBtn = document.getElementById('join-room-btn');
const originalText = joinBtn.innerHTML;
joinBtn.innerHTML = '🔄 連接中...';
joinBtn.disabled = true;
db.ref('rooms/' + gameState.roomId).update(updates).then(function() {
showToast('成功', '歡迎 ' + name + '!', 'success');
db.ref('rooms/' + gameState.roomId + '/players/' + playerData.id).onDisconnect().remove();
}).catch(function(error) {
console.error('Error joining room:', error);
showToast('錯誤', '加入失敗,請稍後重試', 'error');
}).finally(function() {
joinBtn.innerHTML = originalText;
joinBtn.disabled = false;
});
}
function setupRoomListeners() {
db.ref('rooms/' + gameState.roomId + '/players').on('value', function(snapshot) {
const players = snapshot.val() || {};
gameState.players = players;
updatePlayersDisplay();
updateUIState();
});
db.ref('rooms/' + gameState.roomId + '/raceActive').on('value', function(snapshot) {
const raceActive = snapshot.val() || false;
gameState.raceActive = raceActive;
if (raceActive) {
showRaceScreen();
startRace();
}
});
db.ref('rooms/' + gameState.roomId + '/raceResults').on('value', function(snapshot) {
const results = snapshot.val();
if (results) {
gameState.raceResults = results;
showRaceResults(results);
}
});
}
function showGameRoom() {
showScreen('gameRoom');
document.getElementById('currentRoomId').textContent = gameState.roomId;
document.getElementById('userRole').textContent = gameState.isHost ? '主持人' : '參與者';
createAnimalSelection();
updatePlayersDisplay();
updateUIState();
}
function createAnimalSelection() {
const grid = document.getElementById('animalGrid');
grid.innerHTML = '';
gameState.animals.forEach(function(animal) {
const button = document.createElement('button');
button.className = 'animal-btn w-16 h-16 bg-white border-2 border-gray-300 rounded-xl flex items-center justify-center text-2xl hover:border-blue-500 hover:bg-blue-50 transition-all';
button.innerHTML = animal.emoji;
button.title = animal.name;
button.onclick = function() { selectAnimal(animal); };
grid.appendChild(button);
});
}
async function selectAnimal(animal) {
if (gameState.raceActive) {
showToast('錯誤', '比賽進行中無法更換動物!', 'error');
return;
}
const isAnimalTaken = Object.values(gameState.players).some(function(player) {
return player.animal && player.animal.id === animal.id && player.name !== gameState.playerName;
});
if (isAnimalTaken) {
showToast('錯誤', '這隻動物已被其他玩家選擇!', 'error');
return;
}
try {
gameState.selectedAnimal = animal;
await db.ref('rooms/' + gameState.roomId + '/players/' + gameState.playerName).update({
animal: animal
});
updateAnimalSelection();
showToast('成功', '已選擇 ' + animal.name, 'success');
} catch (error) {
console.error('Select animal error:', error);
showToast('錯誤', '選擇動物失敗,請重試', 'error');
}
}
function updateAnimalSelection() {
const buttons = document.querySelectorAll('.animal-btn');
buttons.forEach(function(btn) {
btn.classList.remove('border-blue-500', 'bg-blue-100', 'border-red-500', 'bg-red-100');
btn.classList.add('border-gray-300', 'bg-white');
});
Object.values(gameState.players).forEach(function(player) {
if (player.animal) {
const animalIndex = gameState.animals.findIndex(function(a) { return a.id === player.animal.id; });
if (animalIndex !== -1) {
const btn = buttons[animalIndex];
if (player.name === gameState.playerName) {
btn.classList.add('border-blue-500', 'bg-blue-100');
} else {
btn.classList.add('border-red-500', 'bg-red-100');
}
}
}
});
}
function updatePlayersDisplay() {
const playersList = document.getElementById('playersList');
const playerCount = document.getElementById('playerCount');
const players = Object.values(gameState.players);
playerCount.textContent = players.length;
if (players.length === 0) {
playersList.innerHTML = '暫無玩家
';
return;
}
playersList.innerHTML = '';
players.forEach(function(player) {
const playerDiv = document.createElement('div');
playerDiv.className = 'flex items-center justify-between p-3 bg-white rounded-lg shadow-sm';
const animalDisplay = player.animal ? (player.animal.emoji + ' ' + player.animal.name) : '未選擇動物';
const readyStatus = player.ready ? '✅ 已準備' : '⏳ 未準備';
const hostBadge = player.isHost ? ' 👑' : '';
playerDiv.innerHTML = '' + player.name + hostBadge + '
' + animalDisplay + '
' + readyStatus + '
';
playersList.appendChild(playerDiv);
});
updateAnimalSelection();
}
async function toggleReady() {
if (!gameState.selectedAnimal) {
showToast('錯誤', '請先選擇一隻動物!', 'error');
return;
}
if (gameState.raceActive) {
showToast('錯誤', '比賽進行中無法更改準備狀態!', 'error');
return;
}
try {
const newReadyState = !gameState.isReady;
gameState.isReady = newReadyState;
await db.ref('rooms/' + gameState.roomId + '/players/' + gameState.playerName).update({
ready: newReadyState
});
showToast('狀態', newReadyState ? '已準備完成' : '取消準備', 'success');
} catch (error) {
console.error('Toggle ready error:', error);
showToast('錯誤', '更新準備狀態失敗,請重試', 'error');
}
}
function updateUIState() {
const readyBtn = document.getElementById('readyBtn');
const startGameBtn = document.getElementById('startGameBtn');
const currentPlayer = gameState.players[gameState.playerName];
if (currentPlayer) {
if (currentPlayer.ready) {
readyBtn.textContent = '取消準備';
readyBtn.className = 'bg-gradient-to-r from-gray-500 to-gray-600 hover:from-gray-600 hover:to-gray-700 text-white px-8 py-3 rounded-xl font-semibold shadow-lg transform hover:scale-105 transition-all';
} else {
readyBtn.textContent = '準備';
readyBtn.className = 'bg-gradient-to-r from-yellow-500 to-yellow-600 hover:from-yellow-600 hover:to-yellow-700 text-white px-8 py-3 rounded-xl font-semibold shadow-lg transform hover:scale-105 transition-all';
}
}
if (gameState.isHost) {
startGameBtn.classList.remove('hidden');
const players = Object.values(gameState.players);
const readyPlayers = players.filter(function(p) { return p.ready && p.animal; });
const canStart = readyPlayers.length >= 2 && !gameState.raceActive;
startGameBtn.disabled = !canStart;
if (canStart) {
startGameBtn.className = 'bg-gradient-to-r from-red-500 to-red-600 hover:from-red-600 hover:to-red-700 text-white px-8 py-3 rounded-xl font-semibold shadow-lg transform hover:scale-105 transition-all';
} else {
startGameBtn.className = 'bg-gradient-to-r from-gray-400 to-gray-500 text-white px-8 py-3 rounded-xl font-semibold cursor-not-allowed opacity-50';
}
} else {
startGameBtn.classList.add('hidden');
}
}
async function startGame() {
if (!gameState.isHost) {
showToast('錯誤', '只有主持人可以開始遊戲!', 'error');
return;
}
const players = Object.values(gameState.players);
const readyPlayers = players.filter(function(p) { return p.ready && p.animal; });
if (readyPlayers.length ' + player.animal.emoji + '
' + player.name + '
' + player.animal.name + '
🏁
' + winner.animal.emoji + '
' + winner.name + '
' + winner.animal.name + ' 獲得勝利!
完成時間: ' + (winner.finishTime / 1000).toFixed(2) + ' 秒
';
}
raceResult.classList.remove('hidden');
}
async function backToRoom() {
try {
if (gameState.isHost) {
await db.ref('rooms/' + gameState.roomId).update({
raceActive: false,
raceResults: null
});
}
gameState.raceActive = false;
gameState.raceResults = null;
showGameRoom();
showToast('返回', '已返回房間', 'success');
} catch (error) {
console.error('Back to room error:', error);
showToast('錯誤', '返回房間失敗,請重試', 'error');
}
}
async function leaveRoom() {
if (!confirm('確定要離開房間嗎?')) {
return;
}
try {
if (gameState.roomId && gameState.playerName) {
await db.ref('rooms/' + gameState.roomId + '/players/' + gameState.playerName).remove();
if (gameState.isHost) {
const playersSnapshot = await db.ref('rooms/' + gameState.roomId + '/players').once('value');
const players = playersSnapshot.val() || {};
if (Object.keys(players).length === 0) {
await db.ref('rooms/' + gameState.roomId).remove();
}
}
}
gameState = {
currentScreen: 'main',
roomId: null,
isHost: false,
playerName: '',
selectedAnimal: null,
isReady: false,
players: {},
raceActive: false,
raceResults: null,
animals: gameState.animals
};
showScreen('mainScreen');
showToast('離開', '已離開房間', 'success');
} catch (error) {
console.error('Leave room error:', error);
showToast('錯誤', '離開房間失敗,請重試', 'error');
}
}
// Event Listeners
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('createRoomBtn').addEventListener('click', createRoom);
document.getElementById('joinRoomBtn').addEventListener('click', function() {
showScreen('joinScreen');
});
document.getElementById('confirmJoinBtn').addEventListener('click', joinRoom);
document.getElementById('backFromJoinBtn').addEventListener('click', function() {
showScreen('mainScreen');
});
document.getElementById('readyBtn').addEventListener('click', toggleReady);
document.getElementById('startGameBtn').addEventListener('click', startGame);
document.getElementById('leaveRoomBtn').addEventListener('click', leaveRoom);
document.getElementById('backToRoomBtn').addEventListener('click', backToRoom);
});
