anycoder-b047da41 / index.html
HAL1993's picture
Upload folder using huggingface_hub
e076712 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bitcoin Dino Runner</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
overflow: hidden;
font-family: 'Arial', sans-serif;
background: #121212;
touch-action: manipulation;
}
#gameContainer {
position: relative;
width: 100vw;
height: 100vh;
}
#gameCanvas {
display: block;
background: linear-gradient(to bottom, #87CEEB, #E0F7FA);
}
#uiContainer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
#hud {
position: absolute;
top: 20px;
left: 20px;
color: white;
font-size: 18px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
#btcBalance {
font-family: 'Courier New', monospace;
font-weight: bold;
color: #FFD700;
}
#startScreen, #gameOverScreen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, 0.7);
color: white;
z-index: 10;
}
#gameOverScreen {
display: none;
}
.title {
font-size: 48px;
margin-bottom: 30px;
color: #FFD700;
text-shadow: 3px 3px 6px rgba(0, 0, 0, 0.8);
}
.subtitle {
font-size: 24px;
margin-bottom: 40px;
text-align: center;
}
.btn {
padding: 15px 30px;
font-size: 20px;
background: linear-gradient(to right, #FFD700, #FFA500);
border: none;
border-radius: 30px;
color: #121212;
cursor: pointer;
pointer-events: auto;
transition: transform 0.2s, box-shadow 0.2s;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
.btn:hover {
transform: translateY(-3px);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.4);
}
.btn:active {
transform: translateY(1px);
}
#controlsInfo {
margin-top: 30px;
font-size: 16px;
opacity: 0.8;
}
#header {
position: absolute;
top: 10px;
right: 10px;
color: white;
font-size: 12px;
z-index: 20;
}
#header a {
color: #FFD700;
text-decoration: none;
}
@media (max-width: 768px) {
.title {
font-size: 36px;
}
.subtitle {
font-size: 18px;
}
.btn {
padding: 12px 24px;
font-size: 18px;
}
#hud {
font-size: 16px;
}
}
</style>
</head>
<body>
<div id="gameContainer">
<canvas id="gameCanvas"></canvas>
<div id="uiContainer">
<div id="hud">
<div>Distance: <span id="distance">0</span>m</div>
<div>BTC: <span id="btcBalance">0.00000000</span></div>
<div>Speed: <span id="speed">1</span>x</div>
</div>
<div id="startScreen">
<h1 class="title">Bitcoin Dino Runner</h1>
<p class="subtitle">Collect BTC while avoiding obstacles!</p>
<button class="btn" id="startBtn">Start Game</button>
<div id="controlsInfo">
<p>Controls: Arrow Keys or Swipe</p>
<p>↑ Jump | ↓ Slide | ← → Move</p>
</div>
</div>
<div id="gameOverScreen">
<h1 class="title">Game Over</h1>
<p class="subtitle">You collected <span id="finalBtc">0.00000000</span> BTC!</p>
<button class="btn" id="restartBtn">Play Again</button>
</div>
</div>
<div id="header">
Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a>
</div>
</div>
<script>
// Game Constants
const LANES = 3;
const LANE_WIDTH = 100;
const PLAYER_WIDTH = 60;
const PLAYER_HEIGHT = 80;
const JUMP_HEIGHT = 120;
const JUMP_DURATION = 800; // ms
const SLIDE_DURATION = 1000; // ms
const BASE_SPEED = 5;
const SPEED_INCREASE = 0.001;
const OBSTACLE_SPAWN_RATE = 120; // frames
const COIN_SPAWN_RATE = 30; // frames
const POWERUP_SPAWN_RATE = 500; // frames
const COIN_VALUE = 0.00000001;
const BIG_COIN_VALUE = 0.00000005;
// Game Variables
let canvas, ctx;
let gameWidth, gameHeight;
let gameActive = false;
let gameOver = false;
let distance = 0;
let btcBalance = 0;
let gameSpeed = 1;
let currentLane = 1; // 0: left, 1: middle, 2: right
let playerState = 'running'; // running, jumping, sliding
let jumpStartTime = 0;
let slideStartTime = 0;
let lastObstacleSpawn = 0;
let lastCoinSpawn = 0;
let lastPowerupSpawn = 0;
let obstacles = [];
let coins = [];
let powerups = [];
let particles = [];
let backgroundOffset = 0;
let midgroundOffset = 0;
let foregroundOffset = 0;
let animationFrameId;
let lastTime = 0;
let combo = 0;
let comboTimeout = null;
let powerupActive = null;
let powerupEndTime = 0;
let shakeOffset = { x: 0, y: 0 };
let magnetRadius = 0;
// DOM Elements
const distanceElement = document.getElementById('distance');
const btcBalanceElement = document.getElementById('btcBalance');
const speedElement = document.getElementById('speed');
const startScreen = document.getElementById('startScreen');
const gameOverScreen = document.getElementById('gameOverScreen');
const finalBtcElement = document.getElementById('finalBtc');
const startBtn = document.getElementById('startBtn');
const restartBtn = document.getElementById('restartBtn');
// Initialize game
function init() {
canvas = document.getElementById('gameCanvas');
ctx = canvas.getContext('2d');
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// Event listeners
startBtn.addEventListener('click', startGame);
restartBtn.addEventListener('click', restartGame);
// Keyboard controls
document.addEventListener('keydown', handleKeyDown);
// Touch controls
canvas.addEventListener('touchstart', handleTouchStart);
canvas.addEventListener('touchmove', handleTouchMove);
// Preload assets (in a real game, you'd want to preload images)
drawStartScreen();
}
function resizeCanvas() {
gameWidth = window.innerWidth;
gameHeight = window.innerHeight;
canvas.width = gameWidth;
canvas.height = gameHeight;
// Redraw if needed
if (!gameActive) {
drawStartScreen();
}
}
function drawStartScreen() {
// Draw animated dinosaur on start screen
ctx.clearRect(0, 0, gameWidth, gameHeight);
// Draw background
drawBackground(0);
// Draw dinosaur
const dinoX = gameWidth / 2;
const dinoY = gameHeight / 2 + 50;
const headBob = Math.sin(Date.now() / 200) * 5;
drawDino(dinoX, dinoY + headBob, 1.5, 'running');
}
function startGame() {
// Reset game state
gameActive = true;
gameOver = false;
distance = 0;
btcBalance = 0;
gameSpeed = 1;
currentLane = 1;
playerState = 'running';
obstacles = [];
coins = [];
powerups = [];
particles = [];
combo = 0;
powerupActive = null;
// Update UI
startScreen.style.display = 'none';
gameOverScreen.style.display = 'none';
updateHUD();
// Start game loop
lastTime = performance.now();
gameLoop(lastTime);
}
function restartGame() {
gameOverScreen.style.display = 'none';
startGame();
}
function endGame() {
gameActive = false;
gameOver = true;
finalBtcElement.textContent = btcBalance.toFixed(8);
gameOverScreen.style.display = 'flex';
cancelAnimationFrame(animationFrameId);
}
function gameLoop(timestamp) {
if (!gameActive) return;
const deltaTime = timestamp - lastTime;
lastTime = timestamp;
// Update game state
update(deltaTime);
// Render game
render();
// Continue loop
animationFrameId = requestAnimationFrame(gameLoop);
}
function update(deltaTime) {
// Increase distance and speed
distance += gameSpeed;
gameSpeed = BASE_SPEED + (distance / 10000) * SPEED_INCREASE;
// Update player state
updatePlayer(deltaTime);
// Spawn obstacles and coins
spawnObjects();
// Update objects
updateObstacles();
updateCoins();
updatePowerups();
updateParticles();
// Check collisions
checkCollisions();
// Update powerups
updateActivePowerup(timestamp);
// Update combo
if (combo > 0 && comboTimeout && timestamp > comboTimeout) {
combo = 0;
comboTimeout = null;
}
// Update HUD
updateHUD();
// Screen shake at high speeds
if (gameSpeed > 10) {
shakeOffset.x = (Math.random() - 0.5) * 2 * (gameSpeed - 10) / 2;
shakeOffset.y = (Math.random() - 0.5) * 2 * (gameSpeed - 10) / 2;
} else {
shakeOffset.x = 0;
shakeOffset.y = 0;
}
}
function updatePlayer(deltaTime) {
// Handle jump animation
if (playerState === 'jumping') {
const jumpProgress = (performance.now() - jumpStartTime) / JUMP_DURATION;
if (jumpProgress >= 1) {
playerState = 'running';
}
}
// Handle slide animation
if (playerState === 'sliding') {
const slideProgress = (performance.now() - slideStartTime) / SLIDE_DURATION;
if (slideProgress >= 1) {
playerState = 'running';
}
}
}
function spawnObjects() {
// Spawn obstacles
if (distance - lastObstacleSpawn > OBSTACLE_SPAWN_RATE / gameSpeed) {
const lane = Math.floor(Math.random() * LANES);
const type = Math.random() > 0.3 ? 'barrier' : 'gap';
obstacles.push({
x: getLaneX(lane),
y: -100,
width: LANE_WIDTH - 20,
height: type === 'gap' ? 0 : 60,
type: type,
lane: lane
});
lastObstacleSpawn = distance;
}
// Spawn coins
if (distance - lastCoinSpawn > COIN_SPAWN_RATE / gameSpeed) {
const lane = Math.floor(Math.random() * LANES);
const isBig = Math.random() > 0.9;
coins.push({
x: getLaneX(lane),
y: -50,
radius: isBig ? 25 : 20,
value: isBig ? BIG_COIN_VALUE : COIN_VALUE,
isBig: isBig,
rotation: 0,
rotationSpeed: Math.random() * 0.1 + 0.05
});
lastCoinSpawn = distance;
}
// Spawn powerups
if (distance - lastPowerupSpawn > POWERUP_SPAWN_RATE / gameSpeed && !powerupActive) {
const lane = Math.floor(Math.random() * LANES);
const types = ['magnet', 'multiplier', 'shield', 'speed'];
const type = types[Math.floor(Math.random() * types.length)];
powerups.push({
x: getLaneX(lane),
y: -50,
radius: 25,
type: type,
rotation: 0
});
lastPowerupSpawn = distance;
}
}
function updateObstacles() {
for (let i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].y += gameSpeed;
// Remove off-screen obstacles
if (obstacles[i].y > gameHeight) {
obstacles.splice(i, 1);
}
}
}
function updateCoins() {
for (let i = coins.length - 1; i >= 0; i--) {
coins[i].y += gameSpeed;
coins[i].rotation += coins[i].rotationSpeed;
// Coin magnet effect
if (powerupActive === 'magnet' && magnetRadius > 0) {
const dx = coins[i].x - getLaneX(currentLane);
const dy = coins[i].y - (gameHeight - 150);
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < magnetRadius) {
const pullStrength = (magnetRadius - distance) / magnetRadius * 10;
coins[i].x -= dx * 0.05 * pullStrength;
coins[i].y -= dy * 0.05 * pullStrength;
}
}
// Remove off-screen coins
if (coins[i].y > gameHeight + 50) {
coins.splice(i, 1);
}
}
// Update magnet radius
if (powerupActive === 'magnet') {
magnetRadius = 200 + Math.sin(Date.now() / 200) * 20;
} else {
magnetRadius = 0;
}
}
function updatePowerups() {
for (let i = powerups.length - 1; i >= 0; i--) {
powerups[i].y += gameSpeed;
powerups[i].rotation += 0.05;
// Remove off-screen powerups
if (powerups[i].y > gameHeight + 50) {
powerups.splice(i, 1);
}
}
}
function updateParticles() {
for (let i = particles.length - 1; i >= 0; i--) {
particles[i].x += particles[i].vx;
particles[i].y += particles[i].vy;
particles[i].life--;
if (particles[i].life <= 0) {
particles.splice(i, 1);
}
}
}
function updateActivePowerup(timestamp) {
if (powerupActive && timestamp > powerupEndTime) {
powerupActive = null;
powerupEndTime = 0;
}
}
function checkCollisions() {
const playerX = getLaneX(currentLane);
const playerY = gameHeight - 150;
const playerHeight = playerState === 'sliding' ? PLAYER_HEIGHT / 2 : PLAYER_HEIGHT;
const playerBottom = playerY + playerHeight;
// Check coin collisions
for (let i = coins.length - 1; i >= 0; i--) {
const dx = coins[i].x - playerX;
const dy = coins[i].y - playerY;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < coins[i].radius + PLAYER_WIDTH / 2) {
// Collect coin
let value = coins[i].value;
// Apply multiplier if active
if (powerupActive === 'multiplier') {
value *= 2;
}
btcBalance += value;
coins.splice(i, 1);
// Add combo
combo++;
if (comboTimeout) clearTimeout(comboTimeout);
comboTimeout = timestamp + 2000; // 2 second combo window
// Create particles
createParticles(coins[i].x, coins[i].y, coins[i].isBig ? 'gold' : 'yellow', 10);
}
}
// Check powerup collisions
for (let i = powerups.length - 1; i >= 0; i--) {
const dx = powerups[i].x - playerX;
const dy = powerups[i].y - playerY;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < powerups[i].radius + PLAYER_WIDTH / 2) {
// Activate powerup
powerupActive = powerups[i].type;
powerupEndTime = performance.now() + 10000; // 10 seconds
powerups.splice(i, 1);
// Create particles
createParticles(powerups[i].x, powerups[i].y, 'blue', 15);
}
}
// Check obstacle collisions
if (powerupActive === 'shield') return; // Shield protects from collisions
for (let i = 0; i < obstacles.length; i++) {
const obstacle = obstacles[i];
// Check if in same lane
if (obstacle.lane !== currentLane) continue;
// Check vertical position
if (obstacle.y + obstacle.height < playerY) continue;
if (obstacle.y > playerBottom) continue;
// For gaps, check if player is jumping
if (obstacle.type === 'gap' && playerState !== 'jumping') {
endGame();
return;
}
// For barriers, check if player is sliding
if (obstacle.type === 'barrier') {
if (playerState !== 'sliding' || obstacle.y > playerY + PLAYER_HEIGHT / 2) {
endGame();
return;
}
}
}
}
function createParticles(x, y, color, count) {
for (let i = 0; i < count; i++) {
particles.push({
x: x,
y: y,
vx: (Math.random() - 0.5) * 4,
vy: (Math.random() - 0.5) * 4,
radius: Math.random() * 3 + 2,
color: color,
life: Math.random() * 30 + 30
});
}
}
function updateHUD() {
distanceElement.textContent = Math.floor(distance / 10);
btcBalanceElement.textContent = btcBalance.toFixed(8);
speedElement.textContent = gameSpeed.toFixed(2);
}
function render() {
// Clear canvas with shake offset
ctx.save();
ctx.translate(shakeOffset.x, shakeOffset.y);
ctx.clearRect(0, 0, gameWidth, gameHeight);
// Draw background layers with parallax
drawBackground(backgroundOffset);
// Draw game objects
drawObstacles();
drawPowerups();
drawCoins();
drawParticles();
// Draw player
const playerX = getLaneX(currentLane);
const playerY = gameHeight - 150;
drawDino(playerX, playerY, 1, playerState);
// Draw powerup indicator
if (powerupActive) {
drawPowerupIndicator();
}
// Draw combo
if (combo > 1) {
drawCombo();
}
ctx.restore();
}
function drawBackground(offset) {
// Sky gradient
const skyGradient = ctx.createLinearGradient(0, 0, 0, gameHeight);
skyGradient.addColorStop(0, '#87CEEB');
skyGradient.addColorStop(1, '#E0F7FA');
ctx.fillStyle = skyGradient;
ctx.fillRect(0, 0, gameWidth, gameHeight);
// Distant mountains
ctx.fillStyle = '#5D4037';
for (let i = 0; i < 5; i++) {
const x = (i * 400 - offset * 0.2) % (gameWidth + 400) - 200;
ctx.beginPath();
ctx.moveTo(x, gameHeight - 200);
ctx.lineTo(x + 200, gameHeight - 300);
ctx.lineTo(x + 400, gameHeight - 200);
ctx.fill();
}
// Ground
ctx.fillStyle = '#8BC34A';
ctx.fillRect(0, gameHeight - 100, gameWidth, 100);
// Lane markers
ctx.strokeStyle = '#FFFFFF';
ctx.lineWidth = 2;
for (let lane = 1; lane < LANES; lane++) {
const x = getLaneX(lane) - LANE_WIDTH / 2;
ctx.setLineDash([20, 20]);
ctx.beginPath();
ctx.moveTo(x, gameHeight - 100);
ctx.lineTo(x, gameHeight);
ctx.stroke();
}
ctx.setLineDash([]);
// Update background offsets
backgroundOffset += gameSpeed * 0.2;
midgroundOffset += gameSpeed * 0.5;
foregroundOffset += gameSpeed;
}
function drawDino(x, y, scale, state) {
ctx.save();
ctx.translate(x, y);
ctx.scale(scale, scale);
// Body color
ctx.fillStyle = '#4CAF50';
// Calculate animation offsets
let bodyYOffset = 0;
let headBob = 0;
let legOffset = 0;
if (state === 'running') {
headBob = Math.sin(Date.now() / 200) * 5;
legOffset = Math.sin(Date.now() / 100) * 10;
} else if (state === 'jumping') {
const jumpProgress = (performance.now() - jumpStartTime) / JUMP_DURATION;
const jumpHeight = JUMP_HEIGHT * Math.sin(jumpProgress * Math.PI);
bodyYOffset = -jumpHeight;
} else if (state === 'sliding') {
bodyYOffset = PLAYER_HEIGHT / 4;
}
// Body
ctx.beginPath();
ctx.ellipse(0, bodyYOffset + 20, 30, 40, 0, 0, Math.PI * 2);
ctx.fill();
// Head
ctx.beginPath();
ctx.ellipse(0, bodyYOffset - 30 + headBob, 25, 20, 0, 0, Math.PI * 2);
ctx.fill();
// Eyes
ctx.fillStyle = 'white';
ctx.beginPath();
ctx.arc(-10, bodyYOffset - 35 + headBob, 5, 0, Math.PI * 2);
ctx.arc(10, bodyYOffset - 35 + headBob, 5, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = 'black';
ctx.beginPath();
ctx.arc(-10, bodyYOffset - 35 + headBob, 2, 0, Math.PI * 2);
ctx.arc(10, bodyYOffset - 35 + headBob, 2, 0, Math.PI * 2);
ctx.fill();
// Headband
ctx.strokeStyle = 'red';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.arc(0, bodyYOffset - 30 + headBob, 20, 0, Math.PI * 2);
ctx.stroke();
// Bitcoin symbol on headband
ctx.fillStyle = 'gold';
ctx.font = 'bold 16px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('₿', 0, bodyYOffset - 30 + headBob);
// Legs
ctx.fillStyle = '#4CAF50';
if (state === 'sliding') {
// Sliding pose
ctx.beginPath();
ctx.ellipse(-20, bodyYOffset + 50, 10, 5, Math.PI/4, 0, Math.PI * 2);
ctx.ellipse(20, bodyYOffset + 50, 10, 5, -Math.PI/4, 0, Math.PI * 2);
ctx.fill();
} else {
// Running/jumping pose
ctx.beginPath();
ctx.ellipse(-15 + legOffset, bodyYOffset + 50, 10, 5, 0, 0, Math.PI * 2);
ctx.ellipse(15 - legOffset, bodyYOffset + 50, 10, 5, 0, 0, Math.PI * 2);
ctx.fill();
}
// Tail
ctx.beginPath();
ctx.moveTo(30, bodyYOffset + 10);
ctx.quadraticCurveTo(50, bodyYOffset, 60, bodyYOffset + 20);
ctx.lineWidth = 8;
ctx.strokeStyle = '#4CAF50';
ctx.stroke();
ctx.restore();
}
function drawObstacles() {
obstacles.forEach(obstacle => {
if (obstacle.type === 'barrier') {
// Draw barrier
ctx.fillStyle = '#795548';
ctx.fillRect(
obstacle.x - obstacle.width / 2,
obstacle.y,
obstacle.width,
obstacle.height
);
// Add some details
ctx.fillStyle = '#5D4037';
ctx.fillRect(
obstacle.x - obstacle.width / 2 + 5,
obstacle.y + 5,
obstacle.width - 10,
obstacle.height - 10
);
} else if (obstacle.type === 'gap') {
// Draw gap indicators
ctx.strokeStyle = '#FF5722';
ctx.lineWidth = 3;
ctx.setLineDash([10, 5]);
ctx.beginPath();
ctx.moveTo(obstacle.x - LANE_WIDTH / 2 + 10, obstacle.y + 20);
ctx.lineTo(obstacle.x + LANE_WIDTH / 2 - 10, obstacle.y + 20);
ctx.stroke();
ctx.setLineDash([]);
}
});
}
function drawCoins() {
coins.forEach(coin => {
ctx.save();
ctx.translate(coin.x, coin.y);
ctx.rotate(coin.rotation);
// Coin gradient
const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, coin.radius);
gradient.addColorStop(0, coin.isBig ? '#FFD700' : '#FFEB3B');
gradient.addColorStop(0.7, coin.isBig ? '#FFC107' : '#FFD600');
gradient.addColorStop(1, coin.isBig ? '#FFA000' : '#FFC107');
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(0, 0, coin.radius, 0, Math.PI * 2);
ctx.fill();
// Bitcoin symbol
ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
ctx.font = `bold ${coin.radius}px Arial`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('₿', 0, 0);
// Glow effect
if (Math.sin(Date.now() / 200) > 0.8) {
ctx.shadowColor = 'gold';
ctx.shadowBlur = 15;
ctx.beginPath();
ctx.arc(0, 0, coin.radius, 0, Math.PI * 2);
ctx.fill();
}
ctx.restore();
});
}
function drawPowerups() {
powerups.forEach(powerup => {
ctx.save();
ctx.translate(powerup.x, powerup.y);
ctx.rotate(powerup.rotation);
// Draw different powerups
switch (powerup.type) {
case 'magnet':
// Magnet powerup
ctx.fillStyle = '#2196F3';
ctx.beginPath();
ctx.arc(0, 0, powerup.radius, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = 'white';
ctx.font = 'bold 20px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('M', 0, 0);
break;
case 'multiplier':
// Multiplier powerup
ctx.fillStyle = '#FF9800';
ctx.beginPath();
ctx.arc(0, 0, powerup.radius, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = 'white';
ctx.font = 'bold 20px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('2×', 0, 0);
break;
case 'shield':
// Shield powerup
ctx.fillStyle = '#4CAF50';
ctx.beginPath();
ctx.arc(0, 0, powerup.radius, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = 'white';
ctx.font = 'bold 20px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('S', 0, 0);
break;
case 'speed':
// Speed powerup
ctx.fillStyle = '#F44336';
ctx.beginPath();
ctx.arc(0, 0, powerup.radius, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = 'white';
ctx.font = 'bold 20px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('⚡', 0, 0);
break;
}
// Glow effect
ctx.shadowColor = 'rgba(255, 255, 255, 0.7)';
ctx.shadowBlur = 10;
ctx.beginPath();
ctx.arc(0, 0, powerup.radius, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
});
}
function drawParticles() {
particles.forEach(particle => {
ctx.globalAlpha = particle.life / 60;
ctx.fillStyle = particle.color;
ctx.beginPath();
ctx.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2);
ctx.fill();
});
ctx.globalAlpha = 1;
}
function drawPowerupIndicator() {
const x = gameWidth - 50;
const y = 50;
const radius = 20;
const timeLeft = (powerupEndTime - performance.now()) / 1000;
const angle = (timeLeft / 10) * Math.PI * 2;
// Draw background circle
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
ctx.beginPath();
ctx.arc(x, y, radius + 5, 0, Math.PI * 2);
ctx.fill();
// Draw timer arc
ctx.strokeStyle = 'white';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.arc(x, y, radius + 2, -Math.PI/2, -Math.PI/2 + angle);
ctx.stroke();
// Draw powerup icon
switch (powerupActive) {
case 'magnet':
ctx.fillStyle = '#2196F3';
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = 'white';
ctx.font = 'bold 16px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('M', x, y);
break;
case 'multiplier':
ctx.fillStyle = '#FF9800';
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = 'white';
ctx.font = 'bold 16px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('2×', x, y);
break;
case 'shield':
ctx.fillStyle = '#4CAF50';
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = 'white';
ctx.font = 'bold 16px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('S', x, y);
break;
case 'speed':
ctx.fillStyle = '#F44336';
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = 'white';
ctx.font = 'bold 16px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('⚡', x, y);
break;
}
}
function drawCombo() {
const x = gameWidth / 2;
const y = 100;
const scale = 1 + combo * 0.05;
ctx.save();
ctx.translate(x, y);
ctx.scale(scale, scale);
ctx.fillStyle = 'rgba(255, 215, 0, 0.8)';
ctx.font = 'bold 24px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(`${combo}× COMBO!`, 0, 0);
ctx.restore();
}
function getLaneX(lane) {
const centerX = gameWidth / 2;
const laneOffset = (lane - 1) * LANE_WIDTH;
return centerX + laneOffset;
}
function handleKeyDown(e) {
if (!gameActive) return;
switch (e.key) {
case 'ArrowLeft':
moveLeft();
break;
case 'ArrowRight':
moveRight();
break;
case 'ArrowUp':
jump();
break;
case 'ArrowDown':
slide();
break;
case ' ':
jump();
break;
case 'Escape':
// Pause game
break;
}
}
function handleTouchStart(e) {
if (!gameActive) return;
e.preventDefault();
const touchX = e.touches[0].clientX;
const touchY = e.touches[0].clientY;
// Check if touch is in left or right half of screen
if (touchX < gameWidth / 2) {
moveLeft();
} else {
moveRight();
}
// Jump if touch is in upper half, slide if in lower half
if (touchY < gameHeight / 2) {
jump();
} else {
slide();
}
}
function handleTouchMove(e) {
e.preventDefault();
}
function moveLeft() {
if (playerState === 'running' || playerState === 'sliding') {
currentLane = Math.max(0, currentLane - 1);
}
}
function moveRight() {
if (playerState === 'running' || playerState === 'sliding') {
currentLane = Math.min(LANES - 1, currentLane + 1);
}
}
function jump() {
if (playerState === 'running') {
playerState = 'jumping';
jumpStartTime = performance.now();
}
}
function slide() {
if (playerState === 'running') {
playerState = 'sliding';
slideStartTime = performance.now();
}
}
// Start the game
window.onload = init;
</script>
</body>
</html>