Compare commits
6 Commits
f4c36c48ac
...
afc24414a3
318
app.js
318
app.js
@@ -221,8 +221,7 @@ function initGame() {
|
||||
const startBtn = document.getElementById('startGameBtn');
|
||||
const scoreEl = document.getElementById('scoreVal');
|
||||
const overlay = document.getElementById('gameOverlay');
|
||||
const submitBtn = document.getElementById('submitScoreBtn');
|
||||
const nameInput = document.getElementById('playerName');
|
||||
const submitBtn = document.getElementById('saveScoreBtn');
|
||||
const leaderboardList = document.getElementById('leaderboardList');
|
||||
const timerDisplay = document.getElementById('timerDisplay');
|
||||
const timeBtns = document.querySelectorAll('.time-btn');
|
||||
@@ -253,27 +252,202 @@ function initGame() {
|
||||
});
|
||||
});
|
||||
|
||||
// Leaderboard logic
|
||||
let leaderboard = JSON.parse(localStorage.getItem('trae_leaderboard') || '[]');
|
||||
|
||||
const updateLeaderboardUI = () => {
|
||||
leaderboardList.innerHTML = '';
|
||||
leaderboard.sort((a, b) => b.score - a.score).slice(0, 10).forEach((entry, idx) => {
|
||||
const dateStr = new Date(entry.date).toLocaleDateString();
|
||||
const durationStr = entry.duration ? `${entry.duration}s` : 'N/A';
|
||||
const div = document.createElement('div');
|
||||
div.className = 'leader-item';
|
||||
div.innerHTML = `
|
||||
<span class="leader-rank">#${idx + 1}</span>
|
||||
<span class="leader-name">${entry.name}</span>
|
||||
<span class="leader-score">${entry.score}</span>
|
||||
<span class="leader-duration">${durationStr}</span>
|
||||
<span class="leader-date">${dateStr}</span>
|
||||
`;
|
||||
leaderboardList.appendChild(div);
|
||||
const playersData = JSON.parse(localStorage.getItem('trae_players') || '{}');
|
||||
let currentUser = null;
|
||||
|
||||
const generateSecretCode = () => {
|
||||
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
|
||||
let code = '';
|
||||
for (let i = 0; i < 6; i++) {
|
||||
code += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||
}
|
||||
return code;
|
||||
};
|
||||
|
||||
window.registerPlayer = () => {
|
||||
const username = document.getElementById('regUsername').value.trim() || 'Anonymous';
|
||||
const pin = generateSecretCode();
|
||||
|
||||
if (playersData[username]) {
|
||||
document.getElementById('regStatus').innerHTML = `<span style="color: #ff4444;">Username already exists! Your PIN: ${playersData[username].code}</span>`;
|
||||
return;
|
||||
}
|
||||
|
||||
playersData[username] = {
|
||||
name: username,
|
||||
code: pin,
|
||||
games: {},
|
||||
lastPlayed: new Date().toISOString()
|
||||
};
|
||||
|
||||
localStorage.setItem('trae_players', JSON.stringify(playersData));
|
||||
currentUser = username;
|
||||
|
||||
document.getElementById('regUsername').value = '';
|
||||
document.getElementById('regStatus').innerHTML = '';
|
||||
document.getElementById('regStatus').parentElement.querySelector('.input-group').style.display = 'none';
|
||||
document.getElementById('currentUserInfo').style.display = 'block';
|
||||
document.getElementById('currentUserDisplay').innerText = username;
|
||||
document.getElementById('currentUserPin').innerText = pin;
|
||||
|
||||
updateLeaderboardUI();
|
||||
};
|
||||
|
||||
window.logoutUser = () => {
|
||||
currentUser = null;
|
||||
document.getElementById('currentUserInfo').style.display = 'none';
|
||||
document.getElementById('regUsername').value = '';
|
||||
document.getElementById('regStatus').innerHTML = '';
|
||||
document.getElementById('regStatus').parentElement.querySelector('.input-group').style.display = 'flex';
|
||||
|
||||
updateAllLoginPrompts();
|
||||
};
|
||||
|
||||
window.submitScore = (gameType, score) => {
|
||||
if (!currentUser) {
|
||||
alert('Please register first to save scores');
|
||||
return;
|
||||
}
|
||||
|
||||
const player = playersData[currentUser];
|
||||
if (!player.games[gameType] || player.games[gameType].score < score) {
|
||||
player.games[gameType] = {
|
||||
score: score,
|
||||
date: new Date().toISOString()
|
||||
};
|
||||
player.lastPlayed = new Date().toISOString();
|
||||
localStorage.setItem('trae_players', JSON.stringify(playersData));
|
||||
updateLeaderboardUI();
|
||||
alert(`Score saved! ${currentUser}: ${score} in ${gameType}`);
|
||||
} else {
|
||||
alert(`Score not saved. Your best for ${gameType} is ${player.games[gameType].score}`);
|
||||
}
|
||||
};
|
||||
|
||||
const updateAllLoginPrompts = () => {
|
||||
const prompts = document.querySelectorAll('[id$="LoginPrompt"]');
|
||||
prompts.forEach(p => {
|
||||
p.style.display = currentUser ? 'none' : 'block';
|
||||
});
|
||||
};
|
||||
|
||||
const updateAllOverlays = () => {
|
||||
if (currentUser) {
|
||||
document.getElementById('currentUserDisplay').innerText = currentUser;
|
||||
document.getElementById('currentUserPin').innerText = playersData[currentUser].code;
|
||||
document.getElementById('currentUserInfo').style.display = 'block';
|
||||
document.getElementById('regStatus').parentElement.querySelector('.input-group').style.display = 'none';
|
||||
}
|
||||
updateAllLoginPrompts();
|
||||
};
|
||||
|
||||
window.copySecretCode = (code) => {
|
||||
navigator.clipboard.writeText(code).then(() => {
|
||||
const btn = event.target;
|
||||
const originalText = btn.innerText;
|
||||
btn.innerText = 'Copied!';
|
||||
btn.style.background = 'var(--trae-green)';
|
||||
setTimeout(() => {
|
||||
btn.innerText = code;
|
||||
btn.style.background = '';
|
||||
}, 1500);
|
||||
});
|
||||
};
|
||||
|
||||
// Leaderboard logic
|
||||
let currentTab = 'total';
|
||||
|
||||
const updateLeaderboardUI = () => {
|
||||
leaderboardList.innerHTML = '';
|
||||
let displayData = [];
|
||||
|
||||
if (currentTab === 'total') {
|
||||
Object.values(playersData).forEach(player => {
|
||||
const totalScore = Object.values(player.games).reduce((sum, val) => sum + val.score, 0);
|
||||
displayData.push({
|
||||
name: player.name,
|
||||
code: player.code,
|
||||
totalScore,
|
||||
game: 'Total',
|
||||
score: totalScore,
|
||||
date: player.lastPlayed
|
||||
});
|
||||
});
|
||||
displayData.sort((a, b) => b.totalScore - a.totalScore);
|
||||
} else {
|
||||
Object.values(playersData).forEach(player => {
|
||||
if (player.games[currentTab]) {
|
||||
displayData.push({
|
||||
name: player.name,
|
||||
code: player.code,
|
||||
game: currentTab,
|
||||
score: player.games[currentTab].score,
|
||||
date: player.games[currentTab].date
|
||||
});
|
||||
}
|
||||
});
|
||||
displayData.sort((a, b) => b.score - a.score);
|
||||
}
|
||||
|
||||
displayData.slice(0, 50).forEach((entry, idx) => {
|
||||
const dateStr = new Date(entry.date).toLocaleDateString();
|
||||
const div = document.createElement('div');
|
||||
div.className = 'leader-item';
|
||||
|
||||
if (currentTab === 'total') {
|
||||
div.innerHTML = `
|
||||
<span class="leader-rank">#${idx + 1}</span>
|
||||
<span class="leader-name">${entry.name}</span>
|
||||
<span class="leader-game">${entry.game}</span>
|
||||
<span class="leader-score">${entry.totalScore}</span>
|
||||
<span class="leader-date">${dateStr}</span>
|
||||
`;
|
||||
} else {
|
||||
const gameLabels = {
|
||||
gift: 'Gift Catcher',
|
||||
traoom: 'Traoom',
|
||||
neonpuzzle: 'Neon Puzzle',
|
||||
rhythm: 'Rhythm Beat',
|
||||
arena: 'Cosmic Arena',
|
||||
tetris: 'Crystal Tetris',
|
||||
platformer: 'Aurora Jumper'
|
||||
};
|
||||
div.innerHTML = `
|
||||
<span class="leader-rank">#${idx + 1}</span>
|
||||
<span class="leader-name">${entry.name}</span>
|
||||
<span class="leader-game">${gameLabels[currentTab]}</span>
|
||||
<span class="leader-score">${entry.score}</span>
|
||||
<span class="leader-code" onclick="copySecretCode('${entry.code}')">${entry.code}</span>
|
||||
<span class="leader-date">${dateStr}</span>
|
||||
`;
|
||||
}
|
||||
leaderboardList.appendChild(div);
|
||||
});
|
||||
|
||||
if (displayData.length === 0) {
|
||||
const emptyMsg = document.createElement('div');
|
||||
emptyMsg.className = 'leader-item';
|
||||
emptyMsg.style.justifyContent = 'center';
|
||||
emptyMsg.innerHTML = `<span style="color: #888;">No scores yet for ${currentTab === 'total' ? 'Total' : currentTab}!</span>`;
|
||||
leaderboardList.appendChild(emptyMsg);
|
||||
}
|
||||
};
|
||||
|
||||
const tabButtons = document.querySelectorAll('.tab-btn');
|
||||
tabButtons.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
tabButtons.forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
currentTab = btn.dataset.tab;
|
||||
updateLeaderboardUI();
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('registerBtn')?.addEventListener('click', registerPlayer);
|
||||
|
||||
updateAllOverlays();
|
||||
updateLeaderboardUI();
|
||||
|
||||
const startTimer = () => {
|
||||
timeLeft = selectedDuration;
|
||||
timerDisplay.innerText = timeLeft + 's';
|
||||
@@ -301,20 +475,23 @@ function initGame() {
|
||||
gameActive = false;
|
||||
if (timerInterval) clearInterval(timerInterval);
|
||||
document.body.style.overflow = '';
|
||||
document.getElementById('overlayTitle').innerText = 'GAME OVER';
|
||||
document.getElementById('gameFinalScore').innerText = score;
|
||||
overlay.classList.remove('hidden');
|
||||
document.getElementById('overlayTitle').innerText = `GAME OVER - SCORE: ${score}`;
|
||||
};
|
||||
|
||||
submitBtn.addEventListener('click', () => {
|
||||
const name = nameInput.value.trim() || 'Anonymous';
|
||||
leaderboard.push({ name, score, date: new Date().toISOString(), duration: selectedDuration });
|
||||
localStorage.setItem('trae_leaderboard', JSON.stringify(leaderboard));
|
||||
updateLeaderboardUI();
|
||||
submitScore('gift', score);
|
||||
overlay.classList.add('hidden');
|
||||
startBtn.style.display = 'block';
|
||||
document.body.style.overflow = '';
|
||||
});
|
||||
|
||||
document.getElementById('playAgainBtn').addEventListener('click', () => {
|
||||
overlay.classList.add('hidden');
|
||||
startBtn.click();
|
||||
});
|
||||
|
||||
const resize = () => {
|
||||
canvas.width = canvas.parentElement.clientWidth;
|
||||
canvas.height = canvas.parentElement.clientHeight;
|
||||
@@ -737,8 +914,7 @@ function initGame() {
|
||||
|
||||
// Export/Import Leaderboard
|
||||
window.exportLeaderboard = () => {
|
||||
const leaderboard = JSON.parse(localStorage.getItem('trae_leaderboard') || '[]');
|
||||
const dataStr = JSON.stringify(leaderboard, null, 2);
|
||||
const dataStr = JSON.stringify(playersData, null, 2);
|
||||
const blob = new Blob([dataStr], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
@@ -756,9 +932,13 @@ window.importLeaderboard = (event) => {
|
||||
reader.onload = (e) => {
|
||||
try {
|
||||
const data = JSON.parse(e.target.result);
|
||||
if (Array.isArray(data)) {
|
||||
localStorage.setItem('trae_leaderboard', JSON.stringify(data));
|
||||
location.reload();
|
||||
if (typeof data === 'object' && data !== null) {
|
||||
Object.keys(data).forEach(name => {
|
||||
playersData[name] = data[name];
|
||||
});
|
||||
localStorage.setItem('trae_players', JSON.stringify(playersData));
|
||||
updateLeaderboardUI();
|
||||
alert(`Imported ${Object.keys(data).length} player(s)!`);
|
||||
}
|
||||
} catch (err) {
|
||||
alert('Invalid file format');
|
||||
@@ -772,7 +952,6 @@ function initTraoom() {
|
||||
const canvas = document.getElementById('traoomCanvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const startBtn = document.getElementById('startTraoomBtn');
|
||||
const restartBtn = document.getElementById('restartTraoomBtn');
|
||||
const killEl = document.getElementById('killVal');
|
||||
const timeEl = document.getElementById('traoomTime');
|
||||
const waveEl = document.getElementById('waveVal');
|
||||
@@ -1189,8 +1368,29 @@ function initTraoom() {
|
||||
mouse.y = touch.clientY - rect.top;
|
||||
}, { passive: false });
|
||||
|
||||
document.getElementById('saveTraoomScoreBtn').addEventListener('click', () => {
|
||||
submitScore('traoom', kills);
|
||||
overlay.classList.add('hidden');
|
||||
startBtn.style.display = 'block';
|
||||
});
|
||||
|
||||
document.getElementById('playTraoomAgainBtn').addEventListener('click', () => {
|
||||
overlay.classList.add('hidden');
|
||||
startBtn.click();
|
||||
});
|
||||
|
||||
document.getElementById('savePuzzleScoreBtn').addEventListener('click', () => {
|
||||
submitScore('neonpuzzle', moves);
|
||||
overlay.classList.add('hidden');
|
||||
startBtn.style.display = 'block';
|
||||
});
|
||||
|
||||
document.getElementById('playPuzzleAgainBtn').addEventListener('click', () => {
|
||||
overlay.classList.add('hidden');
|
||||
startBtn.click();
|
||||
});
|
||||
|
||||
startBtn.addEventListener('click', startGame);
|
||||
restartBtn.addEventListener('click', startGame);
|
||||
|
||||
window.addEventListener('resize', resize);
|
||||
resize();
|
||||
@@ -1201,7 +1401,6 @@ function initNeonPuzzle() {
|
||||
const canvas = document.getElementById('neonpuzzleCanvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const startBtn = document.getElementById('startPuzzleBtn');
|
||||
const nextBtn = document.getElementById('nextPuzzleBtn');
|
||||
const levelEl = document.getElementById('puzzleLevel');
|
||||
const movesEl = document.getElementById('puzzleMoves');
|
||||
const overlay = document.getElementById('neonpuzzleOverlay');
|
||||
@@ -1378,7 +1577,6 @@ function initNeonPuzzle() {
|
||||
});
|
||||
|
||||
startBtn.addEventListener('click', startGame);
|
||||
nextBtn.addEventListener('click', nextLevel);
|
||||
|
||||
window.addEventListener('resize', resize);
|
||||
resize();
|
||||
@@ -1389,7 +1587,6 @@ function initRhythmBeat() {
|
||||
const canvas = document.getElementById('rhythmCanvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const startBtn = document.getElementById('startRhythmBtn');
|
||||
const restartBtn = document.getElementById('restartRhythmBtn');
|
||||
const scoreEl = document.getElementById('rhythmScore');
|
||||
const comboEl = document.getElementById('rhythmCombo');
|
||||
const streakEl = document.getElementById('rhythmStreak');
|
||||
@@ -1574,8 +1771,18 @@ function initRhythmBeat() {
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('saveRhythmScoreBtn').addEventListener('click', () => {
|
||||
submitScore('rhythm', score);
|
||||
overlay.classList.add('hidden');
|
||||
startBtn.style.display = 'block';
|
||||
});
|
||||
|
||||
document.getElementById('playRhythmAgainBtn').addEventListener('click', () => {
|
||||
overlay.classList.add('hidden');
|
||||
startBtn.click();
|
||||
});
|
||||
|
||||
startBtn.addEventListener('click', startGame);
|
||||
restartBtn.addEventListener('click', startGame);
|
||||
|
||||
window.addEventListener('resize', resize);
|
||||
resize();
|
||||
@@ -1586,7 +1793,6 @@ function initCosmicArena() {
|
||||
const canvas = document.getElementById('arenaCanvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const startBtn = document.getElementById('startArenaBtn');
|
||||
const restartBtn = document.getElementById('restartArenaBtn');
|
||||
const hpEl = document.getElementById('arenaHP');
|
||||
const killsEl = document.getElementById('arenaKills');
|
||||
const waveEl = document.getElementById('arenaWave');
|
||||
@@ -1849,8 +2055,18 @@ function initCosmicArena() {
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('saveArenaScoreBtn').addEventListener('click', () => {
|
||||
submitScore('arena', kills);
|
||||
overlay.classList.add('hidden');
|
||||
startBtn.style.display = 'block';
|
||||
});
|
||||
|
||||
document.getElementById('playArenaAgainBtn').addEventListener('click', () => {
|
||||
overlay.classList.add('hidden');
|
||||
startBtn.click();
|
||||
});
|
||||
|
||||
startBtn.addEventListener('click', startGame);
|
||||
restartBtn.addEventListener('click', startGame);
|
||||
|
||||
window.addEventListener('resize', resize);
|
||||
resize();
|
||||
@@ -1861,7 +2077,6 @@ function initTetris() {
|
||||
const canvas = document.getElementById('tetrisCanvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const startBtn = document.getElementById('startTetrisBtn');
|
||||
const restartBtn = document.getElementById('restartTetrisBtn');
|
||||
const scoreEl = document.getElementById('tetrisScore');
|
||||
const linesEl = document.getElementById('tetrisLines');
|
||||
const levelEl = document.getElementById('tetrisLevel');
|
||||
@@ -2100,8 +2315,18 @@ function initTetris() {
|
||||
draw();
|
||||
});
|
||||
|
||||
document.getElementById('saveTetrisScoreBtn').addEventListener('click', () => {
|
||||
submitScore('tetris', score);
|
||||
overlay.classList.add('hidden');
|
||||
startBtn.style.display = 'block';
|
||||
});
|
||||
|
||||
document.getElementById('playTetrisAgainBtn').addEventListener('click', () => {
|
||||
overlay.classList.add('hidden');
|
||||
startBtn.click();
|
||||
});
|
||||
|
||||
startBtn.addEventListener('click', startGame);
|
||||
restartBtn.addEventListener('click', startGame);
|
||||
|
||||
window.addEventListener('resize', resize);
|
||||
resize();
|
||||
@@ -2112,7 +2337,6 @@ function initPlatformer() {
|
||||
const canvas = document.getElementById('platformerCanvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const startBtn = document.getElementById('startPlatformerBtn');
|
||||
const restartBtn = document.getElementById('restartPlatformerBtn');
|
||||
const coinsEl = document.getElementById('coinsVal');
|
||||
const timeEl = document.getElementById('platformerTime');
|
||||
const overlay = document.getElementById('platformerOverlay');
|
||||
@@ -2347,8 +2571,18 @@ function initPlatformer() {
|
||||
window.addEventListener('keydown', (e) => keys[e.code] = true);
|
||||
window.addEventListener('keyup', (e) => keys[e.code] = false);
|
||||
|
||||
document.getElementById('savePlatformerScoreBtn').addEventListener('click', () => {
|
||||
submitScore('platformer', collected * 10 + Math.max(0, 100 - parseInt(formatTime(gameTime).replace(':', ''))));
|
||||
overlay.classList.add('hidden');
|
||||
startBtn.style.display = 'block';
|
||||
});
|
||||
|
||||
document.getElementById('playPlatformerAgainBtn').addEventListener('click', () => {
|
||||
overlay.classList.add('hidden');
|
||||
startBtn.click();
|
||||
});
|
||||
|
||||
startBtn.addEventListener('click', startGame);
|
||||
restartBtn.addEventListener('click', startGame);
|
||||
|
||||
window.addEventListener('resize', resize);
|
||||
resize();
|
||||
|
||||
90
index.html
90
index.html
@@ -19,6 +19,7 @@
|
||||
<div class="logo">TRAE <span>AURORA</span></div>
|
||||
<ul class="nav-links">
|
||||
<li><a href="#hero">Home</a></li>
|
||||
<li><a href="#registration">Register</a></li>
|
||||
<li><a href="#game">Gift Catcher</a></li>
|
||||
<li><a href="#traoom">Traoom</a></li>
|
||||
<li><a href="#neonpuzzle">Puzzle</a></li>
|
||||
@@ -146,6 +147,25 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Registration Section -->
|
||||
<section id="registration" class="glass-section">
|
||||
<div class="section-card glass">
|
||||
<h2>🎮 Player Registration</h2>
|
||||
<p>Register once to save your scores across all games</p>
|
||||
<div class="input-group" style="justify-content: center; max-width: 400px; margin: 0 auto;">
|
||||
<input type="text" id="regUsername" placeholder="Username" maxlength="15" style="flex: 1;">
|
||||
<button id="registerBtn" class="btn-primary">Register</button>
|
||||
</div>
|
||||
<p id="regStatus" style="text-align: center; margin-top: 15px; font-weight: bold;"></p>
|
||||
<div id="currentUserInfo" class="user-info" style="display: none; text-align: center; margin-top: 15px;">
|
||||
<p>🎉 Your Name: <strong id="currentUserDisplay"></strong></p>
|
||||
<p style="font-size: 0.8rem; color: #888;">Your PIN: <strong id="currentUserPin"></strong></p>
|
||||
<p style="font-size: 0.7rem; color: #666; margin-top: 5px;">Use this Name + PIN for all games</p>
|
||||
<button onclick="logoutUser()" class="btn-secondary" style="margin-top: 10px; background: #ff4444;">Logout</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Game Section -->
|
||||
<section id="game" class="game-section">
|
||||
<div class="section-card glass">
|
||||
@@ -162,10 +182,14 @@
|
||||
</div>
|
||||
<div id="gameOverlay" class="game-overlay hidden">
|
||||
<h3 id="overlayTitle">GAME OVER</h3>
|
||||
<div class="input-group">
|
||||
<input type="text" id="playerName" placeholder="Enter your name" maxlength="15">
|
||||
<button id="submitScoreBtn" class="btn-primary">Save Score</button>
|
||||
<p id="finalScore">Final Score: <span id="gameFinalScore">0</span></p>
|
||||
<div id="saveScoreButtons" class="input-group">
|
||||
<button id="saveScoreBtn" class="btn-primary">Save Score</button>
|
||||
<button id="playAgainBtn" class="btn-secondary" style="background: var(--trae-green);">Play Again</button>
|
||||
</div>
|
||||
<p id="loginPrompt" style="display: none; text-align: center; color: #ff6b6b;">
|
||||
<a href="#registration" style="color: var(--trae-cyan);">Register first</a> to save scores
|
||||
</p>
|
||||
</div>
|
||||
<button id="startGameBtn" class="btn-primary">Start Mission</button>
|
||||
</div>
|
||||
@@ -175,7 +199,17 @@
|
||||
<canvas id="gameCanvas"></canvas>
|
||||
</div>
|
||||
<div class="leaderboard glass">
|
||||
<h3>Leaderboard</h3>
|
||||
<div class="leaderboard-header">
|
||||
<h3>Global Leaderboard</h3>
|
||||
<button class="tab-btn active" data-tab="total">Total</button>
|
||||
<button class="tab-btn" data-tab="gift">Gift Catcher</button>
|
||||
<button class="tab-btn" data-tab="traoom">Traoom</button>
|
||||
<button class="tab-btn" data-tab="neonpuzzle">Neon Puzzle</button>
|
||||
<button class="tab-btn" data-tab="rhythm">Rhythm Beat</button>
|
||||
<button class="tab-btn" data-tab="arena">Cosmic Arena</button>
|
||||
<button class="tab-btn" data-tab="tetris">Crystal Tetris</button>
|
||||
<button class="tab-btn" data-tab="platformer">Aurora Jumper</button>
|
||||
</div>
|
||||
<div id="leaderboardList"></div>
|
||||
<div class="export-buttons">
|
||||
<button class="export-btn" onclick="exportLeaderboard()">Download Scores</button>
|
||||
@@ -202,7 +236,13 @@
|
||||
<h3 id="traoomOverlayTitle">GAME OVER</h3>
|
||||
<p id="traoomOverlayScore">Bugs Fixed: <span id="finalKills">0</span></p>
|
||||
<p id="traoomOverlayTime">Time Survived: <span id="finalTime">0:00</span></p>
|
||||
<button id="restartTraoomBtn" class="btn-primary">Play Again</button>
|
||||
<div class="input-group">
|
||||
<button id="saveTraoomScoreBtn" class="btn-primary">Save Score</button>
|
||||
<button id="playTraoomAgainBtn" class="btn-secondary" style="background: var(--trae-green);">Play Again</button>
|
||||
</div>
|
||||
<p id="traoomLoginPrompt" style="display: none; text-align: center; color: #ff6b6b;">
|
||||
<a href="#registration" style="color: var(--trae-cyan);">Register first</a> to save scores
|
||||
</p>
|
||||
</div>
|
||||
<button id="startTraoomBtn" class="btn-primary">Start Bug Hunt</button>
|
||||
</div>
|
||||
@@ -228,7 +268,13 @@
|
||||
<div id="neonpuzzleOverlay" class="game-overlay hidden">
|
||||
<h3 id="puzzleOverlayTitle">LEVEL COMPLETE</h3>
|
||||
<p id="puzzleOverlayScore">Moves: <span id="finalMoves">0</span></p>
|
||||
<button id="nextPuzzleBtn" class="btn-primary">Next Level</button>
|
||||
<div class="input-group">
|
||||
<button id="savePuzzleScoreBtn" class="btn-primary">Save Score</button>
|
||||
<button id="playPuzzleAgainBtn" class="btn-secondary" style="background: var(--trae-green);">Play Again</button>
|
||||
</div>
|
||||
<p id="puzzleLoginPrompt" style="display: none; text-align: center; color: #ff6b6b;">
|
||||
<a href="#registration" style="color: var(--trae-cyan);">Register first</a> to save scores
|
||||
</p>
|
||||
</div>
|
||||
<button id="startPuzzleBtn" class="btn-primary">Start Puzzle</button>
|
||||
</div>
|
||||
@@ -255,7 +301,13 @@
|
||||
<div id="rhythmOverlay" class="game-overlay hidden">
|
||||
<h3 id="rhythmOverlayTitle">BEAT OVER</h3>
|
||||
<p id="rhythmOverlayScore">Final Score: <span id="finalRhythmScore">0</span></p>
|
||||
<button id="restartRhythmBtn" class="btn-primary">Play Again</button>
|
||||
<div class="input-group">
|
||||
<button id="saveRhythmScoreBtn" class="btn-primary">Save Score</button>
|
||||
<button id="playRhythmAgainBtn" class="btn-secondary" style="background: var(--trae-green);">Play Again</button>
|
||||
</div>
|
||||
<p id="rhythmLoginPrompt" style="display: none; text-align: center; color: #ff6b6b;">
|
||||
<a href="#registration" style="color: var(--trae-cyan);">Register first</a> to save scores
|
||||
</p>
|
||||
</div>
|
||||
<button id="startRhythmBtn" class="btn-primary">Start Rhythm</button>
|
||||
</div>
|
||||
@@ -282,7 +334,13 @@
|
||||
<div id="arenaOverlay" class="game-overlay hidden">
|
||||
<h3 id="arenaOverlayTitle">GAME OVER</h3>
|
||||
<p id="arenaOverlayScore">Enemies Defeated: <span id="finalArenaKills">0</span></p>
|
||||
<button id="restartArenaBtn" class="btn-primary">Play Again</button>
|
||||
<div class="input-group">
|
||||
<button id="saveArenaScoreBtn" class="btn-primary">Save Score</button>
|
||||
<button id="playArenaAgainBtn" class="btn-secondary" style="background: var(--trae-green);">Play Again</button>
|
||||
</div>
|
||||
<p id="arenaLoginPrompt" style="display: none; text-align: center; color: #ff6b6b;">
|
||||
<a href="#registration" style="color: var(--trae-cyan);">Register first</a> to save scores
|
||||
</p>
|
||||
</div>
|
||||
<button id="startArenaBtn" class="btn-primary">Enter Arena</button>
|
||||
</div>
|
||||
@@ -309,7 +367,13 @@
|
||||
<div id="tetrisOverlay" class="game-overlay hidden">
|
||||
<h3 id="tetrisOverlayTitle">GAME OVER</h3>
|
||||
<p id="tetrisOverlayScore">Final Score: <span id="finalTetrisScore">0</span></p>
|
||||
<button id="restartTetrisBtn" class="btn-primary">Play Again</button>
|
||||
<div class="input-group">
|
||||
<button id="saveTetrisScoreBtn" class="btn-primary">Save Score</button>
|
||||
<button id="playTetrisAgainBtn" class="btn-secondary" style="background: var(--trae-green);">Play Again</button>
|
||||
</div>
|
||||
<p id="tetrisLoginPrompt" style="display: none; text-align: center; color: #ff6b6b;">
|
||||
<a href="#registration" style="color: var(--trae-cyan);">Register first</a> to save scores
|
||||
</p>
|
||||
</div>
|
||||
<button id="startTetrisBtn" class="btn-primary">Start Game</button>
|
||||
</div>
|
||||
@@ -336,7 +400,13 @@
|
||||
<h3 id="platformerOverlayTitle">LEVEL COMPLETE</h3>
|
||||
<p id="platformerOverlayScore">Coins: <span id="finalCoins">0</span></p>
|
||||
<p id="platformerOverlayTime">Time: <span id="finalPlatformerTime">0:00</span></p>
|
||||
<button id="restartPlatformerBtn" class="btn-primary">Play Again</button>
|
||||
<div class="input-group">
|
||||
<button id="savePlatformerScoreBtn" class="btn-primary">Save Score</button>
|
||||
<button id="playPlatformerAgainBtn" class="btn-secondary" style="background: var(--trae-green);">Play Again</button>
|
||||
</div>
|
||||
<p id="platformerLoginPrompt" style="display: none; text-align: center; color: #ff6b6b;">
|
||||
<a href="#registration" style="color: var(--trae-cyan);">Register first</a> to save scores
|
||||
</p>
|
||||
</div>
|
||||
<button id="startPlatformerBtn" class="btn-primary">Start Game</button>
|
||||
</div>
|
||||
|
||||
272
style.css
272
style.css
@@ -35,7 +35,7 @@ body {
|
||||
}
|
||||
|
||||
.app-container {
|
||||
max-width: 1200px;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
width: 100%;
|
||||
@@ -913,30 +913,158 @@ body {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.leaderboard h3 {
|
||||
font-family: var(--font-accent);
|
||||
text-align: center;
|
||||
.leaderboard-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.leaderboard-header h3 {
|
||||
font-family: var(--font-accent);
|
||||
color: var(--aurora-cyan);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
background: var(--glass-bg);
|
||||
border: 1px solid var(--glass-border);
|
||||
color: var(--text-primary);
|
||||
padding: 8px 16px;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
font-family: var(--font-accent);
|
||||
font-size: 0.85rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.tab-btn:hover {
|
||||
background: rgba(0, 255, 102, 0.2);
|
||||
border-color: var(--trae-green);
|
||||
}
|
||||
|
||||
.tab-btn.active {
|
||||
background: var(--trae-green);
|
||||
color: var(--bg-dark);
|
||||
border-color: var(--trae-green);
|
||||
}
|
||||
|
||||
.leader-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 10px 20px;
|
||||
padding: 12px 20px;
|
||||
border-bottom: 1px solid var(--glass-border);
|
||||
font-family: var(--font-accent);
|
||||
align-items: center;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
.leader-item:hover {
|
||||
background: rgba(0, 255, 102, 0.1);
|
||||
}
|
||||
|
||||
.leader-item:last-child {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.leader-rank { color: var(--trae-green); }
|
||||
.leader-name { flex: 1; margin-left: 20px; }
|
||||
.leader-score { color: var(--aurora-cyan); }
|
||||
.leader-duration { color: #ff9900; font-size: 0.85rem; margin-left: 10px; }
|
||||
.leader-date { color: #888; font-size: 0.8rem; margin-left: 10px; }
|
||||
.leader-rank {
|
||||
color: var(--trae-green);
|
||||
font-weight: bold;
|
||||
min-width: 40px;
|
||||
}
|
||||
|
||||
.leader-name {
|
||||
flex: 1;
|
||||
margin-left: 20px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.leader-score {
|
||||
color: var(--aurora-cyan);
|
||||
font-weight: bold;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.leader-game {
|
||||
color: #ff6600;
|
||||
font-size: 0.8rem;
|
||||
padding: 4px 10px;
|
||||
border-radius: 8px;
|
||||
background: rgba(255, 102, 0, 0.1);
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.leader-code {
|
||||
color: #7000ff;
|
||||
font-size: 0.75rem;
|
||||
margin-left: 10px;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
background: rgba(112, 0, 255, 0.1);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.leader-code:hover {
|
||||
background: rgba(112, 0, 255, 0.2);
|
||||
}
|
||||
|
||||
.leader-duration {
|
||||
color: #ff9900;
|
||||
font-size: 0.85rem;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.leader-date {
|
||||
color: #888;
|
||||
font-size: 0.8rem;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.code-hint {
|
||||
color: #888;
|
||||
font-size: 0.8rem;
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.pin-explainer {
|
||||
color: var(--aurora-cyan);
|
||||
font-size: 0.75rem;
|
||||
text-align: center;
|
||||
margin-top: 8px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 8px;
|
||||
background: rgba(0, 255, 255, 0.1);
|
||||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.export-buttons {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
justify-content: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.export-btn {
|
||||
background: var(--glass-bg);
|
||||
border: 1px solid var(--glass-border);
|
||||
color: var(--text-primary);
|
||||
padding: 10px 20px;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
font-family: var(--font-accent);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.export-btn:hover {
|
||||
background: rgba(0, 255, 102, 0.2);
|
||||
border-color: var(--trae-green);
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: scale(0.9); }
|
||||
@@ -1039,24 +1167,132 @@ footer span, .glm-link, .author-link, .gemini-link {
|
||||
.forge-input-group { flex-direction: column; }
|
||||
.btn-secondary { margin-left: 0; margin-top: 15px; }
|
||||
|
||||
.app-container { padding: 0 10px; }
|
||||
|
||||
.hero h1 { font-size: 2rem; }
|
||||
.app-icons { gap: 15px; }
|
||||
.app-icon-inner { width: 60px; height: 60px; font-size: 30px; }
|
||||
.app-label { font-size: 0.75rem; }
|
||||
|
||||
.game-container {
|
||||
padding: 15px;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.section-card {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.game-container, .traoom-container, .neonpuzzle-container, .rhythm-container, .arena-container, .platformer-container {
|
||||
height: 50vh;
|
||||
min-height: 350px;
|
||||
}
|
||||
|
||||
canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.game-overlay {
|
||||
padding: 25px 20px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.input-group input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.btn-primary, .export-btn {
|
||||
padding: 10px 15px;
|
||||
font-size: 0.9rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.leaderboard-header {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
padding: 8px 10px;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.leader-item {
|
||||
flex-wrap: wrap;
|
||||
padding: 10px;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.leader-game, .leader-code {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.game-timer {
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.game-ui > div {
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.traoom-container {
|
||||
height: 60vh;
|
||||
min-height: 400px;
|
||||
height: 50vh;
|
||||
min-height: 350px;
|
||||
}
|
||||
|
||||
.traoom-stats {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
gap: 5px;
|
||||
left: 8px;
|
||||
top: 8px;
|
||||
}
|
||||
|
||||
.traoom-stat {
|
||||
padding: 6px 12px;
|
||||
padding: 5px 10px;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.traoom-controls p, .neonpuzzle-controls p, .rhythm-container p, .arena-container p {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
footer p {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.traoom-controls p {
|
||||
font-size: 0.8rem;
|
||||
.export-buttons {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.hero h1 { font-size: 1.5rem; }
|
||||
.app-icons { gap: 10px; }
|
||||
.app-icon-inner { width: 50px; height: 50px; font-size: 24px; }
|
||||
.app-label { font-size: 0.7rem; }
|
||||
|
||||
.game-container, .traoom-container, .neonpuzzle-container, .rhythm-container, .arena-container, .platformer-container {
|
||||
height: 45vh;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.game-overlay h3 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.leaderboard-header h3 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user