aviator / index.html
hologramicon's picture
Update index.html
e1de682 verified
<!DOCTYPE html>
<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">&times;</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>