Neon Racer X
by BlazeKoala99985 lines30.0 KB
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Neon Racer X</title>
<style>
:root {
--cyan: #00f5ff;
--pink: #ff2bd6;
--purple: #8b5cf6;
--bg: #03040c;
}
*, *::before, *::after { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
background: radial-gradient(circle at 50% 20%, #0d122d 0%, var(--bg) 100%);
color: #fff;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
display: flex;
justify-content: center;
align-items: center;
padding: 12px;
}
button {
border: 1px solid rgba(0, 245, 255, 0.4);
background: linear-gradient(135deg, #101633 0%, #080c1f 100%);
color: #fff;
padding: 12px 18px;
border-radius: 12px;
font-weight: 600;
cursor: pointer;
box-shadow: 0 4px 15px rgba(0, 245, 255, 0.1);
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}
button:hover {
background: linear-gradient(135deg, #17234a 0%, #0d132b 100%);
border-color: var(--cyan);
box-shadow: 0 0 20px rgba(0, 245, 255, 0.35);
transform: translateY(-2px);
}
button:active { transform: translateY(1px); }
button:disabled { opacity: .4; cursor: not-allowed; box-shadow: none; transform: none; }
#app {
width: 100%;
max-width: 440px;
text-align: center;
}
/* --- FAKE LOADER SCREEN (GRÖßER GEMACHT) --- */
#loaderOverlay {
position: absolute;
inset: 0;
z-index: 20;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: #03040c;
padding: 32px;
transition: opacity 0.4s ease;
}
#loaderOverlay.fade-out {
opacity: 0;
pointer-events: none;
}
.loader-content {
width: 100%;
max-width: 380px; /* Breiter gemacht */
text-align: center;
}
.loader-title {
color: var(--cyan);
font-size: 28px; /* Größere Schrift */
font-weight: 800;
letter-spacing: 4px;
margin-bottom: 12px;
text-shadow: 0 0 20px rgba(0,245,255,0.7);
}
.loader-subtitle {
color: #94a3b8;
font-size: 15px; /* Größere Untertitel-Schrift */
margin-bottom: 30px;
letter-spacing: 2px;
}
.loader-bar-bg {
width: 100%;
height: 16px; /* Dickerer Ladebalken */
background: #0d122d;
border: 2px solid rgba(0, 245, 255, 0.4); /* Kräftigerer Rand */
border-radius: 8px;
overflow: hidden;
position: relative;
box-shadow: inset 0 0 12px rgba(0,0,0,0.8), 0 0 15px rgba(0, 245, 255, 0.2);
}
.loader-bar-fill {
width: 0%;
height: 100%;
background: linear-gradient(90deg, var(--cyan), var(--pink));
box-shadow: 0 0 20px var(--cyan);
border-radius: 6px;
transition: width 0.05s linear;
}
.loader-percentage {
margin-top: 16px;
font-size: 22px; /* Größere Prozentanzeige */
font-weight: 700;
color: #fff;
letter-spacing: 2px;
font-variant-numeric: tabular-nums;
text-shadow: 0 0 10px rgba(255,255,255,0.5);
}
/* --- HAUPTMENÜ DESIGN --- */
#mainMenu {
background: linear-gradient(180deg, rgba(16, 22, 48, 0.95) 0%, rgba(8, 12, 28, 0.98) 100%);
border: 2px solid rgba(0, 245, 255, 0.5);
box-shadow: 0 0 40px rgba(0, 245, 255, 0.2), inset 0 0 20px rgba(0, 245, 255, 0.05);
position: relative;
overflow: hidden;
}
#mainMenu::before {
content: "";
position: absolute;
top: 0; left: 0; right: 0;
height: 3px;
background: linear-gradient(90deg, transparent, var(--cyan), var(--pink), transparent);
}
.logo-container { padding: 10px 0 5px; }
.logo-badge {
display: inline-block;
padding: 4px 12px;
background: rgba(0, 245, 255, 0.1);
border: 1px solid rgba(0, 245, 255, 0.3);
border-radius: 20px;
font-size: 11px;
letter-spacing: 3px;
color: var(--cyan);
text-transform: uppercase;
margin-bottom: 8px;
box-shadow: 0 0 10px rgba(0, 245, 255, 0.15);
}
h1, .panel h2.main-title {
color: #fff;
text-shadow: 0 0 25px rgba(0, 245, 255, 0.6), 0 0 10px rgba(255, 43, 214, 0.3);
margin: 0 0 12px;
font-size: clamp(28px, 8vw, 38px);
letter-spacing: 2px;
font-weight: 800;
font-style: italic;
}
.menu-desc { color: #94a3b8; font-size: 13.5px; line-height: 1.5; margin-bottom: 15px; padding: 0 10px; }
.menu-hints {
background: rgba(3, 4, 12, 0.5);
border: 1px dashed rgba(255, 255, 255, 0.15);
border-radius: 10px;
padding: 10px 14px;
font-size: 12px;
color: #cbd5e1;
margin-bottom: 18px;
}
.btn-play {
background: linear-gradient(135deg, #00bcd4 0%, #008ba3 100%) !important;
color: #03040c !important;
font-size: 18px;
font-weight: 800;
letter-spacing: 1px;
border: 1px solid #b9faff !important;
box-shadow: 0 0 25px rgba(0, 188, 212, 0.5) !important;
transition: all 0.3s ease;
}
.btn-play:hover {
background: linear-gradient(135deg, #33d4e8 0%, #00bcd4 100%) !important;
box-shadow: 0 0 35px rgba(0, 188, 212, 0.8) !important;
transform: translateY(-2px) scale(1.02);
}
#topbar {
display: flex;
justify-content: space-between;
gap: 8px;
margin-bottom: 10px;
font-size: 13px;
}
.stat {
flex: 1;
background: rgba(17, 21, 42, 0.8);
padding: 8px 4px;
border: 1px solid rgba(40, 46, 82, 0.8);
border-radius: 12px;
backdrop-filter: blur(5px);
}
.stat strong { font-size: 15px; color: var(--cyan); }
#game {
width: 100%;
height: min(64vh, 600px);
min-height: 380px;
position: relative;
overflow: hidden;
background: #060917;
border: 2px solid var(--cyan);
border-radius: 18px;
box-shadow: 0 0 25px rgba(0, 245, 255, 0.25);
touch-action: pan-y;
}
#road {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
background: linear-gradient(90deg, #0c1020 0%, #0c1020 3%, #151c33 3%, #151c33 97%, #0c1020 97%, #0c1020 100%);
}
.lane-line {
position: absolute;
top: -100%;
height: 200%;
width: 3px;
background: repeating-linear-gradient(to bottom, #4f5d82 0 30px, transparent 30px 60px);
opacity: .5;
}
#line1 { left: 33.333%; }
#line2 { left: 66.666%; }
.car {
position: absolute;
width: 48px;
height: 76px;
border-radius: 12px 12px 10px 10px;
border: 2px solid rgba(255,255,255,0.9);
box-shadow: 0 0 15px currentColor;
transform: translateX(-50%);
z-index: 4;
}
.car::before {
content: "";
position: absolute;
width: 28px;
height: 24px;
top: 12px;
left: 50%;
transform: translateX(-50%);
background: #d4ffff;
border-radius: 6px 6px 3px 3px;
border: 1px solid #ffffff;
}
.car::after {
content: "";
position: absolute;
bottom: 8px; left: 8px; right: 8px; height: 6px;
border-radius: 3px;
background: #ff365e;
box-shadow: 0 0 10px #ff365e;
}
#player { bottom: 30px; left: 50%; background: #00bcd4; color: var(--cyan); }
.enemy { background: #b72c75; color: var(--pink); }
.enemy::before { background: #ff9ddf; }
.pickup { position: absolute; z-index: 5; transform: translate(-50%, -50%); font-size: 26px; filter: drop-shadow(0 0 10px currentColor); }
#effects { position: absolute; z-index: 7; top: 10px; left: 10px; right: 10px; display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; pointer-events: none; }
.effect { padding: 4px 10px; background: rgba(17, 24, 45, 0.85); border: 1px solid var(--cyan); border-radius: 20px; font-size: 11px; font-weight: 600; }
#shieldRing { position: absolute; width: 62px; height: 90px; border: 2px dashed var(--cyan); box-shadow: 0 0 15px var(--cyan); border-radius: 16px; bottom: 23px; left: 50%; transform: translateX(-50%); display: none; z-index: 3; pointer-events: none; }
#overlay {
position: absolute;
inset: 0;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
padding: 16px;
background: rgba(3, 5, 15, 0.85);
backdrop-filter: blur(8px);
}
.panel {
width: 100%;
max-height: 100%;
overflow-y: auto;
background: rgba(13, 18, 38, 0.95);
border: 1px solid rgba(0, 245, 255, 0.3);
border-radius: 20px;
padding: 22px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.6);
}
.panel h2 { color: var(--cyan); margin: 0 0 10px; font-size: 22px; }
.panel p { line-height: 1.5; color: #cbd5e1; font-size: 14px; }
.hidden { display: none !important; }
.menu-buttons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-top: 15px;
}
.menu-buttons button { width: 100%; }
.wide { grid-column: 1 / -1; }
#controls { display: flex; gap: 10px; margin-top: 12px; }
#controls button { flex: 1; font-size: 18px; padding: 14px; }
#message { min-height: 22px; margin: 10px 0 2px; font-size: 13px; color: #93c5fd; font-weight: 500; }
.upgrade-card { border: 1px solid rgba(52, 61, 98, 0.6); border-radius: 14px; padding: 12px; margin: 10px 0; text-align: left; background: rgba(10, 16, 34, 0.7); }
.upgrade-head { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.upgrade-card p { color: #94a3b8; font-size: 12px; margin: 6px 0 10px; }
.level-bar { display: flex; gap: 4px; margin: 8px 0; }
.level-bar span { height: 6px; flex: 1; background: #1e2642; border-radius: 4px; }
.level-bar span.on { background: var(--cyan); box-shadow: 0 0 8px var(--cyan); }
.small { font-size: 12px; color: #94a3b8; }
#footer { margin-top: 8px; color: #64748b; font-size: 11px; letter-spacing: 0.5px; }
</style>
</head>
<body>
<!-- FAKE LOADER OVERLAY -->
<div id="loaderOverlay">
<div class="loader-content">
<div class="loader-title">NEON RACER X</div>
<div class="loader-subtitle">SYSTEM LÄDT...</div>
<div class="loader-bar-bg">
<div class="loader-bar-fill" id="loaderBar"></div>
</div>
<div class="loader-percentage" id="loaderText">0%</div>
</div>
</div>
<div id="app">
<h1>NEON RACER X</h1>
<div id="topbar">
<div class="stat">🏆 Score<br><strong id="score">0</strong></div>
<div class="stat">🪙 Münzen<br><strong id="coins">0</strong></div>
<div class="stat">⚡ Bestwert<br><strong id="best">0</strong></div>
</div>
<div id="game">
<div id="road">
<div class="lane-line" id="line1"></div>
<div class="lane-line" id="line2"></div>
</div>
<div id="effects"></div>
<div id="shieldRing"></div>
<div class="car" id="player"></div>
<div id="overlay">
<!-- HAUPTMENÜ -->
<div class="panel" id="mainMenu">
<div class="logo-container">
<div class="logo-badge">Cyber Arcade Edition</div>
<h2 class="main-title">NEON RACER X</h2>
</div>
<p class="menu-desc">Weiche Hindernissen aus, sammle Münzen und rüste deine Power-ups auf, um neue Rekorde aufzustellen!</p>
<div class="menu-hints">
⌨️ <strong>Steuerung:</strong> ← → / A & D · Touch / Wischen auf Mobile
</div>
<div class="menu-buttons">
<button class="wide btn-play" id="startBtn">▶ SPIEL STARTEN</button>
<button id="upgradeBtn">⬆ Upgrades</button>
<button id="shopBtn">🎨 Skins</button>
<button class="wide" id="questBtn">📅 Tagesaufgaben</button>
</div>
</div>
<div class="panel hidden" id="upgradeMenu">
<h2>⬆ Upgrade-Shop</h2>
<p>Deine Münzen: 🪙 <strong id="upgradeCoins">0</strong></p>
<p class="small">Jede gekaufte Stufe verlängert Schild und Magnet permanent.</p>
<div id="upgradeList"></div>
<button class="wide" id="upgradeBack">↩ Zurück</button>
</div>
<div class="panel hidden" id="shopMenu">
<h2>🎨 Skin-Shop</h2>
<p>Münzen: 🪙 <strong id="shopCoins">0</strong></p>
<div id="skinList"></div>
<button class="wide" id="shopBack">↩ Zurück</button>
</div>
<div class="panel hidden" id="questMenu">
<h2>📅 Tagesaufgaben</h2>
<div id="questList"></div>
<button class="wide" id="questBack">↩ Zurück</button>
</div>
<div class="panel hidden" id="pauseMenu">
<h2>⏸ Pause</h2>
<div class="menu-buttons" style="margin-top: 15px;">
<button class="wide" id="resumeBtn">Weiter spielen</button>
<button class="wide" id="pauseHomeBtn">Hauptmenü</button>
</div>
</div>
<div class="panel hidden" id="overMenu">
<h2>💥 Game Over</h2>
<p>Dein Score: <strong id="finalScore" style="color:var(--cyan)">0</strong></p>
<p>Gesammelte Münzen: <strong id="finalCoins" style="color:var(--cyan)">0</strong></p>
<div class="menu-buttons" style="margin-top: 15px;">
<button class="wide" id="againBtn">🔄 Nochmal spielen</button>
<button class="wide" id="overHomeBtn">Hauptmenü</button>
</div>
</div>
</div>
</div>
<div id="controls">
<button id="leftBtn">◀</button>
<button id="pauseBtn">Ⅱ Pause</button>
<button id="rightBtn">▶</button>
</div>
<div id="message">Sammle Münzen für deine Upgrades!</div>
<div id="footer">Schild: 🛡️ · Magnet: 🧲 · Münze: 🪙</div>
</div>
<script>
"use strict";
const $ = id => document.getElementById(id);
const game = $("game");
const player = $("player");
const overlay = $("overlay");
/* --- FAKE LOADER LOGIK --- */
function initLoader() {
const bar = $("loaderBar");
const text = $("loaderText");
const overlayEl = $("loaderOverlay");
let progress = 0;
const startTime = performance.now();
const duration = 2000; // 2 Sekunden bis 99%
function updateLoader(now) {
const elapsed = now - startTime;
if (progress < 99) {
progress = Math.min(99, Math.floor((elapsed / duration) * 99));
bar.style.width = progress + "%";
text.textContent = progress + "%";
requestAnimationFrame(updateLoader);
} else {
// Bei 99% angekommen: 1 Sekunde warten, dann auf 100% setzen
bar.style.width = "99%";
text.textContent = "99%";
setTimeout(() => {
progress = 100;
bar.style.width = "100%";
text.textContent = "100%";
// Bei 100% angekommen: Noch 1 Sekunde stehen bleiben, dann ausblenden
setTimeout(() => {
overlayEl.classList.add("fade-out");
}, 1000);
}, 1000);
}
}
requestAnimationFrame(updateLoader);
}
// Loader direkt beim Start ausführen
window.addEventListener("DOMContentLoaded", initLoader);
const UPGRADE_PRICES = [200, 500, 1000, 1500, 1500];
const UPGRADE_NAMES = [
"Stufe 1 – Power-up Plus",
"Stufe 2 – Power-up Pro",
"Stufe 3 – Power-up Ultra",
"Stufe 4 – Power-up Elite",
"Stufe 5 – Power-up Max"
];
const SKINS = [
{name:"Neon Cyan", color:"#00bcd4", price:0},
{name:"Hot Pink", color:"#ff2bd6", price:150},
{name:"Lime Flash", color:"#7dff4f", price:300},
{name:"Gold Rush", color:"#ffbf00", price:500}
];
function loadNumber(key, fallback=0) {
try {
const n = Number(localStorage.getItem(key));
return Number.isFinite(n) && n >= 0 ? n : fallback;
} catch { return fallback; }
}
function loadJSON(key, fallback) {
try {
const value = JSON.parse(localStorage.getItem(key));
return value ?? fallback;
} catch { return fallback; }
}
function save(key, value) {
try { localStorage.setItem(key, String(value)); } catch {}
}
let coins = loadNumber("nr_coins", 0);
let best = loadNumber("nr_best", 0);
let upgradeLevel = Math.min(5, Math.floor(loadNumber("nr_upgradeLevel", 0)));
let ownedSkins = loadJSON("nr_ownedSkins", [0]);
let selectedSkin = Math.min(SKINS.length-1, loadNumber("nr_selectedSkin", 0));
if (!Array.isArray(ownedSkins)) ownedSkins = [0];
if (!ownedSkins.includes(0)) ownedSkins.push(0);
let running = false;
let paused = false;
let gameOver = false;
let score = 0;
let runCoins = 0;
let elapsed = 0;
let lastTime = 0;
let spawnTimer = 0;
let coinTimer = 0;
let powerTimer = 0;
let lane = 1;
let enemies = [];
let pickups = [];
let roadOffset = 0;
let shieldTime = 0;
let magnetTime = 0;
let lastQuestSecond = 0;
let animationId = 0;
const daily = {
date: new Date().toDateString(),
coins: 0,
score: 0,
games: 0
};
const savedDaily = loadJSON("nr_daily", null);
if (savedDaily && savedDaily.date === daily.date) {
daily.coins = savedDaily.coins || 0;
daily.score = savedDaily.score || 0;
daily.games = savedDaily.games || 0;
}
function saveDaily() { save("nr_daily", JSON.stringify(daily)); }
const quests = [
{id:"coins", name:"Sammle 30 Münzen", target:30, reward:75},
{id:"score", name:"Erreiche 1000 Punkte", target:1000, reward:100},
{id:"games", name:"Spiele 3 Runden", target:3, reward:60}
];
let claimed = loadJSON("nr_claimed", {});
if (!claimed || typeof claimed !== "object") claimed = {};
function getQuestProgress(q) {
if (q.id === "coins") return daily.coins;
if (q.id === "score") return daily.score;
return daily.games;
}
function saveAll() {
save("nr_coins", coins);
save("nr_best", best);
save("nr_upgradeLevel", upgradeLevel);
save("nr_ownedSkins", JSON.stringify(ownedSkins));
save("nr_selectedSkin", selectedSkin);
save("nr_claimed", JSON.stringify(claimed));
saveDaily();
}
function setMessage(text) { $("message").textContent = text; }
function updateHUD() {
$("score").textContent = Math.floor(score);
$("coins").textContent = coins;
$("best").textContent = best;
}
function setPlayerSkin() {
player.style.background = SKINS[selectedSkin].color;
player.style.color = SKINS[selectedSkin].color;
}
function currentShieldDuration() { return 7 + upgradeLevel * 3; }
function currentMagnetDuration() { return 8 + upgradeLevel * 3; }
function laneX(index) { return (index + 0.5) * game.clientWidth / 3; }
function setLane(index) {
lane = Math.max(0, Math.min(2, index));
player.style.left = laneX(lane) + "px";
$("shieldRing").style.left = laneX(lane) + "px";
}
function showPanel(panelId) {
overlay.classList.remove("hidden");
for (const el of overlay.querySelectorAll(".panel")) el.classList.add("hidden");
$(panelId).classList.remove("hidden");
}
function showMainMenu() {
running = false;
paused = false;
gameOver = false;
showPanel("mainMenu");
$("pauseBtn").textContent = "Ⅱ Pause";
setMessage("Sammle Münzen für deine Upgrades!");
cancelAnimationFrame(animationId);
}
function makeElement(className, text) {
const el = document.createElement("div");
el.className = className;
if (text) el.textContent = text;
game.appendChild(el);
return el;
}
function renderUpgrades() {
$("upgradeCoins").textContent = coins;
const list = $("upgradeList");
list.innerHTML = "";
for (let i = 0; i < 5; i++) {
const level = i + 1;
const bought = upgradeLevel >= level;
const next = upgradeLevel + 1 === level;
const price = UPGRADE_PRICES[i];
const card = document.createElement("div");
card.className = "upgrade-card";
const head = document.createElement("div");
head.className = "upgrade-head";
const title = document.createElement("strong");
title.textContent = UPGRADE_NAMES[i];
const status = document.createElement("span");
status.className = "small";
status.textContent = bought ? "✓ Gekauft" : (next ? price + " 🪙" : "Gesperrt");
head.append(title, status);
card.appendChild(head);
const bar = document.createElement("div");
bar.className = "level-bar";
for (let j = 0; j < 5; j++) {
const segment = document.createElement("span");
if (j < level && upgradeLevel >= j + 1) segment.classList.add("on");
bar.appendChild(segment);
}
card.appendChild(bar);
const desc = document.createElement("p");
desc.textContent = "Schild: " + (7 + level * 3) + "s · Magnet: " + (8 + level * 3) + "s";
card.appendChild(desc);
const button = document.createElement("button");
button.style.width = "100%";
if (bought) {
button.textContent = "Bereits gekauft";
button.disabled = true;
} else if (!next) {
button.textContent = "Erst Stufe " + (level - 1) + " kaufen";
button.disabled = true;
} else {
button.textContent = "⬆ Kaufen – " + price + " Münzen";
button.disabled = coins < price;
button.addEventListener("click", () => buyUpgrade(level));
}
card.appendChild(button);
list.appendChild(card);
}
}
function buyUpgrade(level) {
if (level !== upgradeLevel + 1 || level < 1 || level > 5) return;
const price = UPGRADE_PRICES[level - 1];
if (coins < price) return;
coins -= price;
upgradeLevel = level;
saveAll();
updateHUD();
renderUpgrades();
setMessage("Upgrade " + level + " gekauft!");
}
function renderSkins() {
$("shopCoins").textContent = coins;
const list = $("skinList");
list.innerHTML = "";
SKINS.forEach((skin, i) => {
const card = document.createElement("div");
card.className = "upgrade-card";
const head = document.createElement("div");
head.className = "upgrade-head";
const title = document.createElement("strong");
title.textContent = "🚗 " + skin.name;
title.style.color = skin.color;
const status = document.createElement("span");
status.className = "small";
status.textContent = ownedSkins.includes(i) ? (selectedSkin === i ? "Ausgewählt" : "Besitzt du") : skin.price + " 🪙";
head.append(title, status);
card.appendChild(head);
const button = document.createElement("button");
button.style.width = "100%";
if (ownedSkins.includes(i)) {
button.textContent = selectedSkin === i ? "Ausgewählt" : "Skin auswählen";
button.disabled = selectedSkin === i;
button.addEventListener("click", () => {
selectedSkin = i;
saveAll();
setPlayerSkin();
renderSkins();
});
} else {
button.textContent = "Kaufen – " + skin.price + " Münzen";
button.disabled = coins < skin.price;
button.addEventListener("click", () => {
if (coins < skin.price) return;
coins -= skin.price;
ownedSkins.push(i);
selectedSkin = i;
saveAll();
updateHUD();
setPlayerSkin();
renderSkins();
setMessage("Neuer Skin freigeschaltet!");
});
}
card.appendChild(button);
list.appendChild(card);
});
}
function renderQuests() {
const list = $("questList");
list.innerHTML = "";
quests.forEach(q => {
const progress = Math.min(q.target, getQuestProgress(q));
const done = progress >= q.target;
const isClaimed = !!claimed[q.id];
const card = document.createElement("div");
card.className = "upgrade-card";
const title = document.createElement("strong");
title.textContent = q.name;
card.appendChild(title);
const desc = document.createElement("p");
desc.textContent = progress + " / " + q.target + " · Belohnung: " + q.reward + " Münzen";
card.appendChild(desc);
const button = document.createElement("button");
button.style.width = "100%";
button.textContent = isClaimed ? "Belohnung abgeholt" : done ? "Belohnung abholen" : "Noch nicht geschafft";
button.disabled = !done || isClaimed;
button.addEventListener("click", () => {
if (!done || claimed[q.id]) return;
claimed[q.id] = true;
coins += q.reward;
saveAll();
updateHUD();
renderQuests();
setMessage("Aufgabe abgeschlossen! +" + q.reward + " Münzen");
});
card.appendChild(button);
list.appendChild(card);
});
}
function addCoins(amount) {
coins += amount;
runCoins += amount;
daily.coins += amount;
saveAll();
updateHUD();
}
function clearObjects() {
enemies.forEach(e => e.el.remove());
pickups.forEach(p => p.el.remove());
enemies = [];
pickups = [];
}
function resetGame() {
clearObjects();
score = 0;
runCoins = 0;
elapsed = 0;
spawnTimer = 0;
coinTimer = 0;
powerTimer = 0;
roadOffset = 0;
shieldTime = 0;
magnetTime = 0;
lastQuestSecond = 0;
lane = 1;
setLane(lane);
updateHUD();
$("effects").innerHTML = "";
$("shieldRing").style.display = "none";
setMessage("Viel Glück!");
}
function startGame() {
cancelAnimationFrame(animationId);
resetGame();
running = true;
paused = false;
gameOver = false;
overlay.classList.add("hidden");
lastTime = performance.now();
animationId = requestAnimationFrame(loop);
}
function spawnEnemy() {
const laneIndex = Math.floor(Math.random() * 3);
const el = makeElement("car enemy");
el.style.left = laneX(laneIndex) + "px";
el.style.top = "-90px";
enemies.push({el, lane:laneIndex, y:-90, passed:false});
}
function spawnPickup() {
const laneIndex = Math.floor(Math.random() * 3);
const roll = Math.random();
let type = "coin";
let symbol = "🪙";
if (roll > .91) { type = "shield"; symbol = "🛡️"; }
else if (roll > .82) { type = "magnet"; symbol = "🧲"; }
const el = makeElement("pickup", symbol);
el.style.left = laneX(laneIndex) + "px";
el.style.top = "-25px";
pickups.push({el, lane:laneIndex, y:-25, type});
}
function activatePickup(type) {
if (type === "shield") {
shieldTime = currentShieldDuration();
setMessage("🛡️ Schild aktiviert!");
} else if (type === "magnet") {
magnetTime = currentMagnetDuration();
setMessage("🧲 Magnet aktiviert!");
}
}
function updateEffects() {
const effects = $("effects");
effects.innerHTML = "";
if (shieldTime > 0) {
const el = document.createElement("span");
el.className = "effect";
el.textContent = "🛡️ Schild " + Math.ceil(shieldTime) + "s";
effects.appendChild(el);
}
if (magnetTime > 0) {
const el = document.createElement("span");
el.className = "effect";
el.textContent = "🧲 Magnet " + Math.ceil(magnetTime) + "s";
effects.appendChild(el);
}
$("shieldRing").style.display = shieldTime > 0 ? "block" : "none";
}
function endGame() {
if (gameOver) return;
gameOver = true;
running = false;
cancelAnimationFrame(animationId);
best = Math.max(best, Math.floor(score));
daily.score = Math.max(daily.score, Math.floor(score));
daily.games += 1;
saveAll();
updateHUD();
$("finalScore").textContent = Math.floor(score);
$("finalCoins").textContent = runCoins;
renderQuests();
showPanel("overMenu");
}
function moveLane(direction) {
if (!running || paused) return;
setLane(lane + direction);
}
function togglePause() {
if (!running && !paused) return;
if (paused) {
paused = false;
overlay.classList.add("hidden");
lastTime = performance.now();
animationId = requestAnimationFrame(loop);
} else {
paused = true;
showPanel("pauseMenu");
$("pauseBtn").textContent = "▶ Weiter";
cancelAnimationFrame(animationId);
}
}
function collision(a, b) {
const ar = a.getBoundingClientRect();
const br = b.getBoundingClientRect();
return ar.left < br.right - 7 && ar.right > br.left + 7 && ar.top < br.bottom - 8 && ar.bottom > br.top + 8;
}
function loop(now) {
if (!running || paused || gameOver) return;
const dt = Math.min((now - lastTime) / 1000, .05);
lastTime = now;
elapsed += dt;
const speed = Math.min(520, 230 + score * .018);
const spawnInterval = Math.max(.62, 1.05 - score * .000025);
roadOffset = (roadOffset + speed * dt) % 60;
$("line1").style.transform = "translateY(" + roadOffset + "px)";
$("line2").style.transform = "translateY(" + roadOffset + "px)";
score += dt * 10;
shieldTime = Math.max(0, shieldTime - dt);
magnetTime = Math.max(0, magnetTime - dt);
spawnTimer += dt;
coinTimer += dt;
powerTimer += dt;
if (spawnTimer >= spawnInterval) {
spawnTimer = 0;
spawnEnemy();
if (score > 1800 && Math.random() < .08) spawnEnemy();
}
if (coinTimer >= .85) { coinTimer = 0; spawnPickup(); }
if (powerTimer >= 5.5) { powerTimer = 0; if (Math.random() < .22) spawnPickup(); }
for (let i = enemies.length - 1; i >= 0; i--) {
const enemy = enemies[i];
enemy.y += speed * dt;
enemy.el.style.top = enemy.y + "px";
if (collision(player, enemy.el)) {
if (shieldTime > 0) {
shieldTime = 0;
enemy.el.remove();
enemies.splice(i, 1);
setMessage("Schild hat den Zusammenstoß abgefangen!");
continue;
}
endGame();
return;
}
if (enemy.y > game.clientHeight + 100) { enemy.el.remove(); enemies.splice(i, 1); score += 5; }
}
for (let i = pickups.length - 1; i >= 0; i--) {
const pickup = pickups[i];
pickup.y += speed * dt;
pickup.el.style.top = pickup.y + "px";
if (magnetTime > 0 && pickup.type === "coin") {
const targetX = laneX(lane);
const currentX = parseFloat(pickup.el.style.left);
const dx = targetX - currentX;
pickup.el.style.left = (currentX + dx * Math.min(1, dt * 5)) + "px";
pickup.lane = lane;
}
if (collision(player, pickup.el)) {
if (pickup.type === "coin") { addCoins(1); score += 15; }
else { activatePickup(pickup.type); }
pickup.el.remove();
pickups.splice(i, 1);
continue;
}
if (pickup.y > game.clientHeight + 40) { pickup.el.remove(); pickups.splice(i, 1); }
}
updateEffects();
updateHUD();
save("nr_best", best);
animationId = requestAnimationFrame(loop);
}
$("startBtn").addEventListener("click", startGame);
$("againBtn").addEventListener("click", startGame);
$("upgradeBtn").addEventListener("click", () => { renderUpgrades(); showPanel("upgradeMenu"); });
$("upgradeBack").addEventListener("click", () => showPanel("mainMenu"));
$("shopBtn").addEventListener("click", () => { renderSkins(); showPanel("shopMenu"); });
$("shopBack").addEventListener("click", () => showPanel("mainMenu"));
$("questBtn").addEventListener("click", () => { renderQuests(); showPanel("questMenu"); });
$("questBack").addEventListener("click", () => showPanel("mainMenu"));
$("pauseBtn").addEventListener("click", togglePause);
$("resumeBtn").addEventListener("click", togglePause);
$("pauseHomeBtn").addEventListener("click", showMainMenu);
$("overHomeBtn").addEventListener("click", showMainMenu);
$("leftBtn").addEventListener("click", () => moveLane(-1));
$("rightBtn").addEventListener("click", () => moveLane(1));
document.addEventListener("keydown", event => {
if (event.key === "ArrowLeft" || event.key.toLowerCase() === "a") moveLane(-1);
if (event.key === "ArrowRight" || event.key.toLowerCase() === "d") moveLane(1);
if (event.key === "Escape" || event.key.toLowerCase() === "p") togglePause();
if (event.key === " " && !running && !paused) startGame();
});
let touchStartX = null;
game.addEventListener("touchstart", event => {
touchStartX = event.changedTouches[0].clientX;
}, {passive:true});
game.addEventListener("touchend", event => {
if (touchStartX === null) return;
const diff = event.changedTouches[0].clientX - touchStartX;
if (Math.abs(diff) > 25) moveLane(diff > 0 ? 1 : -1);
touchStartX = null;
}, {passive:true});
window.addEventListener("resize", () => setLane(lane));
setPlayerSkin();
setLane(1);
updateHUD();
renderUpgrades();
</script>
</body>
</html>Game Source: Neon Racer X
Creator: BlazeKoala99
Libraries: none
Complexity: complex (985 lines, 30.0 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: neon-racer-x-blazekoala99" to link back to the original. Then publish at arcadelab.ai/publish.