asteroid / index.html
hologramicon's picture
Update index.html
50b90f1 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&display=swap" rel="stylesheet">
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" xintegrity="sha512-9usAa10IRO0HhonpyAIVpjrylPvoDwiPUiKdWk5t3PyolY1cOd4DSE0Ga+ri4AuTroPR5aQvXU9xC6qOPnzFeg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<style>
/* Base styles for the body and main layout container */
body {
font-family: 'Inter', sans-serif;
background-color: #1a1a1a; /* Dark gray from Mini Bonanza example */
color: #d0d0d0; /* Light gray text from Mini Bonanza example */
min-height: 100vh;
overflow: hidden; /* Prevent scrollbars */
display: flex;
justify-content: center;
align-items: center;
}
/* Main layout container to hold sidebar and game content */
.main-layout-container {
display: flex;
flex-direction: column; /* Default to column for small screens */
width: 100%;
height: 100vh; /* Full viewport height */
max-width: 1200px; /* Max width for desktop */
background-color: #1a1a1a; /* Match body background */
}
/* Sidebar styling from Mini Bonanza */
.sidebar {
background: linear-gradient(145deg, #282828, #1c1c1c); /* Dark gray gradient for sidebar */
padding: 1.5rem; /* p-6 from Mini Bonanza */
width: 100%; /* Full width on small screens */
height: auto; /* Auto height on small screens */
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); /* Lighter shadow for mobile */
color: #d0d0d0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
flex-shrink: 0;
}
@media (min-width: 1024px) { /* lg breakpoint */
.main-layout-container {
flex-direction: row; /* Row for desktop */
height: 100vh; /* Full height */
border-radius: 0.75rem; /* rounded-lg */
box-shadow: 0 0 35px rgba(255, 255, 255, 0.15); /* Enhanced shadow */
overflow: hidden;
}
.sidebar {
width: 16rem; /* w-64 from Mini Bonanza */
height: 100%; /* Full height of parent */
border-radius: 0.75rem 0 0 0.75rem; /* rounded-r-lg */
box-shadow: 8px 0 20px rgba(0, 0, 0, 0.4); /* Deeper shadow */
position: relative; /* Not fixed on desktop */
transform: translateX(0) !important; /* Ensure visible on desktop */
}
.mobile-menu-button {
display: none; /* Hide on desktop */
}
.sidebar-overlay {
display: none !important; /* Hide on desktop */
}
}
/* Sidebar specific styles from Mini Bonanza */
.sidebar-title-group {
display: flex;
align-items: center;
margin-bottom: 2.5rem; /* mb-10 */
align-self: flex-start; /* self-start */
width: 100%;
}
.sidebar-title-group .icon {
color: #a0aec0; /* text-gray-400 */
font-size: 1.875rem; /* text-3xl */
margin-right: 0.75rem; /* mr-3 */
}
.sidebar-title-group .title {
font-size: 1.875rem; /* text-3xl */
font-weight: 700; /* font-bold */
color: #fff; /* text-white */
}
.balance-display-container {
background-color: #2d2d2d; /* bg-gray-800 */
padding: 1rem; /* p-4 */
border-radius: 0.5rem; /* rounded-lg */
box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06); /* shadow-inner */
margin-bottom: 1.5rem; /* mb-6 */
width: 100%;
text-align: center;
}
.balance-label {
color: #a0aec0; /* text-gray-400 */
font-size: 1.125rem; /* text-lg */
font-weight: 500; /* font-medium */
display: block;
}
#totalBalanceDisplay {
font-size: 2.25rem; /* text-4xl */
font-weight: 800; /* font-extrabold */
color: #68d391; /* text-green-400 */
margin-top: 0.5rem; /* mt-2 */
display: block;
}
#userIdDisplay {
color: #a0aec0; /* text-gray-500 */
font-size: 0.75rem; /* text-xs */
margin-top: 0.5rem; /* mt-2 */
display: block;
}
.qr-deposit-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-bottom: 1rem; /* mb-4 */
}
.qr-deposit-container .qr-box {
background-color: #fff; /* bg-white */
padding: 1rem; /* p-4 */
border-radius: 0.5rem; /* rounded-lg */
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); /* shadow-md */
border: 1px solid #cbd5e0; /* border border-gray-300 */
}
.qr-deposit-container img {
width: 8rem; /* w-32 */
height: 8rem; /* h-32 */
}
.qr-deposit-container label {
color: #a0aec0; /* text-gray-400 */
font-size: 0.875rem; /* text-sm */
margin-top: 0.5rem; /* mt-2 */
}
.game-button {
padding: 0.75rem 1.5rem;
border-radius: 0.75rem;
font-weight: 700;
font-size: 1rem;
cursor: pointer;
transition: all 0.2s ease-in-out;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
border: none;
width: 100%;
margin-bottom: 1rem;
}
.game-button:last-of-type {
margin-bottom: 0;
}
.game-button:disabled {
opacity: 0.5;
cursor: not-allowed;
box-shadow: none;
}
.start-button {
background-color: #5cb85c;
color: white;
background-image: linear-gradient(to bottom right, #5cb85c, #4cae4c);
}
.start-button:hover:not(:disabled) {
background-color: #4cae4c;
transform: translateY(-2px);
box-shadow: 0 6px 8px rgba(0,0,0,0.2);
}
.stop-button {
background-color: #d9534f;
color: white;
background-image: linear-gradient(to bottom right, #d9534f, #c9302c);
}
.stop-button:hover:not(:disabled) {
background-color: #c9302c;
transform: translateY(-2px);
box-shadow: 0 6px 8px rgba(0,0,0,0.2);
}
.activity-log {
color: #d0d0d0; /* text-gray-300 */
margin-top: 1.5rem; /* mt-6 */
width: 100%;
border-top: 1px solid #4a5568; /* border-t border-gray-700 */
padding-top: 1rem; /* pt-4 */
}
.activity-log h3 {
font-size: 1.125rem; /* text-lg */
font-weight: 600; /* font-semibold */
color: #e0e0e0; /* text-gray-200 */
margin-bottom: 0.75rem; /* mb-3 */
}
.activity-log ul {
color: #a0aec0; /* text-gray-400 */
border-radius: 0.5rem; /* rounded-lg */
border: 1px solid #4a5568; /* border border-gray-700 */
padding: 0.75rem; /* p-3 */
max-height: 10rem; /* max-h-40 */
overflow-y: auto;
font-size: 0.875rem; /* text-sm */
}
.activity-log ul li {
margin-bottom: 0.25rem;
}
/* Game content styling */
.game-content-main {
flex-grow: 1;
padding: 1rem; /* p-4 */
background-color: #1a1a1a;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
overflow: auto;
}
/* Game specific styles */
.game-container {
width: 90%;
max-width: 600px;
aspect-ratio: 3/4; /* Adjust aspect ratio for a taller game area */
background-color: #0d1117; /* Dark space background */
border: 3px solid #667eea; /* Blue border */
position: relative;
overflow: hidden;
border-radius: 0.75rem;
box-shadow: 0 0 20px rgba(102, 126, 234, 0.5);
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between; /* Distribute content vertically */
padding: 1rem;
}
.game-title {
font-family: 'Orbitron', sans-serif;
font-size: clamp(1.5rem, 4vw, 2.5rem);
color: #fff;
text-shadow: 0 0 10px #667eea;
margin-bottom: 1rem;
}
.game-area {
position: relative;
width: 100%;
flex-grow: 1; /* Take available space */
overflow: hidden; /* Crucial for asteroid movement */
background-color: rgba(0,0,0,0.2); /* Slightly transparent background */
border-radius: 0.5rem;
margin-bottom: 1rem;
}
.player-element {
position: absolute;
bottom: 10px; /* Distance from bottom of game-area */
left: 50%;
transform: translateX(-50%);
font-size: clamp(1.5rem, 5vw, 2.5rem); /* Responsive player size */
user-select: none;
z-index: 10;
transition: left 0.05s linear; /* Smooth player movement */
}
.asteroid-element {
position: absolute;
top: -50px; /* Start above visible area */
font-size: clamp(1rem, 4vw, 2rem); /* Responsive asteroid size */
user-select: none;
z-index: 5;
}
.game-info-panel {
display: flex;
justify-content: space-around;
align-items: center;
width: 100%;
padding: 0.5rem;
background-color: rgba(0,0,0,0.5);
border-radius: 0.5rem;
margin-top: 0.5rem;
}
.score-display, .message-area {
font-size: clamp(1rem, 2.5vw, 1.2rem);
font-weight: bold;
color: #39FF14; /* Neon green */
text-shadow: 0 0 8px rgba(57, 255, 20, 0.4);
text-align: center;
width: 100%;
margin-top: 0.5rem;
min-height: 1.5em; /* Ensure space even when empty */
}
.message-area {
color: #ef4444; /* Red for game over */
}
.game-controls-bottom {
display: flex;
gap: 1rem;
margin-top: 1rem;
justify-content: center; /* Center buttons */
width: 100%;
}
.game-controls-bottom .game-button {
padding: 0.5rem 1rem;
font-size: clamp(0.9rem, 2vw, 1.1rem);
border-radius: 0.5rem;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}
/* Custom Modal Styling from Mini Bonanza */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.7);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
}
.modal-overlay.active {
opacity: 1;
visibility: visible;
}
.modal-content {
background-color: #2d2d2d;
padding: 2rem;
border-radius: 1rem;
box-shadow: 0 10px 20px rgba(0,0,0,0.3);
text-align: center;
max-width: 400px;
width: 90%;
position: relative;
transform: translateY(-20px);
transition: transform 0.3s ease;
}
.modal-overlay.active .modal-content {
transform: translateY(0);
}
.modal-content h3 {
font-size: 1.8rem;
font-weight: 700;
margin-bottom: 1rem;
color: #d0d0d0;
}
.modal-content p {
font-size: 1.1rem;
color: #a0a0a0;
margin-bottom: 1.5rem;
}
.modal-close-button {
position: absolute;
top: 1rem;
right: 1rem;
background: none;
border: none;
color: #a0a0a0;
font-size: 1.5rem;
cursor: pointer;
transition: color 0.2s;
}
.modal-close-button:hover {
color: #d9534f;
}
/* Mobile menu specific styles */
.mobile-menu-button {
position: fixed;
top: 1rem;
left: 1rem;
z-index: 50;
padding: 0.5rem;
border-radius: 9999px; /* rounded-full */
background-color: #4a5568; /* bg-gray-700 */
color: #fff; /* text-white */
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); /* shadow-lg */
outline: none;
transition: background-color 0.2s;
}
.mobile-menu-button:focus {
box-shadow: 0 0 0 3px rgba(108, 117, 125, 0.5); /* focus:ring-2 focus:ring-gray-600 */
}
.sidebar.translate-x-0 {
transform: translateX(0);
}
.sidebar.-translate-x-full {
transform: translateX(-100%);
}
.sidebar-overlay {
position: fixed;
inset: 0;
background-color: rgba(0, 0, 0, 0.5); /* bg-black bg-opacity-50 */
z-index: 30;
display: none; /* hidden */
}
.sidebar-overlay.active {
display: block;
}
/* Mobile-only control buttons */
.mobile-only-button {
display: none; /* Hidden by default */
}
@media (max-width: 1023px) { /* Show on screens smaller than lg (desktop) */
.mobile-only-button {
display: block; /* Show on small screens */
padding: 1rem 1.5rem; /* Larger touch area */
font-size: 1.5rem;
}
.game-controls-bottom {
flex-direction: row; /* Keep buttons in a row on mobile */
justify-content: space-around; /* Distribute space */
}
/* Hide keyboard control instructions on mobile if needed */
/* You might add a message here to instruct mobile users to use buttons */
}
</style>
</head>
<body>
<div class="main-layout-container">
<button
id="mobileMenuButton"
class="mobile-menu-button lg:hidden"
aria-label="Open menu">
<i class="fas fa-bars"></i>
</button>
<aside
id="sidebar"
class="sidebar fixed inset-y-0 left-0 z-40 transform -translate-x-full transition-transform duration-300 ease-in-out lg:relative lg:translate-x-0 lg:flex-shrink-0 lg:rounded-r-lg">
<nav class="mb-8 w-full">
<ul>
<li class="mb-4">
<a href="#" class="flex items-center p-3 rounded-lg bg-gray-600 shadow-md">
<i class="fas fa-gamepad mr-3"></i>
<span class="text-lg">Play</span>
</a>
</li>
</ul>
</nav>
<div class="sidebar-title-group">
<i class="fas fa-gem icon"></i>
<h1 class="title">Asteroid</h1>
</div>
<div class="balance-display-container">
<p class="balance-label">Total Balance</p>
<h3 id="totalBalanceDisplay">$0.00</h3>
<p id="userIdDisplay" class="user-id-text"></p>
</div>
<div class="mt-6 flex flex-col gap-4 w-full">
<div class="qr-deposit-container">
<div class="qr-box">
<img src="https://huggingface.co/spaces/hologramicon/roulette/resolve/main/1.png" alt="QR Code for Deposit">
</div>
<label>Scan to Deposit</label>
</div>
<button id="depositViaQrButton" class="game-button start-button">Deposit via QR</button>
<button id="withdrawButton" class="game-button stop-button">Withdraw</button>
<div id="betLog" class="activity-log">
<h3>Recent Activity</h3>
<ul id="activityList">
<li>Balance initialized to $100.00</li>
</ul>
</div>
</div>
</aside>
<div id="sidebarOverlay" class="sidebar-overlay lg:hidden"></div>
<main class="game-content-main">
<div class="game-container">
<div id="gameArea" class="game-area">
<div id="player" class="player-element">🚀</div>
</div>
<div class="game-info-panel">
<span id="scoreDisplay" class="score-display">Score: 0</span>
</div>
<div id="messageArea" class="message-area"></div>
<div class="game-controls-bottom">
<button id="leftArrow" class="game-button mobile-only-button start-button"></button>
<button id="startButton" class="game-button start-button">Start</button>
<button id="restartButton" class="game-button stop-button hidden">Reload</button>
<button id="rightArrow" class="game-button mobile-only-button start-button"></button>
</div>
</div>
</main>
</div>
<div id="customModal" class="modal-overlay">
<div class="modal-content">
<button class="modal-close-button" id="modalCloseButton">&times;</button>
<h3 id="modalTitle"></h3>
<p id="modalMessage"></p>
</div>
</div>
<script>
// --- Global Game State ---
let gameInterval;
let asteroidSpawnInterval;
let score = 0;
let isGameRunning = false;
let playerX = 0; // Player's X position relative to gameArea
let playerWidth = 0; // Will be set dynamically
let gameAreaWidth = 0; // Will be set dynamically
let gameAreaHeight = 0; // Will be set dynamically
const playerSpeed = 8;
let asteroidSpeed = 2;
const asteroids = []; // Array to store asteroid elements
const gameEntryFee = 5.00; // Tarifa de entrada al juego
// --- DOM Elements ---
const gameArea = document.getElementById('gameArea');
const player = document.getElementById('player');
const scoreDisplay = document.getElementById('scoreDisplay');
const messageArea = document.getElementById('messageArea');
const startButton = document.getElementById('startButton');
const restartButton = document.getElementById('restartButton');
const leftArrowButton = document.getElementById('leftArrow');
const rightArrowButton = document.getElementById('rightArrow');
// Sidebar elements (for balance and user ID persistence)
const totalBalanceDisplay = document.getElementById('totalBalanceDisplay');
const userIdDisplay = document.getElementById('userIdDisplay');
const activityListEl = document.getElementById('activityList');
const depositViaQrButton = document.getElementById('depositViaQrButton');
const withdrawButton = document.getElementById('withdrawButton');
// Modal elements
const customModal = document.getElementById('customModal');
const modalTitle = document.getElementById('modalTitle');
const modalMessage = document.getElementById('modalMessage');
const modalCloseButton = document.getElementById('modalCloseButton');
// --- Balance Management (from Mini Bonanza) ---
window.userBalance = parseFloat(localStorage.getItem('esquivaAsteroideBalance')) || 100.00;
/**
* Updates the user's balance and refreshes the display.
* Also saves the balance to localStorage.
* @param {number} newAmount - The new balance amount.
*/
function updateUserBalance(newAmount) {
window.userBalance = newAmount;
totalBalanceDisplay.textContent = `$${window.userBalance.toFixed(2)}`;
localStorage.setItem('esquivaAsteroideBalance', window.userBalance.toFixed(2));
addActivityToLog(`Balance actualizado a $${newAmount.toFixed(2)}`);
}
/**
* Adds a new entry to the activity log displayed in the sidebar.
* @param {string} message - The message to add to the log.
*/
function addActivityToLog(message) {
const li = document.createElement('li');
li.textContent = message;
activityListEl.prepend(li);
if (activityListEl.children.length > 20) {
activityListEl.removeChild(activityListEl.lastChild);
}
}
// --- Modal Functions ---
/**
* Displays a custom modal with a given title and message.
* @param {string} title - The title of the modal.
* @param {string} msg - The message content of the modal.
*/
function showCustomModal(title, msg) {
modalTitle.textContent = title;
modalMessage.textContent = msg;
customModal.classList.add('active');
}
/**
* Hides the custom modal.
*/
function hideCustomModal() {
customModal.classList.remove('active');
}
// --- Game Initialization & Reset ---
/**
* Initializes the game state, resets score, clears asteroids, and sets up UI.
*/
function initializeGame() {
// Set initial dimensions based on gameArea
const gameAreaRect = gameArea.getBoundingClientRect();
gameAreaWidth = gameAreaRect.width;
gameAreaHeight = gameAreaRect.height;
playerWidth = player.offsetWidth;
playerX = (gameAreaWidth / 2) - (playerWidth / 2);
player.style.left = playerX + 'px';
player.style.bottom = '10px'; // Ensure player is at the bottom
score = 0;
asteroidSpeed = 2;
scoreDisplay.textContent = `Puntuación: ${score}`;
messageArea.textContent = 'Esquiva los asteroides!';
restartButton.classList.add('hidden');
startButton.classList.remove('hidden');
// Clear existing asteroids
asteroids.forEach(asteroid => asteroid.element.remove());
asteroids.length = 0; // Clear the array
}
/**
* Starts the game, deducting entry fee and initiating game loop.
*/
function startGame() {
if (isGameRunning) return;
if (window.userBalance < gameEntryFee) {
messageArea.textContent = `¡Saldo insuficiente! Necesitas $${gameEntryFee.toFixed(2)} para jugar.`;
addActivityToLog(`Intento de juego fallido: saldo insuficiente ($${window.userBalance.toFixed(2)}).`);
return;
}
// Deduct game entry fee
updateUserBalance(window.userBalance - gameEntryFee);
addActivityToLog(`Comenzó el juego: se deducen $${gameEntryFee.toFixed(2)}.`);
isGameRunning = true;
startButton.classList.add('hidden');
messageArea.textContent = '¡Vamos!';
score = 0;
scoreDisplay.textContent = `Puntuación: ${score}`;
asteroidSpeed = 2; // Reset speed on start
gameLoop();
asteroidSpawnInterval = setInterval(createAsteroid, 1000); // Spawn every 1 second
}
/**
* Ends the current game, updates balance with score, and shows final message.
*/
function endGame() {
isGameRunning = false;
clearInterval(gameInterval);
clearInterval(asteroidSpawnInterval);
// Add score to balance
updateUserBalance(window.userBalance + score);
addActivityToLog(`Juego terminado. Puntuación: ${score}. Ganancia añadida al balance.`);
messageArea.textContent = `¡Juego Terminado! Puntuación final: ${score}`;
restartButton.classList.remove('hidden');
startButton.classList.add('hidden'); // Hide start button after game over
}
/**
* Resets the game to its initial state.
*/
function resetGame() {
endGame(); // Stop any ongoing game and finalize score/balance
initializeGame(); // Re-initialize state and clear board
}
// --- Player Movement ---
let keys = {};
let isMovingLeft = false;
let isMovingRight = false;
document.addEventListener('keydown', (e) => {
keys[e.key] = true;
});
document.addEventListener('keyup', (e) => {
keys[e.key] = false;
});
/**
* Handles player movement based on keyboard input and mobile button states.
*/
function handlePlayerMovement() {
if (keys['ArrowLeft'] || keys['a'] || isMovingLeft) {
playerX -= playerSpeed;
}
if (keys['ArrowRight'] || keys['d'] || isMovingRight) {
playerX += playerSpeed;
}
// Keep player within game area bounds
if (playerX < 0) playerX = 0;
if (playerX > gameAreaWidth - playerWidth) playerX = gameAreaWidth - playerWidth;
player.style.left = playerX + 'px';
}
// --- Touch Controls for Mobile ---
let touchStartX = 0;
let playerInitialX = 0;
gameArea.addEventListener('touchstart', (e) => {
if (!isGameRunning) return;
touchStartX = e.touches[0].clientX;
playerInitialX = playerX;
e.preventDefault(); // Prevent scrolling
}, { passive: false }); // Use passive: false to allow preventDefault
gameArea.addEventListener('touchmove', (e) => {
if (!isGameRunning) return;
const touchCurrentX = e.touches[0].clientX;
const deltaX = touchCurrentX - touchStartX;
let newPlayerX = playerInitialX + deltaX;
if (newPlayerX < 0) newPlayerX = 0;
if (newPlayerX > gameAreaWidth - playerWidth) newPlayerX = gameAreaWidth - playerWidth;
playerX = newPlayerX;
player.style.left = playerX + 'px';
e.preventDefault(); // Prevent scrolling
}, { passive: false }); // Use passive: false to allow preventDefault
// Event listeners for mobile arrow buttons
leftArrowButton.addEventListener('touchstart', (e) => {
isMovingLeft = true;
e.preventDefault();
});
leftArrowButton.addEventListener('touchend', () => {
isMovingLeft = false;
});
leftArrowButton.addEventListener('mousedown', () => { // For desktop click support
isMovingLeft = true;
});
leftArrowButton.addEventListener('mouseup', () => {
isMovingLeft = false;
});
rightArrowButton.addEventListener('touchstart', (e) => {
isMovingRight = true;
e.preventDefault();
});
rightArrowButton.addEventListener('touchend', () => {
isMovingRight = false;
});
rightArrowButton.addEventListener('mousedown', () => { // For desktop click support
isMovingRight = true;
});
rightArrowButton.addEventListener('mouseup', () => {
isMovingRight = false;
});
// --- Asteroid Logic ---
/**
* Creates a new asteroid element and adds it to the game.
*/
function createAsteroid() {
if (!isGameRunning) return;
const asteroid = document.createElement('div');
asteroid.classList.add('asteroid-element');
const size = Math.random() * 20 + 20; // Size between 20 and 40
asteroid.style.width = size + 'px';
asteroid.style.height = size + 'px';
asteroid.style.left = Math.random() * (gameAreaWidth - size) + 'px';
asteroid.textContent = '🪨'; // Rock emoji
gameArea.appendChild(asteroid);
asteroids.push({
element: asteroid,
x: parseFloat(asteroid.style.left),
y: -size, // Start above the game area
size: size,
speed: asteroidSpeed + Math.random() * 1.5 // Random variation
});
}
/**
* Moves all active asteroids down the screen.
*/
function moveAsteroids() {
for (let i = asteroids.length - 1; i >= 0; i--) {
const asteroid = asteroids[i];
asteroid.y += asteroid.speed;
asteroid.element.style.top = asteroid.y + 'px';
// Remove asteroid if it goes off screen
if (asteroid.y > gameAreaHeight) {
asteroid.element.remove();
asteroids.splice(i, 1);
score++; // Increase score for each asteroid dodged
scoreDisplay.textContent = `Puntuación: ${score}`;
// Increase difficulty over time
if (score % 10 === 0 && asteroidSpeed < 10) { // Increase speed every 10 points up to a limit
asteroidSpeed += 0.5;
messageArea.textContent = `¡Velocidad aumentada!`;
}
}
}
}
// --- Collision Detection ---
/**
* Checks for collisions between the player and any asteroids.
*/
function checkCollision() {
const playerRect = player.getBoundingClientRect();
const gameAreaRect = gameArea.getBoundingClientRect(); // Get game area position
// Adjust playerRect coordinates relative to gameArea
const adjustedPlayerRect = {
left: playerRect.left - gameAreaRect.left,
right: playerRect.right - gameAreaRect.left,
top: playerRect.top - gameAreaRect.top,
bottom: playerRect.bottom - gameAreaRect.top,
width: playerRect.width,
height: playerRect.height
};
for (let i = asteroids.length - 1; i >= 0; i--) {
const asteroid = asteroids[i];
const asteroidRect = asteroid.element.getBoundingClientRect();
// Adjust asteroidRect coordinates relative to gameArea
const adjustedAsteroidRect = {
left: asteroidRect.left - gameAreaRect.left,
right: asteroidRect.right - gameAreaRect.left,
top: asteroidRect.top - gameAreaRect.top,
bottom: asteroidRect.bottom - gameAreaRect.top,
width: asteroidRect.width,
height: asteroidRect.height
};
// Simple AABB collision detection
if (
adjustedPlayerRect.left < adjustedAsteroidRect.left + adjustedAsteroidRect.width &&
adjustedPlayerRect.left + adjustedPlayerRect.width > adjustedAsteroidRect.left &&
adjustedPlayerRect.top < adjustedAsteroidRect.top + adjustedAsteroidRect.height &&
adjustedPlayerRect.top + adjustedPlayerRect.height > adjustedAsteroidRect.top
) {
endGame();
break;
}
}
}
// --- Game Loop ---
/**
* The main game loop, updates game state and renders frames.
*/
function gameLoop() {
if (!isGameRunning) return;
handlePlayerMovement();
moveAsteroids();
checkCollision();
gameInterval = requestAnimationFrame(gameLoop);
}
// --- Event Listeners ---
startButton.addEventListener('click', startGame);
restartButton.addEventListener('click', resetGame);
// Modal event listeners
modalCloseButton.addEventListener('click', hideCustomModal);
customModal.addEventListener('click', (e) => {
if (e.target === customModal) hideCustomModal();
});
// Sidebar deposit/withdraw buttons
depositViaQrButton.addEventListener('click', () => {
showCustomModal("Depositar Fondos", "Usa el código QR para depositar fondos en tu cuenta.");
addActivityToLog("Botón de depósito clicado.");
});
withdrawButton.addEventListener('click', () => {
showCustomModal("Información", "Deposita $50 USDT para habilitar retiros.");
addActivityToLog("Botón de retiro clicado.");
});
// Mobile menu toggle functionality
const mobileMenuButton = document.getElementById('mobileMenuButton');
const sidebar = document.getElementById('sidebar');
const sidebarOverlay = document.getElementById('sidebarOverlay');
mobileMenuButton.addEventListener('click', () => {
sidebar.classList.toggle('-translate-x-full');
sidebarOverlay.classList.toggle('active');
});
sidebarOverlay.addEventListener('click', () => {
sidebar.classList.add('-translate-x-full');
sidebarOverlay.classList.remove('active');
});
// Initial setup on page load
window.onload = () => {
// Generate a simple unique ID for the user if not already present
let userId = localStorage.getItem('esquivaAsteroideUserId');
if (!userId) {
userId = 'user_' + Math.random().toString(36).substr(2, 9);
localStorage.setItem('esquivaAsteroideUserId', userId);
}
userIdDisplay.textContent = `User ID: ${userId}`;
updateUserBalance(window.userBalance); // Set initial balance display
initializeGame(); // Set up initial game state
};
// Handle window resize to adjust game area dimensions
window.addEventListener('resize', () => {
const gameAreaRect = gameArea.getBoundingClientRect();
gameAreaWidth = gameAreaRect.width;
gameAreaHeight = gameAreaRect.height;
// Re-position player if necessary after resize
playerX = (gameAreaWidth / 2) - (playerWidth / 2);
player.style.left = playerX + 'px';
});
</script>
</body>
</html>