Spaces:
Paused
Paused
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Aviator Game</title> | |
| <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" crossorigin="anonymous" referrerpolicy="no-referrer" /> | |
| <style> | |
| /* General body styles */ | |
| body { | |
| font-family: 'Inter', sans-serif; | |
| background-color: #1a1a1a; /* Dark gray background */ | |
| color: #d0d0d0; /* Light gray text color */ | |
| min-height: 100vh; | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| overflow: hidden; /* Prevent body scrolling */ | |
| } | |
| /* Main game container */ | |
| .game-container { | |
| background-color: #2d2d2d; /* Slightly lighter dark gray background */ | |
| border-radius: 1rem; | |
| padding: 2rem; | |
| box-shadow: 0 10px 15px rgba(0, 0, 0, 0.2); | |
| text-align: center; | |
| width: 100%; | |
| max-width: 600px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 1.5rem; | |
| } | |
| /* Multiplier display style */ | |
| .multiplier-display { | |
| font-size: 4rem; | |
| font-weight: 800; | |
| color: #68d391; /* Muted green for multiplier */ | |
| margin-bottom: 1rem; | |
| text-shadow: 0 0 10px rgba(104, 211, 145, 0.5); | |
| } | |
| /* Plane and graph area */ | |
| .plane-area { | |
| position: relative; | |
| height: 400px; /* Increased height for the graph */ | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| margin-bottom: 1.5rem; | |
| overflow: hidden; | |
| border: 1px solid #4a4a4a; /* Border for the graph area */ | |
| border-radius: 0.5rem; | |
| background-color: #222222; /* Slightly darker background for the graph */ | |
| } | |
| /* Plane icon style */ | |
| .plane { | |
| font-size: 3rem; | |
| position: absolute; /* Dynamically positioned by JS */ | |
| color: #a0a0a0; /* Gray plane color */ | |
| z-index: 10; | |
| } | |
| /* Graph canvas */ | |
| .graph-canvas { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background-color: transparent; | |
| z-index: 5; | |
| } | |
| /* Input group (bet, deposit) */ | |
| .input-group { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 0.5rem; | |
| margin-bottom: 1rem; | |
| } | |
| .input-group label { | |
| font-weight: 600; | |
| color: #a0a0a0; /* Gray label color */ | |
| } | |
| .input-group input { | |
| background-color: #404040; /* Darker gray input background */ | |
| border: 1px solid #505050; /* Medium gray border */ | |
| border-radius: 0.5rem; | |
| padding: 0.75rem 1rem; | |
| color: #d0d0d0; /* Light gray text */ | |
| font-size: 1.125rem; | |
| width: 100%; | |
| max-width: 200px; | |
| text-align: center; | |
| } | |
| /* Buttons group */ | |
| .buttons-group { | |
| display: flex; | |
| gap: 1rem; | |
| justify-content: center; | |
| } | |
| /* Base style for game buttons */ | |
| .game-button { | |
| padding: 0.75rem 1.5rem; | |
| border-radius: 0.75rem; | |
| font-weight: 700; | |
| font-size: 1.125rem; | |
| cursor: pointer; | |
| transition: all 0.2s ease-in-out; | |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
| border: none; | |
| } | |
| .game-button:disabled { | |
| opacity: 0.5; | |
| cursor: not-allowed; | |
| box-shadow: none; | |
| } | |
| /* Start button style */ | |
| .start-button { | |
| background-color: #5cb85c; /* Muted green */ | |
| 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/Cash out button style */ | |
| .stop-button { | |
| background-color: #d9534f; /* Muted red */ | |
| 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); | |
| } | |
| /* Game message display */ | |
| .message-display { | |
| margin-top: 1.5rem; | |
| font-size: 1.25rem; | |
| font-weight: 600; | |
| min-height: 2rem; | |
| } | |
| .message-win { | |
| color: #68d391; /* Muted green for win */ | |
| } | |
| .message-lose { | |
| color: #fc8181; /* Muted red for lose */ | |
| } | |
| /* Game instructions */ | |
| .instructions { | |
| font-size: 0.8rem; | |
| color: #a0a0a0; /* Gray text for instructions */ | |
| margin-top: 1rem; | |
| } | |
| /* Deposit QR code container */ | |
| .qr-deposit-container { | |
| margin-top: 1rem; | |
| text-align: center; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 0.5rem; | |
| } | |
| .qr-deposit-container img { | |
| width: 100px; | |
| height: 100px; | |
| border-radius: 0.5rem; | |
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); | |
| } | |
| /* Custom modal style */ | |
| .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; | |
| } | |
| .modal-content { | |
| background-color: #2d2d2d; /* Dark gray modal background */ | |
| 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; | |
| } | |
| .modal-content h3 { | |
| font-size: 1.8rem; | |
| font-weight: 700; | |
| margin-bottom: 1rem; | |
| color: #d0d0d0; /* Light gray text */ | |
| } | |
| .modal-content p { | |
| font-size: 1.1rem; | |
| color: #a0a0a0; /* Gray text */ | |
| margin-bottom: 1.5rem; | |
| } | |
| .modal-close-button { | |
| position: absolute; | |
| top: 1rem; | |
| right: 1rem; | |
| background: none; | |
| border: none; | |
| color: #a0a0a0; /* Gray close button */ | |
| font-size: 1.5rem; | |
| cursor: pointer; | |
| transition: color 0.2s; | |
| } | |
| .modal-close-button:hover { | |
| color: #d9534f; /* Muted red on hover */ | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="flex flex-col lg:flex-row h-screen bg-gray-900 font-sans"> | |
| <button | |
| id="mobileMenuButton" | |
| class="lg:hidden fixed top-4 left-4 z-50 p-2 rounded-full bg-gray-700 text-white shadow-lg focus:outline-none focus:ring-2 focus:ring-gray-600" | |
| aria-label="Open menu" | |
| > | |
| <i class="fas fa-bars"></i> | |
| </button> | |
| <aside | |
| id="sidebar" | |
| class="fixed inset-y-0 left-0 z-40 w-64 bg-gradient-to-br from-gray-900 to-gray-700 text-white p-6 transform transition-transform duration-300 ease-in-out | |
| -translate-x-full lg:relative lg:translate-x-0 lg:flex-shrink-0 lg:shadow-xl lg:rounded-r-lg" | |
| > | |
| <div class="flex items-center mb-10"> | |
| <i class="fas fa-plane-departure text-gray-400 text-3xl mr-3"></i> | |
| <h1 class="text-3xl font-bold">Aviator</h1> | |
| </div> | |
| <nav class="mb-8"> | |
| <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="bg-gray-800 p-4 rounded-lg shadow-inner"> | |
| <p class="text-gray-400 text-lg font-medium">Total Balance</p> | |
| <h3 id="totalBalanceDisplay" class="text-4xl font-extrabold text-green-400 mt-2">$0.00</h3> | |
| </div> | |
| <div class="mt-6 flex flex-col gap-4"> | |
| <div class="qr-deposit-container"> | |
| <img src="https://huggingface.co/spaces/hologramicon/roulette/resolve/main/1.png" alt="QR Code for Deposit"> | |
| <label class="text-gray-400">Scan to Deposit</label> | |
| </div> | |
| <button id="depositViaQrButton" class="game-button start-button w-full mt-4">Deposit via QR</button> | |
| <button id="withdrawButton" class="game-button stop-button w-full mt-4">Withdraw</button> | |
| </div> | |
| </aside> | |
| <main class="flex-1 p-6 lg:p-10 overflow-auto flex flex-col items-center justify-center"> | |
| <div | |
| id="sidebarOverlay" | |
| class="fixed inset-0 bg-black bg-opacity-50 z-30 hidden lg:hidden" | |
| ></div> | |
| <div class="game-container"> | |
| <div class="plane-area"> | |
| <canvas id="graphCanvas" class="graph-canvas"></canvas> | |
| <span id="plane" class="plane">✈️</span> | |
| </div> | |
| <div class="multiplier-display" id="multiplierDisplay">1.00x</div> | |
| <div class="input-group"> | |
| <label for="betAmount">Bet (USD):</label> | |
| <input type="number" id="betAmount" value="10.00" min="1" step="0.01"> | |
| </div> | |
| <div class="buttons-group"> | |
| <button id="startButton" class="game-button start-button" disabled>Start Flight</button> | |
| <button id="stopButton" class="game-button stop-button" disabled>Cash Out</button> | |
| </div> | |
| <div class="message-display" id="messageDisplay">Place your bet and press Start Flight!</div> | |
| <p class="instructions"> | |
| Instructions: Place your bet and press "Start Flight". Press "Cash Out" before the plane crashes to multiply your bet. If the plane crashes before you cash out, you lose. | |
| </p> | |
| </div> | |
| </main> | |
| </div> | |
| <div id="messageModal" class="modal-overlay hidden"> | |
| <div class="modal-content"> | |
| <button class="modal-close-button" id="modalCloseButton">×</button> | |
| <h3 id="modalTitle"></h3> | |
| <p id="modalMessage"></p> | |
| </div> | |
| </div> | |
| <script type="module"> | |
| // Initializes the user balance locally. | |
| // Check sessionStorage for a stored balance, otherwise default to 100.00 USD. | |
| let userBalance = parseFloat(sessionStorage.getItem('aviatorBalance')) || 100.00; | |
| // Get DOM element references | |
| const multiplierDisplay = document.getElementById('multiplierDisplay'); | |
| const startButton = document.getElementById('startButton'); | |
| const stopButton = document.getElementById('stopButton'); | |
| const betAmountInput = document.getElementById('betAmount'); | |
| const messageDisplay = document.getElementById('messageDisplay'); | |
| const plane = document.getElementById('plane'); | |
| const graphCanvas = document.getElementById('graphCanvas'); | |
| const ctx = graphCanvas.getContext('2d'); | |
| const totalBalanceDisplay = document.getElementById('totalBalanceDisplay'); | |
| const depositViaQrButton = document.getElementById('depositViaQrButton'); | |
| const withdrawButton = document.getElementById('withdrawButton'); | |
| // Elements for mobile sidebar | |
| const mobileMenuButton = document.getElementById('mobileMenuButton'); | |
| const sidebar = document.getElementById('sidebar'); | |
| const sidebarOverlay = document.getElementById('sidebarOverlay'); | |
| // Elements for message modal | |
| const messageModal = document.getElementById('messageModal'); | |
| const modalTitle = document.getElementById('modalTitle'); | |
| const modalMessage = document.getElementById('modalMessage'); | |
| const modalCloseButton = document.getElementById('modalCloseButton'); | |
| // Game state variables | |
| let isGameRunning = false; | |
| let currentMultiplier = 1.00; | |
| let gameInterval; | |
| let betAmount = 0; | |
| let graphPoints = []; // Stores points to draw the graph | |
| let gameTime = 0; // Tracks game time for the X-axis | |
| let crashPoint = 0; // The multiplier at which the plane will crash | |
| let gameRoundCounter = parseInt(sessionStorage.getItem('aviatorRoundCounter')) || 0; // Counter for game rounds | |
| // Update balance display on page load | |
| document.addEventListener('DOMContentLoaded', () => { | |
| if (totalBalanceDisplay) { | |
| totalBalanceDisplay.textContent = `$${userBalance.toFixed(2)}`; | |
| } | |
| if (startButton) { | |
| startButton.disabled = false; // Enable start button | |
| } | |
| console.log("Aviator Game: Initial local balance set to $", userBalance.toFixed(2)); | |
| }); | |
| /** | |
| * Displays a custom modal with a title and message. | |
| * @param {string} title - The modal title. | |
| * @param {string} msg - The message to display in the modal. | |
| */ | |
| function showCustomMessage(title, msg) { | |
| modalTitle.textContent = title; | |
| modalMessage.textContent = msg; | |
| messageModal.classList.remove('hidden'); | |
| } | |
| /** | |
| * Hides the custom modal. | |
| */ | |
| function hideCustomMessage() { | |
| messageModal.classList.add('hidden'); | |
| } | |
| // Event listeners for the modal | |
| modalCloseButton.addEventListener('click', hideCustomMessage); | |
| messageModal.addEventListener('click', (e) => { | |
| if (e.target === messageModal) { // Close only if clicking on the overlay | |
| hideCustomMessage(); | |
| } | |
| }); | |
| // Toggle sidebar for mobile | |
| mobileMenuButton.addEventListener('click', () => { | |
| sidebar.classList.toggle('-translate-x-full'); | |
| sidebarOverlay.classList.toggle('hidden'); | |
| mobileMenuButton.setAttribute('aria-label', sidebar.classList.contains('-translate-x-full') ? 'Open menu' : 'Close menu'); | |
| mobileMenuButton.innerHTML = sidebar.classList.contains('-translate-x-full') ? '<i class="fas fa-bars"></i>' : '<i class="fas fa-times"></i>'; | |
| }); | |
| sidebarOverlay.addEventListener('click', () => { | |
| sidebar.classList.add('-translate-x-full'); | |
| sidebarOverlay.classList.add('hidden'); | |
| mobileMenuButton.setAttribute('aria-label', 'Open menu'); | |
| mobileMenuButton.innerHTML = '<i class="fas fa-bars"></i>'; | |
| }); | |
| /** | |
| * Updates the user balance locally, in the display, and in sessionStorage. | |
| * @param {number} newAmount - The new balance amount. | |
| */ | |
| function updateLocalBalance(newAmount) { | |
| userBalance = parseFloat(newAmount.toFixed(2)); // Ensure two decimal places | |
| if (totalBalanceDisplay) { | |
| totalBalanceDisplay.textContent = `$${userBalance.toFixed(2)}`; | |
| } | |
| // Save the updated balance to sessionStorage | |
| sessionStorage.setItem('aviatorBalance', userBalance.toFixed(2)); | |
| console.log("Local balance updated and saved to sessionStorage: $", userBalance.toFixed(2)); | |
| } | |
| // Event listener for the "Deposit via QR" button | |
| depositViaQrButton.addEventListener('click', () => { | |
| showCustomMessage("Deposit Funds", "Use the QR to deposit funds into your account."); | |
| }); | |
| // Event listener for the "Withdraw" button | |
| withdrawButton.addEventListener('click', () => { | |
| showCustomMessage("Withdrawal Blocked", "You need to deposit $50 USDT to withdraw your funds."); | |
| }); | |
| /** | |
| * Resizes the graph canvas to match its container. | |
| */ | |
| function resizeCanvas() { | |
| graphCanvas.width = graphCanvas.offsetWidth; | |
| graphCanvas.height = graphCanvas.offsetHeight; | |
| drawGrid(); // Redraw grid on resize | |
| if (graphPoints.length > 0) { | |
| drawGraph(); // Redraw graph if there are points | |
| } | |
| } | |
| // Event listeners for canvas resize | |
| window.addEventListener('load', resizeCanvas); | |
| window.addEventListener('resize', resizeCanvas); | |
| /** | |
| * Updates the game message display (not the modal). | |
| * @param {string} msg - The message to display. | |
| * @param {string} type - The message type ('info', 'win', 'lose') to apply styles. | |
| */ | |
| function updateMessageDisplay(msg, type = 'info') { | |
| messageDisplay.textContent = msg; | |
| messageDisplay.className = 'message-display'; // Reset classes | |
| if (type === 'win') { | |
| messageDisplay.classList.add('message-win'); | |
| } else if (type === 'lose') { | |
| messageDisplay.classList.add('message-lose'); | |
| } | |
| } | |
| /** | |
| * Draws the background grid on the canvas. | |
| */ | |
| function drawGrid() { | |
| ctx.strokeStyle = '#4a4a4a'; // Grid line color (gray) | |
| ctx.lineWidth = 0.5; | |
| // Draw horizontal lines | |
| const horizontalSpacing = graphCanvas.height / 10; // 10 lines | |
| for (let i = 0; i <= 10; i++) { | |
| const y = i * horizontalSpacing; | |
| ctx.beginPath(); | |
| ctx.moveTo(0, y); | |
| ctx.lineTo(graphCanvas.width, y); | |
| ctx.stroke(); | |
| } | |
| // Draw vertical lines | |
| const verticalSpacing = graphCanvas.width / 10; // 10 lines | |
| for (let i = 0; i <= 10; i++) { | |
| const x = i * verticalSpacing; | |
| ctx.beginPath(); | |
| ctx.moveTo(x, 0); | |
| ctx.lineTo(x, graphCanvas.height); | |
| ctx.stroke(); | |
| } | |
| } | |
| /** | |
| * Draws the graph line and positions the plane. | |
| */ | |
| function drawGraph() { | |
| ctx.clearRect(0, 0, graphCanvas.width, graphCanvas.height); // Clear canvas | |
| drawGrid(); // Redraw grid first | |
| if (graphPoints.length < 2) { | |
| // Position the plane at the start if there's one or no points | |
| const startX = graphPoints.length > 0 ? graphPoints[0].x : 0; | |
| const startY = graphPoints.length > 0 ? graphPoints[0].y : graphCanvas.height; | |
| plane.style.left = `${startX - plane.offsetWidth / 2}px`; | |
| plane.style.top = `${startY - plane.offsetHeight / 2}px`; | |
| return; | |
| } | |
| ctx.beginPath(); | |
| ctx.moveTo(graphPoints[0].x, graphPoints[0].y); // Start at the first point | |
| for (let i = 1; i < graphPoints.length; i++) { | |
| ctx.lineTo(graphPoints[i].x, graphPoints[i].y); // Draw line to subsequent points | |
| } | |
| ctx.strokeStyle = '#68d391'; // Muted green line color | |
| ctx.lineWidth = 3; | |
| ctx.stroke(); | |
| // Position the plane at the end of the line | |
| const lastPoint = graphPoints[graphPoints.length - 1]; | |
| plane.style.left = `${lastPoint.x - plane.offsetWidth / 2}px`; | |
| plane.style.top = `${lastPoint.y - plane.offsetHeight / 2}px`; | |
| } | |
| /** | |
| * Starts the game. | |
| */ | |
| function startGame() { | |
| if (isGameRunning) return; | |
| betAmount = parseFloat(betAmountInput.value); | |
| if (isNaN(betAmount) || betAmount <= 0) { | |
| showCustomMessage("Bet Error", "Please enter a valid bet amount."); | |
| return; | |
| } | |
| if (betAmount > userBalance) { // Check local balance | |
| showCustomMessage("Insufficient Balance", "You do not have enough balance to place this bet."); | |
| return; | |
| } | |
| isGameRunning = true; | |
| startButton.disabled = true; | |
| stopButton.disabled = false; | |
| betAmountInput.disabled = true; | |
| currentMultiplier = 1.00; | |
| multiplierDisplay.textContent = '1.00x'; | |
| updateMessageDisplay('The plane is flying!', 'info'); | |
| const newBalanceAfterBet = userBalance - betAmount; // Calculate new balance | |
| updateLocalBalance(newBalanceAfterBet); // Update local balance | |
| // Increment game round counter and save to sessionStorage | |
| gameRoundCounter++; | |
| sessionStorage.setItem('aviatorRoundCounter', gameRoundCounter); | |
| // Determine crash point for this round | |
| // Base crash point: random between 1.01 and a high value (e.g., 200) | |
| crashPoint = 1.01 + Math.random() * 198.99; // Random between 1.01 and 200 | |
| // Apply special multiplier logic | |
| if (gameRoundCounter % 10 === 0) { // Every 10th round, guarantee at least 50x | |
| crashPoint = Math.max(crashPoint, 50.0); | |
| console.log(`Round ${gameRoundCounter}: Guaranteed at least 50x! Crash point set to ${crashPoint.toFixed(2)}x`); | |
| } else if (gameRoundCounter % 5 === 0) { // Every 5th round, guarantee at least 20x | |
| crashPoint = Math.max(crashPoint, 20.0); | |
| console.log(`Round ${gameRoundCounter}: Guaranteed at least 20x! Crash point set to ${crashPoint.toFixed(2)}x`); | |
| } else { | |
| console.log(`Round ${gameRoundCounter}: Normal round. Crash point: ${crashPoint.toFixed(2)}x`); | |
| } | |
| // Reset graph | |
| graphPoints = [{ x: 0, y: graphCanvas.height }]; // Graph starting point at (0, height) | |
| gameTime = 0; | |
| ctx.clearRect(0, 0, graphCanvas.width, graphCanvas.height); // Clear canvas | |
| drawGrid(); // Draw initial grid | |
| // Max multiplier for visual scaling on the graph (plane reaches top at this multiplier) | |
| const visualMaxMultiplier = 7; | |
| gameInterval = setInterval(() => { | |
| currentMultiplier += 0.01; // Increment multiplier | |
| gameTime++; // Increment game time (for X-axis) | |
| multiplierDisplay.textContent = `${currentMultiplier.toFixed(2)}x`; | |
| // Calculate zigzag amplitude based on multiplier for a "gradient" effect | |
| const baseZigzagAmplitude = 10; // Minimum zigzag height | |
| const multiplierZigzagFactor = 5; // How much zigzag increases with multiplier | |
| const zigzagAmplitude = baseZigzagAmplitude + (currentMultiplier * multiplierZigzagFactor); | |
| const zigzagFrequency = 0.2; // Adjust for desired zigzag density | |
| // Map multiplier to canvas Y coordinate (inverted to draw from bottom up) | |
| // The `(currentMultiplier - 1)` ensures that at 1x, the plane starts at the bottom. | |
| // The `visualMaxMultiplier - 1` ensures the scaling is correct for the range. | |
| let y = graphCanvas.height - ((currentMultiplier - 1) / (visualMaxMultiplier - 1)) * graphCanvas.height; | |
| // Add sine wave component for zigzag | |
| y -= (Math.sin(gameTime * zigzagFrequency) * zigzagAmplitude); | |
| y = Math.max(0, y); // Ensure Y does not exceed top of canvas | |
| // Scale X based on time. Adjust divisor for desired speed across the graph. | |
| const x = (gameTime / 100) * graphCanvas.width; // 100 intervals for full width | |
| // Add point to graphPoints, ensuring it stays within canvas width | |
| if (x <= graphCanvas.width) { | |
| graphPoints.push({ x: x, y: y }); | |
| } else { | |
| // If the graph exceeds the width, shift all points to the left | |
| const shiftAmount = x - graphCanvas.width; | |
| graphPoints = graphPoints.map(p => ({ x: p.x - shiftAmount, y: p.y })); | |
| graphPoints.push({ x: graphCanvas.width, y: y }); | |
| } | |
| drawGraph(); // Redraw graph and position plane | |
| // Crash logic: Plane crashes if current multiplier reaches or exceeds the crashPoint | |
| if (currentMultiplier >= crashPoint) { | |
| crashGame(); | |
| } | |
| }, 100); // Update every 100ms | |
| } | |
| /** | |
| * Stops the game and calculates winnings. | |
| */ | |
| function stopGame() { | |
| if (!isGameRunning) return; | |
| clearInterval(gameInterval); | |
| isGameRunning = false; | |
| startButton.disabled = false; | |
| stopButton.disabled = true; | |
| betAmountInput.disabled = false; | |
| const winnings = betAmount * currentMultiplier; | |
| const newBalanceAfterWin = userBalance + winnings; // Calculate new balance | |
| updateLocalBalance(newBalanceAfterWin); // Update local balance | |
| updateMessageDisplay(`Cashed out! You won: $${winnings.toFixed(2)} USD (x${currentMultiplier.toFixed(2)})`, 'win'); | |
| showCustomMessage("Victory!", `You cashed out at x${currentMultiplier.toFixed(2)} and won $${winnings.toFixed(2)} USD.`); | |
| } | |
| /** | |
| * Handles the plane "crash" event. | |
| */ | |
| function crashGame() { | |
| if (!isGameRunning) return; | |
| clearInterval(gameInterval); | |
| isGameRunning = false; | |
| startButton.disabled = false; | |
| stopButton.disabled = true; | |
| betAmountInput.disabled = false; | |
| // Balance was already deducted at the start, no change needed for loss | |
| updateMessageDisplay(`Crashed at x${currentMultiplier.toFixed(2)}! You lost $${betAmount.toFixed(2)} USD.`, 'lose'); | |
| showCustomMessage("Crash!", `The plane crashed at x${currentMultiplier.toFixed(2)}. You lost your bet of $${betAmount.toFixed(2)} USD.`); | |
| } | |
| // Assign event listeners to buttons | |
| startButton.addEventListener('click', startGame); | |
| stopButton.addEventListener('click', stopGame); | |
| </script> | |
| </body> | |
| </html> |