<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Neon Adventure</title>
<style>
*{box-sizing:border-box}
html,body{
margin:0;
width:100%;
min-height:100%;
overflow:hidden;
font-family:"Segoe UI",Arial,sans-serif;
color:#f5f7ff;
background:
radial-gradient(circle at 15% 20%,#743cff44,transparent 42%),
radial-gradient(circle at 85% 80%,#00d4ff22,transparent 42%),
#090d1b;
}
button{
font:inherit;
}
.screen{
display:none;
width:100%;
height:100vh;
padding:20px;
}
.screen.active{
display:block;
animation:appear .3s ease;
}
@keyframes appear{
from{opacity:0;transform:translateY(12px)}
to{opacity:1;transform:none}
}
/* =========================
HAUPTMENÜ
========================= */
.menu{
width:min(1300px,100%);
height:100%;
margin:auto;
display:grid;
grid-template-columns:1.2fr 1fr;
gap:80px;
align-items:center;
padding:60px;
}
.symbol{
width:110px;
height:110px;
display:grid;
place-items:center;
margin-bottom:30px;
border:1px solid #a893ff55;
border-radius:30px;
background:#956bff18;
color:#c4b5ff;
font-size:60px;
box-shadow:0 0 60px #956bff20;
}
.subtitle{
color:#b9a5ff;
font-size:17px;
font-weight:700;
letter-spacing:4px;
text-transform:uppercase;
}
h1{
margin:12px 0 22px;
font-size:clamp(50px,7vw,105px);
line-height:.95;
letter-spacing:-4px;
}
.description{
color:#adb6d3;
font-size:25px;
line-height:1.6;
}
.menu-buttons{
display:grid;
gap:18px;
}
.menu-button{
width:100%;
min-height:95px;
display:flex;
align-items:center;
gap:20px;
padding:25px 30px;
border:1px solid #ffffff18;
border-radius:24px;
color:white;
background:#ffffff08;
cursor:pointer;
text-align:left;
transition:.2s;
}
.menu-button:hover{
transform:translateY(-4px);
}
.menu-button .icon{
width:42px;
text-align:center;
font-size:30px;
}
.menu-button .arrow{
margin-left:auto;
opacity:.5;
font-size:28px;
}
.play{
background:linear-gradient(135deg,#9061ff,#6555ef);
box-shadow:0 15px 50px #8055ff35;
}
.shop{
color:#ffe4b0;
}
.levels{
color:#aeefff;
}
.settings{
color:#d6ddf4;
}
/* =========================
ALLGEMEIN
========================= */
.container{
width:min(1300px,100%);
height:100%;
margin:auto;
}
.topbar{
height:70px;
display:flex;
align-items:center;
justify-content:space-between;
gap:15px;
}
.back{
padding:11px 18px;
border:1px solid #ffffff18;
border-radius:13px;
color:white;
background:#ffffff09;
cursor:pointer;
}
.page-title{
margin:0;
font-size:40px;
letter-spacing:-1px;
}
.stats{
display:flex;
gap:10px;
}
.stat{
padding:10px 16px;
border:1px solid #ffffff15;
border-radius:14px;
background:#ffffff08;
}
.stat strong{
color:white;
}
/* =========================
LEVELAUSWAHL
========================= */
.level-info{
margin:10px 0 20px;
padding:20px;
border:1px solid #ffffff12;
border-radius:20px;
background:#ffffff08;
color:#aeb8d4;
}
.level-grid{
display:grid;
grid-template-columns:repeat(7,1fr);
gap:12px;
max-height:calc(100vh - 170px);
overflow:auto;
padding:5px;
}
.level-button{
min-height:75px;
border:1px solid #ffffff18;
border-radius:17px;
color:white;
background:#ffffff08;
cursor:pointer;
font-weight:800;
font-size:20px;
transition:.2s;
}
.level-button:hover:not(:disabled){
transform:translateY(-3px);
border-color:#a78bff80;
}
.level-button.current{
background:linear-gradient(135deg,#9061ff,#6555ef);
}
.level-button.completed{
color:#7ee7b7;
border-color:#7ee7b740;
}
.level-button:disabled{
opacity:.25;
cursor:not-allowed;
}
/* =========================
GAME
========================= */
.game-wrap{
width:100%;
height:calc(100vh - 105px);
display:flex;
flex-direction:column;
}
.game-info{
display:grid;
grid-template-columns:repeat(5,1fr);
gap:10px;
margin-bottom:10px;
}
.game-stat{
padding:10px 15px;
border:1px solid #ffffff12;
border-radius:14px;
background:#ffffff08;
}
.game-stat small{
color:#7f8baa;
text-transform:uppercase;
font-size:11px;
letter-spacing:1px;
}
.game-stat div{
margin-top:2px;
font-size:19px;
font-weight:800;
}
.game{
position:relative;
flex:1;
min-height:350px;
overflow:hidden;
border:1px solid #8c6cff35;
border-radius:25px;
background:
radial-gradient(circle at 70% 20%,#5731a522,transparent 40%),
linear-gradient(#080c19,#0b1223);
box-shadow:0 20px 80px #00000060;
}
.game::before{
content:"";
position:absolute;
inset:0;
background-image:
linear-gradient(#ffffff05 1px,transparent 1px),
linear-gradient(90deg,#ffffff05 1px,transparent 1px);
background-size:40px 40px;
pointer-events:none;
}
/* =========================
LEVEL OBJEKTE
========================= */
.platform{
position:absolute;
height:22px;
border-radius:8px;
background:linear-gradient(135deg,#8c67ff,#5745d8);
border:1px solid #c1b5ff55;
box-shadow:0 6px 20px #6045d450;
}
.platform.ground{
background:linear-gradient(135deg,#5e4acb,#40369a);
}
.platform.moving{
background:linear-gradient(135deg,#00d4ff,#327be8);
box-shadow:0 0 20px #00d4ff45;
}
.player{
position:absolute;
z-index:20;
width:38px;
height:48px;
border-radius:12px;
display:grid;
place-items:center;
background:linear-gradient(135deg,#aa70ff,#5d50e8);
border:1px solid #d7ceff66;
box-shadow:0 0 30px #865cff80;
font-size:23px;
}
.coin{
position:absolute;
z-index:12;
width:24px;
height:24px;
display:grid;
place-items:center;
border-radius:50%;
background:#ffc44e;
color:#684100;
box-shadow:0 0 20px #ffb000aa;
font-size:12px;
}
.enemy{
position:absolute;
z-index:15;
width:38px;
height:38px;
display:grid;
place-items:center;
border-radius:11px;
background:linear-gradient(135deg,#ff5878,#b92b54);
box-shadow:0 0 20px #ff406060;
font-size:19px;
}
.spike{
position:absolute;
z-index:10;
color:#ff5578;
font-size:27px;
line-height:1;
filter:drop-shadow(0 0 8px #ff557888);
}
.checkpoint{
position:absolute;
z-index:10;
width:7px;
height:75px;
background:#00d4ff;
box-shadow:0 0 18px #00d4ff;
}
.checkpoint::after{
content:"";
position:absolute;
left:5px;
top:0;
width:45px;
height:25px;
background:#00d4ff;
clip-path:polygon(0 0,100% 25%,0 100%);
}
.goal{
position:absolute;
z-index:12;
width:45px;
height:65px;
display:grid;
place-items:center;
border:3px solid #7ff4ff;
border-radius:22px;
background:#00d4ff15;
box-shadow:0 0 35px #00d4ff90;
font-size:23px;
}
.overlay{
position:absolute;
inset:0;
z-index:100;
display:none;
place-items:center;
background:#050811d9;
backdrop-filter:blur(7px);
}
.overlay.show{
display:grid;
}
.overlay-box{
width:min(480px,90%);
padding:35px;
border:1px solid #ffffff20;
border-radius:25px;
background:#151b31;
text-align:center;
}
.overlay-box h2{
margin-top:0;
font-size:38px;
}
.overlay-box p{
color:#aeb8d4;
line-height:1.6;
}
.primary{
padding:14px 25px;
border:0;
border-radius:13px;
color:white;
background:linear-gradient(135deg,#9061ff,#6555ef);
cursor:pointer;
font-weight:800;
}
/* =========================
SHOP / SETTINGS
========================= */
.cards{
display:grid;
grid-template-columns:repeat(3,1fr);
gap:18px;
margin-top:30px;
}
.card{
padding:27px;
border:1px solid #ffffff12;
border-radius:22px;
background:#ffffff08;
}
.card-icon{
width:70px;
height:70px;
display:grid;
place-items:center;
border-radius:18px;
background:#956bff18;
font-size:35px;
}
.card h2{
margin-bottom:7px;
}
.card p{
min-height:50px;
color:#aab4d2;
line-height:1.5;
}
.price{
margin:20px 0;
color:#ffd166;
font-weight:800;
}
.buy{
width:100%;
padding:13px;
border:1px solid #a78bff40;
border-radius:12px;
color:white;
background:#9061ff18;
cursor:pointer;
}
.buy.owned{
color:#7ee7b7;
}
.settings-list{
max-width:800px;
margin-top:30px;
display:grid;
gap:15px;
}
.setting{
display:flex;
align-items:center;
justify-content:space-between;
gap:20px;
padding:22px;
border:1px solid #ffffff12;
border-radius:20px;
background:#ffffff08;
}
.setting h3{
margin:0 0 5px;
}
.setting p{
margin:0;
color:#aab4d2;
}
select{
padding:10px;
border:1px solid #ffffff20;
border-radius:10px;
color:white;
background:#11172a;
}
.reset{
padding:12px 17px;
border:1px solid #ff567540;
border-radius:12px;
color:#ff9aaa;
background:#ff567510;
cursor:pointer;
}
/* =========================
MOBILE
========================= */
.mobile-controls{
display:none;
grid-template-columns:repeat(3,60px);
justify-content:center;
gap:7px;
margin-top:8px;
}
.mobile-controls button{
height:52px;
border:1px solid #ffffff20;
border-radius:13px;
color:white;
background:#ffffff0c;
font-size:22px;
}
.empty{
visibility:hidden;
}
@media(max-width:900px){
.menu{
grid-template-columns:1fr;
text-align:center;
gap:35px;
padding:25px;
}
.symbol{
margin:0 auto 20px;
}
.description{
font-size:19px;
}
.level-grid{
grid-template-columns:repeat(5,1fr);
}
.cards{
grid-template-columns:1fr;
overflow:auto;
max-height:calc(100vh - 120px);
}
}
@media(max-width:600px){
.screen{
padding:10px;
}
.game-info{
grid-template-columns:repeat(3,1fr);
}
.game-stat:nth-child(4),
.game-stat:nth-child(5){
display:none;
}
.mobile-controls{
display:grid;
}
.level-grid{
grid-template-columns:repeat(4,1fr);
}
.page-title{
font-size:28px;
}
}
</style>
</head>
<body>
<!-- =====================================================
HAUPTMENÜ
===================================================== -->
<section id="home" class="screen active">
<div class="menu">
<div>
<div class="symbol">✦</div>
<div class="subtitle">
35 Level · Jump & Run
</div>
<h1>
Neon<br>Adventure
</h1>
<p class="description">
Springe über Plattformen,<br>
sammle Münzen und erreiche das Portal.
</p>
</div>
<div class="menu-buttons">
<button class="menu-button play" id="playBtn">
<span class="icon">▶</span>
<span>Spielen</span>
<span class="arrow">→</span>
</button>
<button class="menu-button levels" id="levelsBtn">
<span class="icon">◆</span>
<span>Level auswählen</span>
<span class="arrow">→</span>
</button>
<button class="menu-button shop" id="shopBtn">
<span class="icon">🛒</span>
<span>Shop</span>
<span class="arrow">→</span>
</button>
<button class="menu-button settings" id="settingsBtn">
<span class="icon">⚙</span>
<span>Einstellungen</span>
<span class="arrow">→</span>
</button>
</div>
</div>
</section>
<!-- =====================================================
LEVEL AUSWAHL
===================================================== -->
<section id="levels" class="screen">
<div class="container">
<div class="topbar">
<button class="back" data-home>
← Hauptmenü
</button>
<h1 class="page-title">
35 Level
</h1>
<div class="stats">
<div class="stat">
🪙 <strong id="levelCoins">0</strong>
</div>
</div>
</div>
<div class="level-info">
<b>Level 1–5:</b> Grundlagen ·
<b>6–15:</b> Präzision ·
<b>16–25:</b> Bewegliche Plattformen ·
<b>26–34:</b> Fallen & Gegner ·
<b>35:</b> Finale
</div>
<div class="level-grid" id="levelGrid"></div>
</div>
</section>
<!-- =====================================================
GAME
===================================================== -->
<section id="gameScreen" class="screen">
<div class="container">
<div class="topbar">
<button class="back" id="gameBack">
← Level
</button>
<h1 class="page-title">
Level <span id="levelNumber">1</span>
</h1>
<div></div>
</div>
<div class="game-wrap">
<div class="game-info">
<div class="game-stat">
<small>Münzen</small>
<div id="coinsUI">0</div>
</div>
<div class="game-stat">
<small>Punkte</small>
<div id="scoreUI">0</div>
</div>
<div class="game-stat">
<small>Leben</small>
<div id="livesUI">❤️❤️❤️</div>
</div>
<div class="game-stat">
<small>Checkpunkt</small>
<div id="checkpointUI">Start</div>
</div>
<div class="game-stat">
<small>Zeit</small>
<div id="timeUI">0</div>
</div>
</div>
<div class="game" id="game">
<div id="player" class="player">
🧙
</div>
<div id="goal" class="goal">
🚪
</div>
<div id="overlay" class="overlay">
<div class="overlay-box">
<h2 id="overlayTitle">
Level geschafft!
</h2>
<p id="overlayText"></p>
<button class="primary" id="overlayButton">
Weiter
</button>
</div>
</div>
</div>
<div class="mobile-controls">
<span class="empty"></span>
<button data-control="jump">
↑
</button>
<span class="empty"></span>
<button data-control="left">
←
</button>
<button data-control="down">
↓
</button>
<button data-control="right">
→
</button>
</div>
</div>
</div>
</section>
<!-- =====================================================
SHOP
===================================================== -->
<section id="shopScreen" class="screen">
<div class="container">
<div class="topbar">
<button class="back" data-home>
← Hauptmenü
</button>
<h1 class="page-title">
Shop
</h1>
<div class="stat">
🪙 <strong id="shopCoins">0</strong>
</div>
</div>
<div class="cards">
<div class="card">
<div class="card-icon">⚡</div>
<h2>Super Speed</h2>
<p>Du bewegst dich schneller.</p>
<div class="price">🪙 50</div>
<button class="buy" data-item="speed" data-price="50">
Kaufen
</button>
</div>
<div class="card">
<div class="card-icon">🛡️</div>
<h2>Schild</h2>
<p>Der erste Treffer eines Levels wird blockiert.</p>
<div class="price">🪙 75</div>
<button class="buy" data-item="shield" data-price="75">
Kaufen
</button>
</div>
<div class="card">
<div class="card-icon">💎</div>
<h2>Kristall</h2>
<p>Münzen geben doppelte Punkte.</p>
<div class="price">🪙 100</div>
<button class="buy" data-item="crystal" data-price="100">
Kaufen
</button>
</div>
</div>
</div>
</section>
<!-- =====================================================
EINSTELLUNGEN
===================================================== -->
<section id="settingsScreen" class="screen">
<div class="container">
<div class="topbar">
<button class="back" data-home>
← Hauptmenü
</button>
<h1 class="page-title">
Einstellungen
</h1>
<div></div>
</div>
<div class="settings-list">
<div class="setting">
<div>
<h3>🔊 Sound</h3>
<p>Soundeffekte aktivieren.</p>
</div>
<input type="checkbox" id="sound" checked>
</div>
<div class="setting">
<div>
<h3>🎮 Schwierigkeit</h3>
<p>Beeinflusst Gegner und Geschwindigkeit.</p>
</div>
<select id="difficulty">
<option value="easy">Einfach</option>
<option value="normal" selected>Normal</option>
<option value="hard">Schwer</option>
</select>
</div>
<div class="setting">
<div>
<h3>🗑️ Fortschritt löschen</h3>
<p>Alle Level und Münzen zurücksetzen.</p>
</div>
<button class="reset" id="reset">
Zurücksetzen
</button>
</div>
</div>
</div>
</section>
<script>
/* =====================================================
SPIELSTAND
===================================================== */
const save = {
coins:0,
unlocked:1,
completed:[],
items:{
speed:false,
shield:false,
crystal:false
}
};
/* =====================================================
35 HANDGEBAUTE LEVEL
=====================================================
Koordinaten:
x/y = Prozent im Level
Plattform:
[x,y,width]
Coin:
[x,y]
Enemy:
[x,y,left,right]
Spike:
[x,y,width]
Checkpoint:
[x,y]
Goal:
[x,y]
===================================================== */
const levels = [
/* 1 */
{
platforms:[
[0,88,100],
[25,72,18],
[50,58,18],
[73,45,18]
],
coins:[[30,66],[55,52],[78,39]],
goal:[92,28]
},
/* 2 */
{
platforms:[
[0,88,100],
[15,72,17],
[39,62,17],
[62,72,17],
[80,56,15]
],
coins:[[20,66],[44,56],[67,66],[84,50]],
goal:[92,38]
},
/* 3 */
{
platforms:[
[0,88,100],
[18,70,15],
[39,52,15],
[60,70,15],
[80,50,15]
],
coins:[[22,64],[43,46],[64,64],[84,44]],
goal:[91,33]
},
/* 4 */
{
platforms:[
[0,88,100],
[12,72,16],
[34,63,14],
[54,49,14],
[75,62,14],
[88,44,12]
],
coins:[[17,66],[39,57],[59,43],[80,56],[92,38]],
goal:[94,30]
},
/* 5 */
{
platforms:[
[0,88,100],
[10,70,14],
[29,55,14],
[48,70,14],
[67,52,14],
[85,38,13]
],
coins:[[15,64],[34,49],[53,64],[72,46],[89,32]],
goal:[94,25]
},
/* 6 */
{
platforms:[
[0,88,100],
[8,70,13],
[26,58,13],
[44,43,13],
[63,58,13],
[81,43,14]
],
coins:[[13,64],[31,52],[49,37],[68,52],[86,37]],
enemies:[
[48,65,42,62]
],
goal:[94,28]
},
/* 7 */
{
platforms:[
[0,88,100],
[12,72,14],
[32,60,13],
[50,46,13],
[68,60,13],
[86,45,12]
],
coins:[[17,66],[37,54],[55,40],[73,54],[91,39]],
enemies:[
[34,52,30,43],
[69,52,65,78]
],
goal:[94,29]
},
/* 8 */
{
platforms:[
[0,88,100],
[8,69,13],
[27,48,13],
[46,67,13],
[64,45,13],
[83,60,13]
],
coins:[[13,63],[32,42],[51,61],[69,39],[88,54]],
enemies:[
[49,61,45,57]
],
goal:[93,43]
},
/* 9 */
{
platforms:[
[0,88,100],
[14,71,12],
[31,57,12],
[48,43,12],
[65,57,12],
[82,42,12]
],
coins:[[18,65],[35,51],[52,37],[69,51],[86,36]],
enemies:[
[32,50,29,39],
[66,50,63,76]
],
goal:[93,28]
},
/* 10 */
{
platforms:[
[0,88,100],
[10,68,12],
[28,51,12],
[46,67,12],
[64,49,12],
[82,32,12]
],
coins:[[14,62],[32,45],[50,61],[68,43],[86,26]],
enemies:[
[47,60,45,57]
],
checkpoint:[50,60],
goal:[94,20]
},
/* 11 */
{
platforms:[
[0,88,100],
[8,73,11],
[25,60,11],
[42,45,11],
[59,62,11],
[76,48,11],
[88,33,10]
],
coins:[[12,67],[29,54],[46,39],[63,56],[80,42],[92,27]],
enemies:[
[43,37,40,50],
[77,40,73,84]
],
goal:[94,21]
},
/* 12 */
{
platforms:[
[0,88,100],
[15,68,12],
[33,48,12],
[51,68,12],
[69,46,12],
[87,30,11]
],
coins:[[19,62],[37,42],[55,62],[73,40],[91,24]],
enemies:[
[52,61,48,60]
],
goal:[94,18]
},
/* 13 */
{
platforms:[
[0,88,100],
[7,70,11],
[23,54,11],
[39,38,11],
[55,54,11],
[71,38,11],
[87,54,11]
],
coins:[[11,64],[27,48],[43,32],[59,48],[75,32],[91,48]],
enemies:[
[40,30,37,49],
[72,30,68,80]
],
goal:[93,30]
},
/* 14 */
{
platforms:[
[0,88,100],
[12,71,10],
[29,61,10],
[46,46,10],
[63,61,10],
[80,45,10]
],
coins:[[16,65],[33,55],[50,40],[67,55],[84,39]],
enemies:[
[30,53,27,38],
[65,53,62,73]
],
goal:[92,30]
},
/* 15 */
{
platforms:[
[0,88,100],
[8,70,10],
[24,55,10],
[40,70,10],
[56,52,10],
[72,35,10],
[88,22,10]
],
coins:[[12,64],[28,49],[44,64],[60,46],[76,29],[92,16]],
enemies:[
[57,45,54,63]
],
checkpoint:[57,45],
goal:[94,10]
},
/* 16 */
{
platforms:[
[0,88,100],
[12,70,13],
[42,56,13],
[72,43,13]
],
moving:[
[27,62,12,27,42],
[57,51,12,42,70]
],
coins:[[17,64],[47,50],[77,37]],
goal:[92,28]
},
/* 17 */
{
platforms:[
[0,88,100],
[10,70,12],
[80,42,12]
],
moving:[
[25,68,11,22,42],
[47,55,11,44,65],
[66,48,10,62,82]
],
coins:[[29,62],[51,49],[70,42],[85,36]],
goal:[92,25]
},
/* 18 */
{
platforms:[
[0,88,100],
[8,70,12],
[42,43,12],
[82,31,12]
],
moving:[
[24,62,10,20,45],
[60,50,10,42,70]
],
coins:[[27,56],[47,37],[65,44],[87,25]],
enemies:[
[44,35,41,52]
],
goal:[92,18]
},
/* 19 */
{
platforms:[
[0,88,100],
[12,69,11],
[84,36,11]
],
moving:[
[27,61,11,24,45],
[48,51,11,43,67],
[68,44,11,62,84]
],
coins:[[30,55],[51,45],[71,38],[88,30]],
enemies:[
[69,32,65,79]
],
goal:[94,20]
},
/* 20 */
{
platforms:[
[0,88,100],
[10,72,11],
[88,29,10]
],
moving:[
[25,64,10,22,45],
[44,53,10,40,62],
[63,44,10,59,82],
[79,35,9,75,89]
],
coins:[[28,58],[47,47],[66,38],[82,29],[92,21]],
checkpoint:[65,38],
goal:[94,15]
},
/* 21 */
{
platforms:[
[0,88,100],
[10,70,11],
[38,55,11],
[72,38,11]
],
moving:[
[24,62,10,20,42],
[54,47,10,48,68]
],
coins:[[27,56],[43,49],[59,41],[77,32]],
enemies:[
[40,47,38,49],
[73,30,70,84]
],
goal:[93,24]
},
/* 22 */
{
platforms:[
[0,88,100],
[8,72,10],
[31,57,10],
[57,43,10],
[84,29,10]
],
moving:[
[19,65,9,16,36],
[44,50,9,39,62],
[70,37,9,65,87]
],
coins:[[22,59],[48,44],[74,31],[88,23]],
enemies:[
[58,34,55,66]
],
goal:[94,16]
},
/* 23 */
{
platforms:[
[0,88,100],
[13,70,10],
[40,48,10],
[70,31,10]
],
moving:[
[25,61,9,20,42],
[55,42,9,50,73]
],
coins:[[28,55],[44,41],[59,35],[74,25]],
enemies:[
[71,23,68,80]
],
checkpoint:[55,35],
goal:[94,19]
},
/* 24 */
{
platforms:[
[0,88,100],
[9,70,9],
[29,52,9],
[50,69,9],
[70,47,9],
[88,30,9]
],
coins:[[13,64],[33,46],[54,63],[74,41],[92,24]],
enemies:[
[31,45,28,37],
[71,40,68,79]
],
goal:[94,17]
},
/* 25 */
{
platforms:[
[0,88,100],
[8,72,9],
[27,58,9],
[46,44,9],
[65,30,9],
[84,19,9]
],
coins:[[12,66],[31,52],[50,38],[69,24],[88,13]],
enemies:[
[47,37,44,54],
[66,23,63,74]
],
checkpoint:[66,23],
goal:[94,9]
},
/* 26 */
{
platforms:[
[0,88,100],
[12,69,11],
[33,52,11],
[55,69,11],
[76,48,11],
[89,30,10]
],
spikes:[
[21,84,8],
[43,84,8],
[66,84,8]
],
coins:[[17,63],[38,46],[60,63],[81,42],[93,24]],
goal:[94,17]
},
/* 27 */
{
platforms:[
[0,88,100],
[9,69,10],
[28,51,10],
[47,69,10],
[66,49,10],
[84,31,10]
],
spikes:[
[18,84,7],
[37,84,7],
[56,84,7],
[75,84,7]
],
coins:[[14,63],[33,45],[52,63],[71,43],[89,25]],
enemies:[
[67,41,63,75]
],
goal:[94,18]
},
/* 28 */
{
platforms:[
[0,88,100],
[14,68,9],
[33,51,9],
[52,35,9],
[71,51,9],
[89,34,9]
],
spikes:[
[23,84,8],
[43,84,8],
[63,84,8],
[82,84,6]
],
coins:[[18,62],[37,45],[56,29],[75,45],[93,28]],
enemies:[
[53,27,50,62]
],
goal:[95,20]
},
/* 29 */
{
platforms:[
[0,88,100],
[8,70,9],
[27,56,9],
[46,42,9],
[65,56,9],
[84,40,9]
],
spikes:[
[16,84,6],
[35,84,6],
[54,84,6],
[73,84,6]
],
enemies:[
[28,49,25,37],
[66,49,63,76]
],
coins:[[12,64],[31,50],[50,36],[69,50],[88,34]],
goal:[94,27]
},
/* 30 */
{
platforms:[
[0,88,100],
[12,71,9],
[31,57,9],
[50,43,9],
[69,57,9],
[88,39,9]
],
spikes:[
[21,84,7],
[40,84,7],
[59,84,7],
[78,84,7]
],
moving:[
[78,48,8,73,88]
],
enemies:[
[51,35,47,58]
],
checkpoint:[69,50],
coins:[[16,65],[35,51],[54,37],[73,51],[92,33]],
goal:[95,23]
},
/* 31 */
{
platforms:[
[0,88,100],
[8,72,8],
[24,59,8],
[40,45,8],
[56,59,8],
[72,43,8],
[88,29,8]
],
spikes:[
[16,84,6],
[32,84,6],
[48,84,6],
[64,84,6],
[80,84,6]
],
enemies:[
[41,38,37,49],
[73,35,69,80]
],
coins:[[12,66],[28,53],[44,39],[60,53],[76,37],[92,23]],
goal:[95,15]
},
/* 32 */
{
platforms:[
[0,88,100],
[10,69,8],
[29,52,8],
[48,69,8],
[67,49,8],
[86,32,8]
],
spikes:[
[19,84,6],
[38,84,6],
[57,84,6],
[76,84,6]
],
moving:[
[19,61,8,15,32],
[57,59,8,53,73]
],
enemies:[
[68,41,64,76]
],
coins:[[14,63],[33,46],[52,63],[71,43],[90,26]],
goal:[95,18]
},
/* 33 */
{
platforms:[
[0,88,100],
[9,70,8],
[26,55,8],
[43,39,8],
[60,55,8],
[77,39,8],
[91,24,7]
],
spikes:[
[17,84,6],
[34,84,6],
[51,84,6],
[68,84,6],
[85,84,5]
],
enemies:[
[44,32,41,51],
[78,32,75,85]
],
coins:[[13,64],[30,49],[47,33],[64,49],[81,33],[94,18]],
goal:[95,12]
},
/* 34 */
{
platforms:[
[0,88,100],
[10,71,8],
[28,57,8],
[46,43,8],
[64,57,8],
[82,39,8]
],
spikes:[
[18,84,6],
[36,84,6],
[54,84,6],
[72,84,6],
[90,84,5]
],
moving:[
[19,63,8,14,34],
[55,49,8,49,69],
[74,44,8,69,89]
],
enemies:[
[47,36,44,55],
[83,32,80,90]
],
checkpoint:[74,34],
coins:[[14,65],[32,51],[50,37],[68,51],[86,33]],
goal:[95,20]
},
/* 35 – FINALE */
{
platforms:[
[0,88,100],
[8,72,9],
[25,58,9],
[42,44,9],
[59,58,9],
[76,42,9],
[89,27,8]
],
spikes:[
[17,84,6],
[34,84,6],
[51,84,6],
[68,84,6],
[84,84,5]
],
moving:[
[19,65,8,14,33],
[51,50,8,47,65],
[69,46,8,66,86]
],
enemies:[
[43,36,40,52],
[77,34,73,85],
[89,19,86,94]
],
checkpoint:[59,50],
coins:[
[13,66],
[30,52],
[47,38],
[64,52],
[81,36],
[93,20]
],
goal:[95,10]
}
];
/* =====================================================
LEVEL-SICHERHEIT
Verhindert unmögliche Sprünge
===================================================== */
function makeLevelsPlayable(){
const MAX_HORIZONTAL_GAP = 15;
const MAX_VERTICAL_RISE = 16;
levels.forEach((level, levelIndex)=>{
if(!level.platforms || level.platforms.length < 2)
return;
// Plattformen nach X sortieren
level.platforms.sort((a,b)=>a[0]-b[0]);
for(let i=1;i<level.platforms.length;i++){
const previous = level.platforms[i-1];
const current = level.platforms[i];
const previousRight =
previous[0] + previous[2];
const currentLeft =
current[0];
/*
* Zu großer horizontaler Abstand
*/
const gap =
currentLeft - previousRight;
if(gap > MAX_HORIZONTAL_GAP){
current[0] =
previousRight + MAX_HORIZONTAL_GAP;
}
/*
* Zu hoher Sprung
*
* Kleinere Y-Werte = höher.
*/
const highestAllowed =
previous[1] - MAX_VERTICAL_RISE;
if(current[1] < highestAllowed){
current[1] = highestAllowed;
}
/*
* Plattform darf nicht aus dem Level ragen
*/
if(current[0] + current[2] > 96){
current[0] = 96 - current[2];
}
if(current[0] < 3){
current[0] = 3;
}
}
/*
* Ziel ebenfalls erreichbar machen
*/
if(level.goal && level.platforms.length){
const last =
level.platforms[level.platforms.length-1];
const lastRight =
last[0] + last[2];
const maxGoalX =
Math.min(
94,
lastRight + 12
);
if(level.goal[0] > maxGoalX){
level.goal[0] = maxGoalX;
}
if(
level.goal[1] <
last[1] - MAX_VERTICAL_RISE
){
level.goal[1] =
last[1] - MAX_VERTICAL_RISE;
}
}
});
}
makeLevelsPlayable();
/* =====================================================
DOM
===================================================== */
const screens={
home:document.getElementById("home"),
levels:document.getElementById("levels"),
game:document.getElementById("gameScreen"),
shop:document.getElementById("shopScreen"),
settings:document.getElementById("settingsScreen")
};
const game=document.getElementById("game");
const player=document.getElementById("player");
const goal=document.getElementById("goal");
const overlay=document.getElementById("overlay");
let currentLevel=1;
let running=false;
let raf=null;
let px=40;
let py=0;
let vx=0;
let vy=0;
let grounded=false;
let coyote=0;
let jumpBuffer=0;
let lives=3;
let score=0;
let collected=0;
let checkpointX=40;
let checkpointY=0;
let checkpointReached=false;
let keys={};
let objects={
platforms:[],
coins:[],
enemies:[],
spikes:[],
checkpoints:[],
moving:[]
};
let startTime=0;
let invincibleUntil = 0;
/* =====================================================
SCREEN
===================================================== */
function showScreen(screen){
Object.values(screens)
.forEach(x=>x.classList.remove("active"));
screen.classList.add("active");
}
/* =====================================================
BUTTONS
===================================================== */
document.getElementById("playBtn")
.addEventListener("click",()=>{
startLevel(save.unlocked);
});
document.getElementById("levelsBtn")
.addEventListener("click",()=>{
renderLevels();
showScreen(screens.levels);
});
document.getElementById("shopBtn")
.addEventListener("click",()=>{
updateShop();
showScreen(screens.shop);
});
document.getElementById("settingsBtn")
.addEventListener("click",()=>{
showScreen(screens.settings);
});
document.querySelectorAll("[data-home]")
.forEach(btn=>{
btn.addEventListener("click",()=>{
stopGame();
showScreen(screens.home);
});
});
document.getElementById("gameBack")
.addEventListener("click",()=>{
stopGame();
renderLevels();
showScreen(screens.levels);
});
/* =====================================================
LEVELAUSWAHL
===================================================== */
function renderLevels(){
const grid=document.getElementById("levelGrid");
grid.innerHTML="";
document.getElementById("levelCoins")
.textContent=save.coins;
for(let i=1;i<=35;i++){
const button=document.createElement("button");
button.className="level-button";
if(i===save.unlocked)
button.classList.add("current");
if(save.completed.includes(i))
button.classList.add("completed");
if(i>save.unlocked){
button.disabled=true;
button.textContent="🔒";
}else{
button.textContent=i;
if(save.completed.includes(i))
button.textContent="✓ "+i;
}
button.addEventListener("click",()=>{
startLevel(i);
});
grid.appendChild(button);
}
}
/* =====================================================
LEVEL START
===================================================== */
function startLevel(number){
if(number>save.unlocked)
return;
currentLevel=number;
stopGame();
showScreen(screens.game);
createLevel();
}
/* =====================================================
LEVEL ERSTELLEN
===================================================== */
function createLevel(){
clearObjects();
const data=levels[currentLevel-1];
px=40;
py=0;
vx=0;
vy=0;
grounded=false;
coyote=0;
jumpBuffer=0;
lives=3;
score=0;
collected=0;
checkpointX=40;
checkpointY=0;
checkpointReached=false;
startTime=performance.now();
document.getElementById("levelNumber")
.textContent=currentLevel;
createPlatforms(data);
createCoins(data);
createEnemies(data);
createSpikes(data);
createMovingPlatforms(data);
createCheckpoint(data);
createGoal(data);
resizePlayer();
updateUI();
running=true;
gameLoop();
}
/* =====================================================
OBJEKTE LÖSCHEN
===================================================== */
function clearObjects(){
game.querySelectorAll(
".platform,.coin,.enemy,.spike,.checkpoint"
).forEach(el=>el.remove());
objects={
platforms:[],
coins:[],
enemies:[],
spikes:[],
checkpoints:[],
moving:[]
};
}
/* =====================================================
PLATTFORMEN
===================================================== */
function createPlatforms(data){
data.platforms.forEach(p=>{
const el=document.createElement("div");
el.className="platform";
if(p[1]===88)
el.classList.add("ground");
el.style.left=p[0]+"%";
el.style.top=p[1]+"%";
el.style.width=p[2]+"%";
game.appendChild(el);
objects.platforms.push({
x:p[0],
y:p[1],
w:p[2],
h:4,
el
});
});
}
/* =====================================================
COINS
===================================================== */
function createCoins(data){
(data.coins||[]).forEach(c=>{
const el=document.createElement("div");
el.className="coin";
el.textContent="🪙";
el.style.left=c[0]+"%";
el.style.top=c[1]+"%";
game.appendChild(el);
objects.coins.push({
x:c[0],
y:c[1],
el,
collected:false
});
});
}
/* =====================================================
GEGNER
===================================================== */
function createEnemies(data){
(data.enemies||[]).forEach(e=>{
const el=document.createElement("div");
el.className="enemy";
el.textContent="👾";
el.style.left=e[0]+"%";
el.style.top=e[1]+"%";
game.appendChild(el);
objects.enemies.push({
x:e[0],
y:e[1],
min:e[2],
max:e[3],
dir:1,
speed:
save.items.speed ? .055 : .04,
el,
hit:false
});
});
}
/* =====================================================
SPIKES
===================================================== */
function createSpikes(data){
(data.spikes||[]).forEach(s=>{
const el=document.createElement("div");
el.className="spike";
el.textContent="▲";
el.style.left=s[0]+"%";
el.style.top=s[1]+"%";
el.style.width=s[2]+"%";
game.appendChild(el);
objects.spikes.push({
x:s[0],
y:s[1],
w:s[2],
el
});
});
}
/* =====================================================
BEWEGLICHE PLATTFORMEN
===================================================== */
function createMovingPlatforms(data){
(data.moving||[]).forEach(m=>{
const el=document.createElement("div");
el.className="platform moving";
el.style.left=m[0]+"%";
el.style.top=m[1]+"%";
el.style.width=m[2]+"%";
game.appendChild(el);
objects.moving.push({
x:m[0],
y:m[1],
w:m[2],
min:m[3],
max:m[4],
dir:1,
el
});
});
}
/* =====================================================
CHECKPOINT
===================================================== */
function createCheckpoint(data){
if(!data.checkpoint)
return;
const el=document.createElement("div");
el.className="checkpoint";
el.style.left=data.checkpoint[0]+"%";
el.style.top=data.checkpoint[1]+"%";
game.appendChild(el);
objects.checkpoints.push({
x:data.checkpoint[0],
y:data.checkpoint[1],
el,
reached:false
});
}
/* =====================================================
ZIEL
===================================================== */
function createGoal(data){
goal.style.left=data.goal[0]+"%";
goal.style.top=data.goal[1]+"%";
}
/* =====================================================
SPIELER GRÖSSE
===================================================== */
function resizePlayer(){
const rect=game.getBoundingClientRect();
if(!py)
py=rect.height-120;
player.style.left=px+"px";
player.style.top=py+"px";
}
/* =====================================================
INPUT
===================================================== */
document.addEventListener("keydown",e=>{
keys[e.key.toLowerCase()]=true;
if(
e.key===" " ||
e.key==="ArrowUp" ||
e.key.toLowerCase()==="w"
){
e.preventDefault();
jumpBuffer=8;
}
});
document.addEventListener("keyup",e=>{
keys[e.key.toLowerCase()]=false;
});
/* =====================================================
MOBILE INPUT
===================================================== */
document.querySelectorAll("[data-control]")
.forEach(button=>{
const control=button.dataset.control;
button.addEventListener("pointerdown",()=>{
if(control==="left")
keys["arrowleft"]=true;
if(control==="right")
keys["arrowright"]=true;
if(control==="jump")
jumpBuffer=8;
});
button.addEventListener("pointerup",()=>{
keys["arrowleft"]=false;
keys["arrowright"]=false;
});
});
/* =====================================================
GAME LOOP
===================================================== */
function gameLoop(){
if(!running)
return;
update();
raf=requestAnimationFrame(gameLoop);
}
/* =====================================================
UPDATE
===================================================== */
function update(){
const rect=game.getBoundingClientRect();
const dt=1;
/* Bewegung */
let speed=
save.items.speed ? .72 : .55;
if(
document.getElementById("difficulty").value
==="easy"
){
speed=.45;
}
if(
document.getElementById("difficulty").value
==="hard"
){
speed=.8;
}
if(
keys["arrowleft"] ||
keys["a"]
){
vx-=speed;
}
if(
keys["arrowright"] ||
keys["d"]
){
vx+=speed;
}
vx*=.84;
vx = Math.max(-8, Math.min(8, vx));
/* Coyote Time */
if(grounded)
coyote=7;
else
coyote--;
/* Sprung */
if(jumpBuffer>0)
jumpBuffer--;
if(
jumpBuffer > 0 &&
coyote > 0
){
// Konstanter, gut kontrollierbarer Sprung
vy = -13.2;
grounded = false;
coyote = 0;
jumpBuffer = 0;
sound(600);
}
/* Gravitation */
// Etwas stärkere Gravitation sorgt dafür,
// dass der Spieler schnell und kontrolliert landet.
vy += 0.52;
if(vy>12)
vy=12;
px+=vx;
py+=vy;
/* Grenzen */
px=Math.max(0,Math.min(
rect.width-38,
px
));
/* Plattformen */
grounded=false;
checkPlatforms();
checkMovingPlatforms()
/* =====================================================
PLATTFORM KOLLISION
===================================================== */
function checkPlatforms(){
const rect=game.getBoundingClientRect();
const pw=38;
const ph=48;
objects.platforms.forEach(p=>{
const x=p.x/100*rect.width;
const y=p.y/100*rect.height;
const w=p.w/100*rect.width;
if(
px+pw>x &&
px<x+w &&
py+ph>=y &&
py+ph<=y+25 &&
vy>=0
){
py=y-ph;
vy=0;
grounded=true;
}
});
}
/* =====================================================
BEWEGLICHE PLATTFORMEN
===================================================== */
function checkMovingPlatforms(){
const rect=game.getBoundingClientRect();
objects.moving.forEach(p=>{
p.x+=p.dir*.18;
if(p.x<p.min || p.x>p.max)
p.dir*=-1;
p.el.style.left=p.x+"%";
const x=p.x/100*rect.width;
const y=p.y/100*rect.height;
const w=p.w/100*rect.width;
if(
px+38>x &&
px<x+w &&
py+48>=y &&
py+48<=y+25 &&
vy>=0
){
py=y-48;
vy=0;
grounded=true;
px+=p.dir*.18/100*rect.width;
}
});
}
/* =====================================================
MÜNZEN
===================================================== */
function checkCoins(){
const rect=game.getBoundingClientRect();
objects.coins.forEach(c=>{
if(c.collected)
return;
const x=c.x/100*rect.width;
const y=c.y/100*rect.height;
if(
Math.abs(px+19-x)<32 &&
Math.abs(py+24-y)<38
){
c.collected=true;
c.el.remove();
collected++;
save.coins++;
score+=
save.items.crystal ? 20 : 10;
sound(800);
}
});
}
/* =====================================================
GEGNER
===================================================== */
function updateEnemies(){
const rect=game.getBoundingClientRect();
objects.enemies.forEach(e=>{
if(e.hit)
return;
e.x+=e.dir*e.speed;
if(e.x<=e.min || e.x>=e.max)
e.dir*=-1;
e.el.style.left=e.x+"%";
const x=e.x/100*rect.width;
const y=e.y/100*rect.height;
if(
Math.abs(px+19-x)<35 &&
Math.abs(py+24-y)<42
){
hitEnemy(e);
}
});
}
/* =====================================================
GEGNER TREFFER
===================================================== */
function hitEnemy(enemy){
if(enemy.hit)
return;
enemy.hit=true;
if(save.items.shield){
save.items.shield=false;
enemy.el.style.opacity=".25";
setTimeout(()=>{
enemy.el.remove();
},150);
sound(950);
return;
}
function loseLife(){
if(!running)
return;
// Kurze Unverwundbarkeit nach einem Treffer
if(performance.now() < invincibleUntil)
return;
invincibleUntil = performance.now() + 1200;
lives--;
sound(120);
player.style.opacity = "0.35";
setTimeout(()=>{
player.style.opacity = "1";
},1200);
if(lives <= 0){
gameOver();
return;
}
const rect = game.getBoundingClientRect();
if(checkpointReached){
px = checkpointX;
py = checkpointY;
}else{
px = 40;
py = rect.height - 130;
}
vx = 0;
vy = 0;
}
/* =====================================================
SPIKES
===================================================== */
function checkSpikes(){
const rect=game.getBoundingClientRect();
objects.spikes.forEach(s=>{
const x=s.x/100*rect.width;
const y=s.y/100*rect.height;
const w=s.w/100*rect.width;
if(
px+30>x &&
px<x+w &&
py+45>y &&
py+10<y+30
){
loseLife();
}
});
}
/* =====================================================
CHECKPOINT
===================================================== */
function checkCheckpoint(){
const rect=game.getBoundingClientRect();
objects.checkpoints.forEach(c=>{
if(c.reached)
return;
const x=c.x/100*rect.width;
const y=c.y/100*rect.height;
if(
Math.abs(px-x)<40 &&
Math.abs(py-y)<70
){
c.reached=true;
checkpointReached=true;
checkpointX=c.x/100*rect.width;
checkpointY=y-48;
c.el.style.background="#7ee7b7";
document.getElementById("checkpointUI")
.textContent="✓";
sound(900);
}
});
}
/* =====================================================
ZIEL
===================================================== */
function checkGoal(){
const rect=game.getBoundingClientRect();
const data=levels[currentLevel-1];
const x=data.goal[0]/100*rect.width;
const y=data.goal[1]/100*rect.height;
if(
Math.abs(px-x)<55 &&
Math.abs(py-y)<75
){
completeLevel();
}
}
/* =====================================================
LEBEN
===================================================== */
function loseLife(){
if(!running)
return;
lives--;
sound(120);
if(lives<=0){
gameOver();
return;
}
const rect=game.getBoundingClientRect();
if(checkpointReached){
px=checkpointX;
py=checkpointY;
}else{
px=40;
py=rect.height-130;
}
vx=0;
vy=0;
}
/* =====================================================
LEVEL GESCHAFFT
===================================================== */
function completeLevel(){
if(!running)
return;
running=false;
if(!save.completed.includes(currentLevel))
save.completed.push(currentLevel);
if(
currentLevel<35 &&
save.unlocked<=currentLevel
){
save.unlocked=currentLevel+1;
}
const bonus=20+currentLevel*2;
save.coins+=bonus;
sound(1100);
if(currentLevel===35){
showOverlay(
"🏆 DU HAST GEWONNEN!",
"Alle 35 Level wurden geschafft! Du bist der Neon-Champion.",
"Hauptmenü"
);
}else{
showOverlay(
"✨ Level geschafft!",
"Bonus: "+bonus+" Münzen · "+collected+" Münzen gesammelt.",
"Nächstes Level"
);
}
}
/* =====================================================
GAME OVER
===================================================== */
function gameOver(){
running=false;
showOverlay(
"💥 Game Over",
"Keine Leben mehr. Dein Checkpoint bleibt für diesen Versuch gespeichert.",
"Nochmal"
);
}
/* =====================================================
OVERLAY
===================================================== */
function showOverlay(title,text,button){
document.getElementById("overlayTitle")
.textContent=title;
document.getElementById("overlayText")
.textContent=text;
document.getElementById("overlayButton")
.textContent=button;
overlay.classList.add("show");
}
document.getElementById("overlayButton")
.addEventListener("click",()=>{
overlay.classList.remove("show");
if(currentLevel===35 &&
save.completed.includes(35)){
showScreen(screens.home);
return;
}
if(
save.completed.includes(currentLevel) &&
currentLevel<35
){
startLevel(currentLevel+1);
return;
}
createLevel();
});
/* =====================================================
UI
===================================================== */
function updateUI(){
document.getElementById("coinsUI")
.textContent=save.coins;
document.getElementById("scoreUI")
.textContent=score;
document.getElementById("livesUI")
.textContent="❤️".repeat(Math.max(0,lives));
document.getElementById("shopCoins")
.textContent=save.coins;
}
/* =====================================================
SHOP
===================================================== */
function updateShop(){
document.getElementById("shopCoins")
.textContent=save.coins;
document.querySelectorAll(".buy")
.forEach(button=>{
const item=button.dataset.item;
const price=Number(button.dataset.price);
if(save.items[item]){
button.textContent="✓ Aktiv";
button.classList.add("owned");
}else{
button.textContent="Kaufen · 🪙 "+price;
button.classList.remove("owned");
}
});
}
document.querySelectorAll(".buy")
.forEach(button=>{
button.addEventListener("click",()=>{
const item=button.dataset.item;
const price=Number(button.dataset.price);
if(save.items[item])
return;
if(save.coins<price){
button.textContent="Zu wenig Münzen";
setTimeout(updateShop,900);
return;
}
save.coins-=price;
save.items[item]=true;
updateShop();
updateUI();
sound(700);
});
});
/* =====================================================
RESET
===================================================== */
document.getElementById("reset")
.addEventListener("click",()=>{
if(!confirm(
"Wirklich den gesamten Fortschritt löschen?"
))
return;
save.coins=0;
save.unlocked=1;
save.completed=[];
save.items={
speed:false,
shield:false,
crystal:false
};
updateShop();
renderLevels();
});
/* =====================================================
SOUND
===================================================== */
function sound(freq){
const checkbox=document.getElementById("sound");
if(!checkbox.checked)
return;
try{
const Audio=
window.AudioContext||
window.webkitAudioContext;
const ctx=new Audio();
const osc=ctx.createOscillator();
const gain=ctx.createGain();
osc.frequency.value=freq;
gain.gain.value=.04;
osc.connect(gain);
gain.connect(ctx.destination);
osc.start();
gain.gain.exponentialRampToValueAtTime(
.001,
ctx.currentTime+.12
);
osc.stop(ctx.currentTime+.12);
}catch(e){}
}
/* =====================================================
STOP
===================================================== */
function stopGame(){
running=false;
if(raf){
cancelAnimationFrame(raf);
raf=null;
}
}
/* =====================================================
START
===================================================== */
renderLevels();
updateShop();
</script>
</body>
</html>