1278 lines
46 KiB
HTML
1278 lines
46 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="da">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Carrot Hunter</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
background: linear-gradient(135deg, #4169E1 0%, #32CD32 100%);
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
min-height: 100vh;
|
|
font-family: 'Arial', sans-serif;
|
|
}
|
|
|
|
#gameContainer {
|
|
position: relative;
|
|
background: white;
|
|
border-radius: 10px;
|
|
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
|
|
overflow: hidden;
|
|
}
|
|
|
|
canvas {
|
|
display: block;
|
|
background: linear-gradient(180deg, #87CEEB 0%, #E0F8FF 100%);
|
|
}
|
|
|
|
#ui {
|
|
position: absolute;
|
|
top: 10px;
|
|
left: 10px;
|
|
right: 10px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
color: white;
|
|
font-size: 20px;
|
|
font-weight: bold;
|
|
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
|
|
z-index: 10;
|
|
}
|
|
|
|
#score, #level {
|
|
background: rgba(0,0,0,0.3);
|
|
padding: 10px 20px;
|
|
border-radius: 5px;
|
|
backdrop-filter: blur(5px);
|
|
}
|
|
|
|
#gameOver {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
background: rgba(0,0,0,0.9);
|
|
color: white;
|
|
padding: 40px;
|
|
border-radius: 10px;
|
|
text-align: center;
|
|
z-index: 100;
|
|
display: none;
|
|
min-width: 300px;
|
|
}
|
|
|
|
#gameOver h1 {
|
|
font-size: 40px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
#gameOver p {
|
|
font-size: 20px;
|
|
margin: 10px 0;
|
|
}
|
|
|
|
#gameOver button {
|
|
background: #32CD32;
|
|
color: white;
|
|
border: none;
|
|
padding: 10px 30px;
|
|
font-size: 16px;
|
|
border-radius: 5px;
|
|
cursor: pointer;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
#gameOver button:hover {
|
|
background: #228B22;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="gameContainer">
|
|
<canvas id="gameCanvas" width="1024" height="576"></canvas>
|
|
<div id="ui">
|
|
<div id="score">Gulerødder: 0/5</div>
|
|
<div id="level">Level: 1</div>
|
|
</div>
|
|
<div id="gameOver">
|
|
<h1 id="gameOverTitle">Level Færdig!</h1>
|
|
<p id="gameOverScore">Score: 0</p>
|
|
<p id="gameOverMessage"></p>
|
|
<button id="restartBtn">Næste Level</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const canvas = document.getElementById('gameCanvas');
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
// Game objects
|
|
let gameState = {
|
|
level: 1,
|
|
score: 0,
|
|
totalCarrots: 0,
|
|
carrots: [],
|
|
enemies: [],
|
|
platforms: [],
|
|
player: null,
|
|
gameOver: false
|
|
};
|
|
|
|
// Player object
|
|
class Player {
|
|
constructor(x, y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.width = 40;
|
|
this.height = 50;
|
|
this.velocityX = 0;
|
|
this.velocityY = 0;
|
|
this.isJumping = false;
|
|
this.jumpPower = 12;
|
|
this.gravity = 0.5;
|
|
this.speed = 5;
|
|
this.direction = 1; // 1 for right, -1 for left
|
|
}
|
|
|
|
draw() {
|
|
// Draw bunny body
|
|
ctx.fillStyle = '#FFFFFF';
|
|
ctx.fillRect(this.x, this.y + 15, this.width, 30);
|
|
|
|
// Draw head
|
|
ctx.fillStyle = '#FFFFFF';
|
|
ctx.beginPath();
|
|
ctx.arc(this.x + this.width/2, this.y + 10, 12, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
|
|
// Draw ears
|
|
ctx.fillStyle = '#FFB6D9';
|
|
ctx.fillRect(this.x + 12, this.y - 10, 6, 15);
|
|
ctx.fillRect(this.x + 22, this.y - 10, 6, 15);
|
|
|
|
// Ear fill (white)
|
|
ctx.fillStyle = '#FFFFFF';
|
|
ctx.fillRect(this.x + 13, this.y - 8, 4, 11);
|
|
ctx.fillRect(this.x + 23, this.y - 8, 4, 11);
|
|
|
|
// Draw eyes
|
|
ctx.fillStyle = '#000000';
|
|
ctx.beginPath();
|
|
ctx.arc(this.x + 14, this.y + 8, 2, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
ctx.beginPath();
|
|
ctx.arc(this.x + 26, this.y + 8, 2, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
|
|
// Draw nose
|
|
ctx.fillStyle = '#FF1493';
|
|
ctx.beginPath();
|
|
ctx.arc(this.x + 20, this.y + 12, 2, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
|
|
// Draw mouth
|
|
ctx.strokeStyle = '#000000';
|
|
ctx.lineWidth = 1;
|
|
ctx.beginPath();
|
|
ctx.arc(this.x + 20, this.y + 14, 1.5, 0, Math.PI);
|
|
ctx.stroke();
|
|
}
|
|
|
|
update(keys) {
|
|
// Horizontal movement
|
|
this.velocityX = 0;
|
|
if (keys['ArrowLeft'] || keys['a']) {
|
|
this.velocityX = -this.speed;
|
|
this.direction = -1;
|
|
}
|
|
if (keys['ArrowRight'] || keys['d']) {
|
|
this.velocityX = this.speed;
|
|
this.direction = 1;
|
|
}
|
|
|
|
this.x += this.velocityX;
|
|
|
|
// Boundary check
|
|
if (this.x < 0) this.x = 0;
|
|
if (this.x + this.width > canvas.width) this.x = canvas.width - this.width;
|
|
|
|
// Apply gravity
|
|
this.velocityY += this.gravity;
|
|
this.y += this.velocityY;
|
|
|
|
// Check platform collisions
|
|
this.isJumping = true;
|
|
for (let platform of gameState.platforms) {
|
|
if (this.velocityY >= 0 &&
|
|
this.y + this.height >= platform.y &&
|
|
this.y + this.height <= platform.y + platform.height + 10 &&
|
|
this.x + this.width > platform.x &&
|
|
this.x < platform.x + platform.width) {
|
|
this.velocityY = 0;
|
|
this.y = platform.y - this.height;
|
|
this.isJumping = false;
|
|
}
|
|
}
|
|
|
|
// Jump
|
|
if ((keys['ArrowUp'] || keys['w'] || keys[' ']) && !this.isJumping) {
|
|
this.velocityY = -this.jumpPower;
|
|
this.isJumping = true;
|
|
}
|
|
|
|
// Check carrot collisions
|
|
for (let i = gameState.carrots.length - 1; i >= 0; i--) {
|
|
let carrot = gameState.carrots[i];
|
|
if (this.checkCollision(carrot)) {
|
|
gameState.carrots.splice(i, 1);
|
|
gameState.score++;
|
|
updateUI();
|
|
}
|
|
}
|
|
|
|
// Check enemy collisions
|
|
for (let enemy of gameState.enemies) {
|
|
if (this.checkCollision(enemy)) {
|
|
gameState.gameOver = true;
|
|
showGameOver('Du blev taget af en fjende!', 'Prøv igen');
|
|
}
|
|
}
|
|
|
|
// Fall off map
|
|
if (this.y > canvas.height) {
|
|
gameState.gameOver = true;
|
|
showGameOver('Du faldt ned!', 'Prøv igen');
|
|
}
|
|
|
|
// Level complete
|
|
if (gameState.carrots.length === 0 && gameState.totalCarrots > 0) {
|
|
gameState.gameOver = true;
|
|
showGameOver('Level Færdig!', 'Næste Level', true);
|
|
}
|
|
}
|
|
|
|
checkCollision(obj) {
|
|
return this.x < obj.x + obj.width &&
|
|
this.x + this.width > obj.x &&
|
|
this.y < obj.y + obj.height &&
|
|
this.y + this.height > obj.y;
|
|
}
|
|
}
|
|
|
|
// Carrot class
|
|
class Carrot {
|
|
constructor(x, y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.width = 25;
|
|
this.height = 25;
|
|
this.rotation = 0;
|
|
}
|
|
|
|
draw() {
|
|
ctx.save();
|
|
ctx.translate(this.x + this.width/2, this.y + this.height/2);
|
|
ctx.rotate(this.rotation);
|
|
|
|
// Carrot body (bright orange)
|
|
ctx.fillStyle = '#FF8C00';
|
|
ctx.beginPath();
|
|
ctx.moveTo(0, -12);
|
|
ctx.lineTo(8, -4);
|
|
ctx.lineTo(8, 4);
|
|
ctx.lineTo(0, 12);
|
|
ctx.lineTo(-8, 4);
|
|
ctx.lineTo(-8, -4);
|
|
ctx.fill();
|
|
|
|
// Carrot outline
|
|
ctx.strokeStyle = '#FF6400';
|
|
ctx.lineWidth = 1;
|
|
ctx.stroke();
|
|
|
|
// Carrot top (bright green)
|
|
ctx.fillStyle = '#32CD32';
|
|
ctx.beginPath();
|
|
ctx.moveTo(0, -12);
|
|
ctx.lineTo(-4, -18);
|
|
ctx.lineTo(0, -20);
|
|
ctx.lineTo(4, -18);
|
|
ctx.fill();
|
|
|
|
ctx.strokeStyle = '#228B22';
|
|
ctx.lineWidth = 1;
|
|
ctx.stroke();
|
|
|
|
ctx.restore();
|
|
this.rotation += 0.05;
|
|
}
|
|
|
|
update() {
|
|
this.draw();
|
|
}
|
|
}
|
|
|
|
// Enemy class
|
|
class Enemy {
|
|
constructor(x, y, width, speed) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.width = width;
|
|
this.height = 30;
|
|
this.speed = speed;
|
|
this.direction = 1;
|
|
this.minX = x - 100;
|
|
this.maxX = x + 100;
|
|
}
|
|
|
|
draw() {
|
|
// Enemy body (bright red)
|
|
ctx.fillStyle = '#FF4500';
|
|
ctx.fillRect(this.x, this.y, this.width, this.height);
|
|
|
|
// Enemy outline
|
|
ctx.strokeStyle = '#CC3300';
|
|
ctx.lineWidth = 2;
|
|
ctx.strokeRect(this.x, this.y, this.width, this.height);
|
|
|
|
// Eyes (yellow)
|
|
ctx.fillStyle = '#FFD700';
|
|
ctx.fillRect(this.x + 5, this.y + 5, 8, 8);
|
|
ctx.fillRect(this.x + this.width - 13, this.y + 5, 8, 8);
|
|
|
|
// Pupils (black)
|
|
ctx.fillStyle = '#000000';
|
|
ctx.fillRect(this.x + 6, this.y + 6, 6, 6);
|
|
ctx.fillRect(this.x + this.width - 12, this.y + 6, 6, 6);
|
|
|
|
// Eyebrows (angry look)
|
|
ctx.strokeStyle = '#000000';
|
|
ctx.lineWidth = 1;
|
|
ctx.beginPath();
|
|
ctx.moveTo(this.x + 5, this.y + 4);
|
|
ctx.lineTo(this.x + 12, this.y + 3);
|
|
ctx.stroke();
|
|
ctx.beginPath();
|
|
ctx.moveTo(this.x + this.width - 13, this.y + 4);
|
|
ctx.lineTo(this.x + this.width - 6, this.y + 3);
|
|
ctx.stroke();
|
|
}
|
|
|
|
update() {
|
|
this.x += this.speed * this.direction;
|
|
|
|
if (this.x <= this.minX || this.x >= this.maxX) {
|
|
this.direction *= -1;
|
|
}
|
|
|
|
this.draw();
|
|
}
|
|
}
|
|
|
|
// Platform class
|
|
class Platform {
|
|
constructor(x, y, width, height = 20) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.width = width;
|
|
this.height = height;
|
|
}
|
|
|
|
draw() {
|
|
ctx.fillStyle = '#8B7355';
|
|
ctx.fillRect(this.x, this.y, this.width, this.height);
|
|
|
|
// Darker border for depth
|
|
ctx.strokeStyle = '#654321';
|
|
ctx.lineWidth = 2;
|
|
ctx.strokeRect(this.x, this.y, this.width, this.height);
|
|
|
|
// Wood grain effect
|
|
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
|
|
for (let i = 0; i < this.width; i += 20) {
|
|
ctx.fillRect(this.x + i, this.y, 2, this.height);
|
|
}
|
|
}
|
|
|
|
update() {
|
|
this.draw();
|
|
}
|
|
}
|
|
|
|
// Level designs
|
|
const levels = [
|
|
{
|
|
name: 'Level 1: Begyndelse',
|
|
platforms: [
|
|
new Platform(0, 500, 1024, 76), // Bottom
|
|
new Platform(150, 400, 150),
|
|
new Platform(400, 350, 150),
|
|
new Platform(650, 400, 150),
|
|
new Platform(200, 250, 200),
|
|
new Platform(600, 250, 200)
|
|
],
|
|
carrots: [
|
|
{x: 170, y: 350},
|
|
{x: 420, y: 300},
|
|
{x: 670, y: 350},
|
|
{x: 300, y: 180},
|
|
{x: 700, y: 180}
|
|
],
|
|
enemies: []
|
|
},
|
|
{
|
|
name: 'Level 2: Fjender',
|
|
platforms: [
|
|
new Platform(0, 500, 1024, 76),
|
|
new Platform(100, 400, 150),
|
|
new Platform(350, 350, 150),
|
|
new Platform(600, 400, 150),
|
|
new Platform(250, 250, 200),
|
|
new Platform(550, 250, 200)
|
|
],
|
|
carrots: [
|
|
{x: 120, y: 350},
|
|
{x: 370, y: 300},
|
|
{x: 620, y: 350},
|
|
{x: 350, y: 180},
|
|
{x: 650, y: 180}
|
|
],
|
|
enemies: [
|
|
new Enemy(300, 320, 40, 2),
|
|
new Enemy(700, 320, 40, 1.5)
|
|
]
|
|
},
|
|
{
|
|
name: 'Level 3: Udfordring',
|
|
platforms: [
|
|
new Platform(0, 500, 1024, 76),
|
|
new Platform(50, 420, 80),
|
|
new Platform(200, 380, 80),
|
|
new Platform(350, 420, 80),
|
|
new Platform(500, 380, 80),
|
|
new Platform(650, 420, 80),
|
|
new Platform(800, 380, 80),
|
|
new Platform(150, 280, 200),
|
|
new Platform(650, 280, 200),
|
|
new Platform(400, 180, 200)
|
|
],
|
|
carrots: [
|
|
{x: 60, y: 370},
|
|
{x: 210, y: 330},
|
|
{x: 360, y: 370},
|
|
{x: 510, y: 330},
|
|
{x: 660, y: 370}
|
|
],
|
|
enemies: [
|
|
new Enemy(250, 250, 40, 2),
|
|
new Enemy(650, 250, 40, 2),
|
|
new Enemy(450, 150, 40, 1.5)
|
|
]
|
|
},
|
|
{
|
|
name: 'Level 4: Boss Landet',
|
|
platforms: [
|
|
new Platform(0, 500, 1024, 76),
|
|
new Platform(80, 420, 100),
|
|
new Platform(280, 400, 100),
|
|
new Platform(480, 420, 100),
|
|
new Platform(680, 400, 100),
|
|
new Platform(150, 300, 150),
|
|
new Platform(700, 300, 150),
|
|
new Platform(400, 200, 200)
|
|
],
|
|
carrots: [
|
|
{x: 100, y: 360},
|
|
{x: 300, y: 340},
|
|
{x: 500, y: 360},
|
|
{x: 700, y: 340},
|
|
{x: 200, y: 240},
|
|
{x: 650, y: 240},
|
|
{x: 430, y: 120},
|
|
{x: 370, y: 120},
|
|
{x: 500, y: 120},
|
|
{x: 300, y: 120}
|
|
],
|
|
enemies: [
|
|
new Enemy(200, 250, 40, 2.5),
|
|
new Enemy(600, 250, 40, 2.5),
|
|
new Enemy(400, 350, 40, 2)
|
|
]
|
|
},
|
|
{
|
|
name: 'Level 5: Fjendekval',
|
|
platforms: [
|
|
new Platform(0, 500, 1024, 76),
|
|
new Platform(100, 420, 80),
|
|
new Platform(300, 380, 80),
|
|
new Platform(500, 420, 80),
|
|
new Platform(700, 380, 80),
|
|
new Platform(150, 280, 180),
|
|
new Platform(680, 280, 180),
|
|
new Platform(400, 180, 180)
|
|
],
|
|
carrots: [
|
|
{x: 120, y: 360},
|
|
{x: 320, y: 320},
|
|
{x: 520, y: 360},
|
|
{x: 720, y: 320},
|
|
{x: 220, y: 220},
|
|
{x: 750, y: 220},
|
|
{x: 350, y: 120},
|
|
{x: 450, y: 120}
|
|
],
|
|
enemies: [
|
|
new Enemy(250, 250, 40, 3),
|
|
new Enemy(650, 250, 40, 3),
|
|
new Enemy(400, 350, 40, 2),
|
|
new Enemy(500, 150, 40, 2.5)
|
|
]
|
|
},
|
|
{
|
|
name: 'Level 6: Springning',
|
|
platforms: [
|
|
new Platform(0, 500, 1024, 76),
|
|
new Platform(50, 450, 70),
|
|
new Platform(150, 420, 70),
|
|
new Platform(250, 450, 70),
|
|
new Platform(350, 420, 70),
|
|
new Platform(450, 450, 70),
|
|
new Platform(550, 420, 70),
|
|
new Platform(650, 450, 70),
|
|
new Platform(750, 420, 70),
|
|
new Platform(850, 450, 70),
|
|
new Platform(200, 300, 150),
|
|
new Platform(650, 300, 150),
|
|
new Platform(400, 150, 200)
|
|
],
|
|
carrots: [
|
|
{x: 60, y: 400},
|
|
{x: 160, y: 370},
|
|
{x: 260, y: 400},
|
|
{x: 360, y: 370},
|
|
{x: 460, y: 400},
|
|
{x: 560, y: 370},
|
|
{x: 660, y: 400},
|
|
{x: 760, y: 370},
|
|
{x: 860, y: 400},
|
|
{x: 250, y: 240},
|
|
{x: 700, y: 240},
|
|
{x: 350, y: 80},
|
|
{x: 450, y: 80}
|
|
],
|
|
enemies: [
|
|
new Enemy(300, 270, 40, 2.5),
|
|
new Enemy(700, 270, 40, 2.5),
|
|
new Enemy(400, 100, 40, 2)
|
|
]
|
|
},
|
|
{
|
|
name: 'Level 7: Kombineret',
|
|
platforms: [
|
|
new Platform(0, 500, 1024, 76),
|
|
new Platform(80, 420, 120),
|
|
new Platform(350, 400, 100),
|
|
new Platform(600, 420, 120),
|
|
new Platform(100, 300, 200),
|
|
new Platform(750, 300, 200),
|
|
new Platform(300, 220, 150),
|
|
new Platform(550, 220, 150),
|
|
new Platform(400, 100, 200)
|
|
],
|
|
carrots: [
|
|
{x: 120, y: 350},
|
|
{x: 370, y: 330},
|
|
{x: 640, y: 350},
|
|
{x: 180, y: 240},
|
|
{x: 800, y: 240},
|
|
{x: 350, y: 160},
|
|
{x: 600, y: 160},
|
|
{x: 300, y: 40},
|
|
{x: 450, y: 40},
|
|
{x: 550, y: 40},
|
|
{x: 650, y: 40},
|
|
{x: 380, y: 270},
|
|
{x: 620, y: 270}
|
|
],
|
|
enemies: [
|
|
new Enemy(200, 250, 40, 2),
|
|
new Enemy(700, 250, 40, 2),
|
|
new Enemy(350, 180, 40, 2.5),
|
|
new Enemy(550, 180, 40, 2.5),
|
|
new Enemy(450, 50, 40, 3)
|
|
]
|
|
},
|
|
{
|
|
name: 'Level 8: Mekanisme Intro',
|
|
platforms: [
|
|
new Platform(0, 500, 1024, 76),
|
|
new Platform(50, 420, 100),
|
|
new Platform(200, 390, 100),
|
|
new Platform(350, 420, 100),
|
|
new Platform(500, 390, 100),
|
|
new Platform(650, 420, 100),
|
|
new Platform(800, 390, 100),
|
|
new Platform(100, 300, 180),
|
|
new Platform(750, 300, 180),
|
|
new Platform(250, 220, 150),
|
|
new Platform(600, 220, 150),
|
|
new Platform(400, 100, 200)
|
|
],
|
|
carrots: [
|
|
{x: 70, y: 360},
|
|
{x: 220, y: 330},
|
|
{x: 370, y: 360},
|
|
{x: 520, y: 330},
|
|
{x: 670, y: 360},
|
|
{x: 820, y: 330},
|
|
{x: 180, y: 240},
|
|
{x: 800, y: 240},
|
|
{x: 300, y: 160},
|
|
{x: 650, y: 160},
|
|
{x: 300, y: 40},
|
|
{x: 400, y: 40},
|
|
{x: 500, y: 40},
|
|
{x: 600, y: 40},
|
|
{x: 450, y: 200}
|
|
],
|
|
enemies: [
|
|
new Enemy(250, 250, 40, 2),
|
|
new Enemy(650, 250, 40, 2),
|
|
new Enemy(300, 170, 40, 2.5),
|
|
new Enemy(600, 170, 40, 2.5)
|
|
]
|
|
},
|
|
{
|
|
name: 'Level 9: Højdeskræk',
|
|
platforms: [
|
|
new Platform(0, 500, 1024, 76),
|
|
new Platform(450, 470, 100),
|
|
new Platform(200, 420, 100),
|
|
new Platform(700, 420, 100),
|
|
new Platform(450, 370, 100),
|
|
new Platform(100, 320, 100),
|
|
new Platform(800, 320, 100),
|
|
new Platform(450, 270, 100),
|
|
new Platform(300, 220, 100),
|
|
new Platform(600, 220, 100),
|
|
new Platform(450, 170, 100),
|
|
new Platform(200, 120, 150),
|
|
new Platform(650, 120, 150)
|
|
],
|
|
carrots: [
|
|
{x: 470, y: 410},
|
|
{x: 220, y: 360},
|
|
{x: 720, y: 360},
|
|
{x: 470, y: 310},
|
|
{x: 120, y: 260},
|
|
{x: 820, y: 260},
|
|
{x: 470, y: 210},
|
|
{x: 320, y: 160},
|
|
{x: 620, y: 160},
|
|
{x: 470, y: 110}
|
|
],
|
|
enemies: [
|
|
new Enemy(450, 330, 40, 2),
|
|
new Enemy(450, 230, 40, 2)
|
|
]
|
|
},
|
|
{
|
|
name: 'Level 10: Faldegruber',
|
|
platforms: [
|
|
new Platform(0, 500, 1024, 76),
|
|
new Platform(50, 420, 150),
|
|
new Platform(400, 420, 200),
|
|
new Platform(800, 420, 50),
|
|
new Platform(150, 340, 80),
|
|
new Platform(500, 340, 80),
|
|
new Platform(750, 340, 80),
|
|
new Platform(100, 280, 150),
|
|
new Platform(600, 280, 150),
|
|
new Platform(350, 220, 80),
|
|
new Platform(700, 220, 80),
|
|
new Platform(300, 140, 200)
|
|
],
|
|
carrots: [
|
|
{x: 100, y: 360},
|
|
{x: 480, y: 360},
|
|
{x: 820, y: 360},
|
|
{x: 170, y: 280},
|
|
{x: 520, y: 280},
|
|
{x: 770, y: 280},
|
|
{x: 180, y: 220},
|
|
{x: 650, y: 220},
|
|
{x: 380, y: 160},
|
|
{x: 750, y: 160},
|
|
{x: 250, y: 80},
|
|
{x: 450, y: 80}
|
|
],
|
|
enemies: [
|
|
new Enemy(300, 290, 40, 2.5),
|
|
new Enemy(650, 290, 40, 2.5),
|
|
new Enemy(380, 180, 40, 2)
|
|
]
|
|
},
|
|
{
|
|
name: 'Level 11: Spring Fest',
|
|
platforms: [
|
|
new Platform(0, 500, 1024, 76),
|
|
new Platform(30, 450, 60),
|
|
new Platform(120, 420, 60),
|
|
new Platform(210, 450, 60),
|
|
new Platform(300, 420, 60),
|
|
new Platform(390, 450, 60),
|
|
new Platform(480, 420, 60),
|
|
new Platform(570, 450, 60),
|
|
new Platform(660, 420, 60),
|
|
new Platform(750, 450, 60),
|
|
new Platform(840, 420, 60),
|
|
new Platform(150, 350, 100),
|
|
new Platform(450, 350, 100),
|
|
new Platform(750, 350, 100),
|
|
new Platform(100, 280, 80),
|
|
new Platform(400, 280, 80),
|
|
new Platform(700, 280, 80),
|
|
new Platform(300, 220, 120),
|
|
new Platform(600, 220, 120),
|
|
new Platform(450, 120, 150)
|
|
],
|
|
carrots: [
|
|
{x: 45, y: 390},
|
|
{x: 135, y: 360},
|
|
{x: 225, y: 390},
|
|
{x: 315, y: 360},
|
|
{x: 405, y: 390},
|
|
{x: 495, y: 360},
|
|
{x: 585, y: 390},
|
|
{x: 675, y: 360},
|
|
{x: 765, y: 390},
|
|
{x: 855, y: 360},
|
|
{x: 200, y: 290},
|
|
{x: 500, y: 290},
|
|
{x: 800, y: 290},
|
|
{x: 140, y: 220},
|
|
{x: 440, y: 220},
|
|
{x: 740, y: 220},
|
|
{x: 360, y: 150},
|
|
{x: 540, y: 150}
|
|
],
|
|
enemies: [
|
|
new Enemy(250, 310, 40, 2),
|
|
new Enemy(550, 310, 40, 2),
|
|
new Enemy(800, 310, 40, 2),
|
|
new Enemy(400, 180, 40, 2.5)
|
|
]
|
|
},
|
|
{
|
|
name: 'Level 12: Tower Climax',
|
|
platforms: [
|
|
new Platform(0, 500, 1024, 76),
|
|
new Platform(100, 450, 100),
|
|
new Platform(700, 450, 100),
|
|
new Platform(400, 420, 120),
|
|
new Platform(150, 380, 80),
|
|
new Platform(800, 380, 80),
|
|
new Platform(450, 350, 100),
|
|
new Platform(200, 310, 100),
|
|
new Platform(750, 310, 100),
|
|
new Platform(400, 270, 100),
|
|
new Platform(100, 240, 120),
|
|
new Platform(700, 240, 120),
|
|
new Platform(450, 200, 80),
|
|
new Platform(250, 160, 100),
|
|
new Platform(650, 160, 100),
|
|
new Platform(400, 120, 150),
|
|
new Platform(200, 80, 80),
|
|
new Platform(700, 80, 80),
|
|
new Platform(450, 40, 100)
|
|
],
|
|
carrots: [
|
|
{x: 130, y: 390},
|
|
{x: 730, y: 390},
|
|
{x: 460, y: 360},
|
|
{x: 180, y: 320},
|
|
{x: 830, y: 320},
|
|
{x: 480, y: 290},
|
|
{x: 230, y: 250},
|
|
{x: 780, y: 250},
|
|
{x: 470, y: 210},
|
|
{x: 280, y: 100},
|
|
{x: 680, y: 100},
|
|
{x: 380, y: 50},
|
|
{x: 520, y: 50},
|
|
{x: 300, y: 30},
|
|
{x: 600, y: 30},
|
|
{x: 150, y: 200},
|
|
{x: 800, y: 200},
|
|
{x: 450, y: 140},
|
|
{x: 400, y: 0},
|
|
{x: 500, y: 0}
|
|
],
|
|
enemies: [
|
|
new Enemy(300, 280, 40, 2.5),
|
|
new Enemy(650, 280, 40, 2.5),
|
|
new Enemy(450, 180, 40, 2),
|
|
new Enemy(250, 130, 40, 2),
|
|
new Enemy(700, 130, 40, 2),
|
|
new Enemy(450, 70, 40, 3)
|
|
]
|
|
},
|
|
{
|
|
name: 'Level 13: Bevægelse Start',
|
|
platforms: [
|
|
new Platform(0, 500, 1024, 76),
|
|
new Platform(150, 420, 150),
|
|
new Platform(650, 420, 150),
|
|
new Platform(400, 350, 200),
|
|
new Platform(100, 280, 180),
|
|
new Platform(800, 280, 180),
|
|
new Platform(450, 200, 180)
|
|
],
|
|
carrots: [
|
|
{x: 210, y: 360},
|
|
{x: 710, y: 360},
|
|
{x: 480, y: 290},
|
|
{x: 180, y: 220},
|
|
{x: 830, y: 220},
|
|
{x: 350, y: 140},
|
|
{x: 550, y: 140},
|
|
{x: 400, y: 120},
|
|
{x: 500, y: 120},
|
|
{x: 300, y: 100},
|
|
{x: 600, y: 100},
|
|
{x: 450, y: 80}
|
|
],
|
|
enemies: [
|
|
new Enemy(250, 350, 40, 2),
|
|
new Enemy(650, 350, 40, 2),
|
|
new Enemy(400, 250, 40, 2.5),
|
|
new Enemy(450, 150, 40, 2.5)
|
|
]
|
|
},
|
|
{
|
|
name: 'Level 14: Timing Master',
|
|
platforms: [
|
|
new Platform(0, 500, 1024, 76),
|
|
new Platform(80, 440, 100),
|
|
new Platform(280, 400, 100),
|
|
new Platform(480, 440, 100),
|
|
new Platform(680, 400, 100),
|
|
new Platform(840, 440, 100),
|
|
new Platform(150, 320, 180),
|
|
new Platform(700, 320, 180),
|
|
new Platform(300, 240, 150),
|
|
new Platform(550, 240, 150),
|
|
new Platform(400, 140, 200)
|
|
],
|
|
carrots: [
|
|
{x: 100, y: 380},
|
|
{x: 300, y: 340},
|
|
{x: 500, y: 380},
|
|
{x: 700, y: 340},
|
|
{x: 860, y: 380},
|
|
{x: 220, y: 260},
|
|
{x: 750, y: 260},
|
|
{x: 350, y: 180},
|
|
{x: 600, y: 180},
|
|
{x: 300, y: 80},
|
|
{x: 400, y: 80},
|
|
{x: 500, y: 80},
|
|
{x: 600, y: 80},
|
|
{x: 350, y: 200},
|
|
{x: 550, y: 200}
|
|
],
|
|
enemies: [
|
|
new Enemy(200, 280, 40, 2.5),
|
|
new Enemy(700, 280, 40, 2.5),
|
|
new Enemy(300, 200, 40, 3),
|
|
new Enemy(600, 200, 40, 3),
|
|
new Enemy(450, 100, 40, 2)
|
|
]
|
|
},
|
|
{
|
|
name: 'Level 15: Vulkan Hede',
|
|
platforms: [
|
|
new Platform(0, 500, 1024, 76),
|
|
new Platform(50, 430, 120),
|
|
new Platform(250, 400, 100),
|
|
new Platform(500, 430, 120),
|
|
new Platform(800, 400, 100),
|
|
new Platform(150, 330, 150),
|
|
new Platform(700, 330, 150),
|
|
new Platform(400, 270, 180),
|
|
new Platform(200, 200, 120),
|
|
new Platform(650, 200, 120),
|
|
new Platform(450, 130, 180),
|
|
new Platform(300, 70, 100),
|
|
new Platform(650, 70, 100)
|
|
],
|
|
carrots: [
|
|
{x: 100, y: 360},
|
|
{x: 270, y: 330},
|
|
{x: 540, y: 360},
|
|
{x: 820, y: 330},
|
|
{x: 220, y: 270},
|
|
{x: 750, y: 270},
|
|
{x: 460, y: 210},
|
|
{x: 260, y: 140},
|
|
{x: 710, y: 140},
|
|
{x: 350, y: 80},
|
|
{x: 500, y: 80},
|
|
{x: 650, y: 10},
|
|
{x: 700, y: 10},
|
|
{x: 380, y: 60},
|
|
{x: 550, y: 150},
|
|
{x: 450, y: 180},
|
|
{x: 400, y: 200},
|
|
{x: 300, y: 300}
|
|
],
|
|
enemies: [
|
|
new Enemy(250, 290, 40, 2.5),
|
|
new Enemy(650, 290, 40, 2.5),
|
|
new Enemy(400, 220, 40, 2.5),
|
|
new Enemy(300, 160, 40, 3),
|
|
new Enemy(650, 160, 40, 3),
|
|
new Enemy(450, 80, 40, 3),
|
|
new Enemy(350, 120, 40, 2)
|
|
]
|
|
},
|
|
{
|
|
name: 'Level 16: Giga Fjender',
|
|
platforms: [
|
|
new Platform(0, 500, 1024, 76),
|
|
new Platform(100, 430, 150),
|
|
new Platform(450, 420, 150),
|
|
new Platform(750, 430, 150),
|
|
new Platform(200, 340, 120),
|
|
new Platform(700, 340, 120),
|
|
new Platform(450, 280, 180),
|
|
new Platform(150, 220, 150),
|
|
new Platform(750, 220, 150),
|
|
new Platform(400, 160, 180),
|
|
new Platform(300, 100, 120),
|
|
new Platform(650, 100, 120),
|
|
new Platform(450, 50, 150)
|
|
],
|
|
carrots: [
|
|
{x: 150, y: 360},
|
|
{x: 480, y: 350},
|
|
{x: 800, y: 360},
|
|
{x: 250, y: 280},
|
|
{x: 750, y: 280},
|
|
{x: 480, y: 220},
|
|
{x: 200, y: 160},
|
|
{x: 800, y: 160},
|
|
{x: 450, y: 100},
|
|
{x: 350, y: 50},
|
|
{x: 550, y: 50}
|
|
],
|
|
enemies: [
|
|
new Enemy(300, 300, 40, 2.5),
|
|
new Enemy(700, 300, 40, 2.5),
|
|
new Enemy(400, 230, 40, 2.5),
|
|
new Enemy(450, 120, 40, 2.5),
|
|
new Enemy(350, 70, 40, 3)
|
|
]
|
|
},
|
|
{
|
|
name: 'Level 17: Alt Går',
|
|
platforms: [
|
|
new Platform(0, 500, 1024, 76),
|
|
new Platform(100, 440, 120),
|
|
new Platform(350, 410, 100),
|
|
new Platform(600, 440, 120),
|
|
new Platform(200, 350, 130),
|
|
new Platform(700, 350, 130),
|
|
new Platform(450, 290, 150),
|
|
new Platform(250, 230, 120),
|
|
new Platform(700, 230, 120),
|
|
new Platform(450, 170, 140),
|
|
new Platform(300, 120, 100),
|
|
new Platform(650, 120, 100),
|
|
new Platform(400, 60, 160)
|
|
],
|
|
carrots: [
|
|
{x: 130, y: 370},
|
|
{x: 370, y: 340},
|
|
{x: 630, y: 370},
|
|
{x: 250, y: 290},
|
|
{x: 750, y: 290},
|
|
{x: 480, y: 230},
|
|
{x: 300, y: 170},
|
|
{x: 700, y: 170},
|
|
{x: 480, y: 110},
|
|
{x: 330, y: 60},
|
|
{x: 680, y: 60},
|
|
{x: 450, y: 0},
|
|
{x: 400, y: 150},
|
|
{x: 500, y: 150},
|
|
{x: 350, y: 300}
|
|
],
|
|
enemies: [
|
|
new Enemy(300, 310, 40, 2.5),
|
|
new Enemy(700, 310, 40, 2.5),
|
|
new Enemy(400, 240, 40, 2.5),
|
|
new Enemy(450, 130, 40, 2.5)
|
|
]
|
|
},
|
|
{
|
|
name: 'Level 18: Ekstrem',
|
|
platforms: [
|
|
new Platform(0, 500, 1024, 76),
|
|
new Platform(80, 460, 90),
|
|
new Platform(200, 440, 90),
|
|
new Platform(320, 460, 90),
|
|
new Platform(440, 440, 90),
|
|
new Platform(560, 460, 90),
|
|
new Platform(680, 440, 90),
|
|
new Platform(800, 460, 90),
|
|
new Platform(180, 360, 120),
|
|
new Platform(700, 360, 120),
|
|
new Platform(450, 300, 140),
|
|
new Platform(250, 240, 110),
|
|
new Platform(700, 240, 110),
|
|
new Platform(450, 180, 130),
|
|
new Platform(300, 130, 100),
|
|
new Platform(650, 130, 100),
|
|
new Platform(400, 70, 150)
|
|
],
|
|
carrots: [
|
|
{x: 100, y: 390},
|
|
{x: 220, y: 370},
|
|
{x: 340, y: 390},
|
|
{x: 460, y: 370},
|
|
{x: 580, y: 390},
|
|
{x: 700, y: 370},
|
|
{x: 820, y: 390},
|
|
{x: 230, y: 300},
|
|
{x: 750, y: 300},
|
|
{x: 480, y: 240},
|
|
{x: 300, y: 180},
|
|
{x: 700, y: 180},
|
|
{x: 480, y: 120},
|
|
{x: 330, y: 70},
|
|
{x: 680, y: 70},
|
|
{x: 450, y: 20},
|
|
{x: 400, y: 220},
|
|
{x: 500, y: 260}
|
|
],
|
|
enemies: [
|
|
new Enemy(300, 320, 40, 2.5),
|
|
new Enemy(700, 320, 40, 2.5),
|
|
new Enemy(400, 250, 40, 2.5),
|
|
new Enemy(450, 140, 40, 2.5),
|
|
new Enemy(350, 100, 40, 2.5)
|
|
]
|
|
},
|
|
{
|
|
name: 'Level 19: Kaos',
|
|
platforms: [
|
|
new Platform(0, 500, 1024, 76),
|
|
new Platform(120, 430, 120),
|
|
new Platform(380, 400, 110),
|
|
new Platform(640, 430, 120),
|
|
new Platform(230, 340, 130),
|
|
new Platform(700, 340, 130),
|
|
new Platform(450, 290, 120),
|
|
new Platform(180, 240, 120),
|
|
new Platform(750, 240, 120),
|
|
new Platform(400, 190, 130),
|
|
new Platform(280, 140, 100),
|
|
new Platform(680, 140, 100),
|
|
new Platform(500, 100, 120),
|
|
new Platform(330, 60, 100),
|
|
new Platform(700, 60, 100),
|
|
new Platform(450, 20, 140)
|
|
],
|
|
carrots: [
|
|
{x: 160, y: 360},
|
|
{x: 400, y: 330},
|
|
{x: 680, y: 360},
|
|
{x: 280, y: 280},
|
|
{x: 780, y: 280},
|
|
{x: 480, y: 230},
|
|
{x: 220, y: 180},
|
|
{x: 800, y: 180},
|
|
{x: 440, y: 130},
|
|
{x: 320, y: 80},
|
|
{x: 700, y: 80},
|
|
{x: 530, y: 50},
|
|
{x: 400, y: 0},
|
|
{x: 550, y: 0},
|
|
{x: 450, y: 150},
|
|
{x: 350, y: 300}
|
|
],
|
|
enemies: [
|
|
new Enemy(300, 300, 40, 2.5),
|
|
new Enemy(700, 300, 40, 2.5),
|
|
new Enemy(450, 240, 40, 2.5),
|
|
new Enemy(250, 180, 40, 2.5),
|
|
new Enemy(750, 180, 40, 2.5),
|
|
new Enemy(400, 150, 40, 2.5),
|
|
new Enemy(500, 80, 40, 2.5)
|
|
]
|
|
},
|
|
{
|
|
name: 'Level 20: BOSS NIVEAU!',
|
|
platforms: [
|
|
new Platform(0, 500, 1024, 76),
|
|
new Platform(120, 450, 120),
|
|
new Platform(450, 430, 140),
|
|
new Platform(800, 450, 120),
|
|
new Platform(230, 380, 130),
|
|
new Platform(700, 380, 130),
|
|
new Platform(450, 320, 150),
|
|
new Platform(180, 260, 120),
|
|
new Platform(800, 260, 120),
|
|
new Platform(400, 200, 160),
|
|
new Platform(280, 150, 110),
|
|
new Platform(680, 150, 110),
|
|
new Platform(500, 100, 130),
|
|
new Platform(350, 50, 120),
|
|
new Platform(650, 50, 120),
|
|
new Platform(450, 10, 160)
|
|
],
|
|
carrots: [
|
|
{x: 160, y: 380},
|
|
{x: 480, y: 360},
|
|
{x: 820, y: 380},
|
|
{x: 280, y: 320},
|
|
{x: 780, y: 320},
|
|
{x: 480, y: 260},
|
|
{x: 220, y: 210},
|
|
{x: 830, y: 210},
|
|
{x: 440, y: 150},
|
|
{x: 320, y: 100},
|
|
{x: 720, y: 100},
|
|
{x: 530, y: 50},
|
|
{x: 380, y: 0},
|
|
{x: 680, y: 0},
|
|
{x: 400, y: 180},
|
|
{x: 350, y: 400},
|
|
{x: 600, y: 400},
|
|
{x: 450, y: 70},
|
|
{x: 250, y: 150},
|
|
{x: 750, y: 150},
|
|
{x: 400, y: 330},
|
|
{x: 500, y: 200},
|
|
{x: 300, y: 200},
|
|
{x: 700, y: 200},
|
|
{x: 450, y: 240}
|
|
],
|
|
enemies: [
|
|
new Enemy(300, 340, 40, 2.5),
|
|
new Enemy(700, 340, 40, 2.5),
|
|
new Enemy(450, 280, 40, 2.5),
|
|
new Enemy(250, 220, 40, 2.5),
|
|
new Enemy(800, 220, 40, 2.5),
|
|
new Enemy(400, 160, 40, 2.5),
|
|
new Enemy(500, 100, 40, 2.5)
|
|
]
|
|
}
|
|
];
|
|
|
|
let currentLevelIndex = 0;
|
|
|
|
function loadLevel(levelIndex) {
|
|
if (levelIndex >= levels.length) {
|
|
showGameOver('Du vandt!', 'Spillet er færdigt!');
|
|
return;
|
|
}
|
|
|
|
currentLevelIndex = levelIndex;
|
|
const level = levels[levelIndex];
|
|
|
|
gameState.level = levelIndex + 1;
|
|
gameState.platforms = level.platforms;
|
|
gameState.carrots = level.carrots.map(c => new Carrot(c.x, c.y));
|
|
gameState.totalCarrots = gameState.carrots.length;
|
|
gameState.enemies = level.enemies;
|
|
gameState.gameOver = false;
|
|
gameState.score = 0;
|
|
|
|
gameState.player = new Player(50, 400);
|
|
updateUI();
|
|
}
|
|
|
|
function updateUI() {
|
|
document.getElementById('score').textContent = `Gulerødder: ${gameState.score}/${gameState.totalCarrots}`;
|
|
document.getElementById('level').textContent = `Level: ${gameState.level}`;
|
|
}
|
|
|
|
function showGameOver(title, message, nextLevel = false) {
|
|
const dialog = document.getElementById('gameOver');
|
|
document.getElementById('gameOverTitle').textContent = title;
|
|
document.getElementById('gameOverScore').textContent = `Score: ${gameState.score}`;
|
|
document.getElementById('gameOverMessage').textContent = message;
|
|
|
|
const btn = document.getElementById('restartBtn');
|
|
btn.textContent = nextLevel ? 'Næste Level' : 'Prøv igen';
|
|
|
|
btn.onclick = function() {
|
|
dialog.style.display = 'none';
|
|
if (nextLevel) {
|
|
loadLevel(currentLevelIndex + 1);
|
|
} else {
|
|
loadLevel(currentLevelIndex);
|
|
}
|
|
};
|
|
|
|
dialog.style.display = 'block';
|
|
}
|
|
|
|
// Input handling
|
|
const keys = {};
|
|
window.addEventListener('keydown', (e) => {
|
|
keys[e.key] = true;
|
|
});
|
|
window.addEventListener('keyup', (e) => {
|
|
keys[e.key] = false;
|
|
});
|
|
|
|
// Game loop
|
|
function gameLoop() {
|
|
// Clear canvas with gradient
|
|
const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
|
|
gradient.addColorStop(0, '#87CEEB');
|
|
gradient.addColorStop(1, '#E0F8FF');
|
|
ctx.fillStyle = gradient;
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
|
// Update and draw
|
|
if (!gameState.gameOver) {
|
|
gameState.player.update(keys);
|
|
|
|
for (let platform of gameState.platforms) {
|
|
platform.update();
|
|
}
|
|
|
|
for (let carrot of gameState.carrots) {
|
|
carrot.update();
|
|
}
|
|
|
|
for (let enemy of gameState.enemies) {
|
|
enemy.update();
|
|
}
|
|
|
|
gameState.player.draw();
|
|
}
|
|
|
|
requestAnimationFrame(gameLoop);
|
|
}
|
|
|
|
// Start game
|
|
loadLevel(0);
|
|
gameLoop();
|
|
</script>
|
|
</body>
|
|
</html>
|