add pics
All checks were successful
Dynamic Branch Deploy / build-and-deploy (push) Successful in 1m11s
All checks were successful
Dynamic Branch Deploy / build-and-deploy (push) Successful in 1m11s
This commit is contained in:
BIN
static/assets/gym-background.jpg
Normal file
BIN
static/assets/gym-background.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 184 KiB |
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
BIN
static/assets/school2-background.jpg
Normal file
BIN
static/assets/school2-background.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 142 KiB |
@@ -3,8 +3,9 @@ const GAME_WIDTH = 800;
|
||||
const GAME_HEIGHT = 400;
|
||||
const GRAVITY = 0.6;
|
||||
const JUMP_POWER = -12;
|
||||
const HIGH_JUMP_POWER = -16;
|
||||
const GROUND_Y = 350;
|
||||
const GAME_SPEED = 5;
|
||||
const BASE_SPEED = 5;
|
||||
const CHUNK_SIZE = 60;
|
||||
const TARGET_FPS = 60;
|
||||
const MS_PER_TICK = 1000 / TARGET_FPS;
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
function updateGameLogic() {
|
||||
if (isCrouching) {
|
||||
inputLog.push({ t: currentTick - lastSentTick, act: "DUCK" });
|
||||
}
|
||||
// 1. Speed Berechnung (Sync mit Server!)
|
||||
let currentSpeed = BASE_SPEED + (score / 500.0) * 0.5;
|
||||
if (currentSpeed > 12.0) currentSpeed = 12.0;
|
||||
|
||||
// 2. Input & Sprung
|
||||
if (isCrouching) inputLog.push({ t: currentTick - lastSentTick, act: "DUCK" });
|
||||
|
||||
const originalHeight = 50; const crouchHeight = 25;
|
||||
player.h = isCrouching ? crouchHeight : originalHeight;
|
||||
let drawY = isCrouching ? player.y + (originalHeight - crouchHeight) : player.y;
|
||||
|
||||
// Jump Power Check
|
||||
let jumpP = JUMP_POWER;
|
||||
if (bootTicks > 0) {
|
||||
jumpP = HIGH_JUMP_POWER;
|
||||
bootTicks--;
|
||||
}
|
||||
|
||||
// Physik
|
||||
player.vy += GRAVITY;
|
||||
if (isCrouching && !player.grounded) player.vy += 2.0;
|
||||
player.y += player.vy;
|
||||
@@ -14,16 +25,45 @@ function updateGameLogic() {
|
||||
if (player.y + originalHeight >= GROUND_Y) {
|
||||
player.y = GROUND_Y - originalHeight; player.vy = 0; player.grounded = true;
|
||||
} else { player.grounded = false; }
|
||||
|
||||
|
||||
// 3. Obstacles
|
||||
let nextObstacles = [];
|
||||
let rightmostX = 0;
|
||||
|
||||
let nextObstacles = []; let rightmostX = 0;
|
||||
for (let obs of obstacles) {
|
||||
obs.x -= GAME_SPEED;
|
||||
obs.x -= currentSpeed;
|
||||
|
||||
const playerHitbox = { x: player.x, y: drawY, w: player.w, h: player.h };
|
||||
|
||||
if (checkCollision(playerHitbox, obs)) {
|
||||
player.color = "darkred";
|
||||
if (!isGameOver) { sendChunk(); gameOver("Kollision"); }
|
||||
// TYPE CHECK
|
||||
if (obs.def.type === "coin") {
|
||||
score += 2000;
|
||||
continue;
|
||||
}
|
||||
else if (obs.def.type === "powerup") {
|
||||
if (obs.def.id === "p_god") godModeLives = 3;
|
||||
if (obs.def.id === "p_bat") hasBat = true;
|
||||
if (obs.def.id === "p_boot") bootTicks = 600;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
// HINDERNIS
|
||||
if (hasBat && obs.def.type === "teacher") {
|
||||
hasBat = false;
|
||||
continue; // Zerstört!
|
||||
}
|
||||
if (godModeLives > 0) {
|
||||
godModeLives--;
|
||||
continue; // Überlebt!
|
||||
}
|
||||
|
||||
player.color = "darkred";
|
||||
if (!isGameOver) { sendChunk(); gameOver("Kollision"); }
|
||||
}
|
||||
}
|
||||
|
||||
if (obs.x + obs.def.width > -100) {
|
||||
nextObstacles.push(obs);
|
||||
if (obs.x + obs.def.width > rightmostX) rightmostX = obs.x + obs.def.width;
|
||||
@@ -31,23 +71,43 @@ function updateGameLogic() {
|
||||
}
|
||||
obstacles = nextObstacles;
|
||||
|
||||
// Spawning
|
||||
// 4. Spawning (Sync mit Go!)
|
||||
if (rightmostX < GAME_WIDTH - 10 && gameConfig) {
|
||||
const gap = Math.floor(400 + rng.nextRange(0, 500));
|
||||
let spawnX = rightmostX + gap; if (spawnX < GAME_WIDTH) spawnX = GAME_WIDTH;
|
||||
|
||||
const isBossPhase = (score % 1500) > 1200;
|
||||
|
||||
let possibleObs = [];
|
||||
gameConfig.obstacles.forEach(def => {
|
||||
if (def.id === "eraser") { if (score >= 500) possibleObs.push(def); } else possibleObs.push(def);
|
||||
if (isBossPhase) {
|
||||
if (def.id === "principal" || def.id === "trashcan") possibleObs.push(def);
|
||||
} else {
|
||||
if (def.id === "principal") return;
|
||||
if (def.id === "eraser" && score < 500) return;
|
||||
possibleObs.push(def);
|
||||
}
|
||||
});
|
||||
const def = rng.pick(possibleObs);
|
||||
|
||||
let def = rng.pick(possibleObs);
|
||||
|
||||
// Speech Sync
|
||||
let speech = null;
|
||||
if (def && def.canTalk) { if (rng.nextFloat() > 0.7) speech = rng.pick(def.speechLines); }
|
||||
if (def && def.canTalk) {
|
||||
if (rng.nextFloat() > 0.7) speech = rng.pick(def.speechLines);
|
||||
}
|
||||
|
||||
// Powerup Rarity Sync (Muss exakt wie Go sein: 10% Chance)
|
||||
if (def && def.type === "powerup") {
|
||||
if (rng.nextFloat() > 0.1) def = null;
|
||||
}
|
||||
|
||||
if (def) {
|
||||
const yOffset = def.yOffset || 0;
|
||||
obstacles.push({ x: spawnX, y: GROUND_Y - def.height - yOffset, def: def, speech: speech });
|
||||
obstacles.push({
|
||||
x: spawnX, y: GROUND_Y - def.height - yOffset,
|
||||
def: def, speech: speech
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,46 +44,53 @@ resize();
|
||||
// --- DRAWING ---
|
||||
|
||||
function drawGame() {
|
||||
// Alles löschen
|
||||
ctx.clearRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
|
||||
|
||||
// Background
|
||||
// --- BACKGROUND ---
|
||||
// Hier war der Check schon drin, das ist gut
|
||||
if (bgSprite.complete && bgSprite.naturalHeight !== 0) {
|
||||
// Hintergrundbild exakt auf 800x400 skalieren
|
||||
ctx.drawImage(bgSprite, 0, 0, GAME_WIDTH, GAME_HEIGHT);
|
||||
} else {
|
||||
// Fallback Farbe
|
||||
ctx.fillStyle = "#f0f0f0";
|
||||
ctx.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
|
||||
}
|
||||
|
||||
// Boden
|
||||
ctx.fillStyle = "rgba(60, 60, 60, 0.8)";
|
||||
ctx.fillRect(0, GROUND_Y, GAME_WIDTH, 50);
|
||||
|
||||
// Hindernisse
|
||||
// --- HINDERNISSE (HIER WAR DER FEHLER) ---
|
||||
obstacles.forEach(obs => {
|
||||
const img = sprites[obs.def.id];
|
||||
if (img) {
|
||||
|
||||
// FIX: Wir prüfen jetzt strikt, ob das Bild wirklich bereit ist
|
||||
if (img && img.complete && img.naturalHeight !== 0) {
|
||||
ctx.drawImage(img, obs.x, obs.y, obs.def.width, obs.def.height);
|
||||
} else {
|
||||
ctx.fillStyle = obs.def.color;
|
||||
// Fallback: Wenn Bild fehlt/kaputt -> Farbiges Rechteck
|
||||
// Wir prüfen auf Typ Coin, damit Coins gold sind, auch wenn Bild fehlt
|
||||
if (obs.def.type === "coin") ctx.fillStyle = "gold";
|
||||
else ctx.fillStyle = obs.def.color;
|
||||
|
||||
ctx.fillRect(obs.x, obs.y, obs.def.width, obs.def.height);
|
||||
}
|
||||
|
||||
if(obs.speech) drawSpeechBubble(obs.x, obs.y, obs.speech);
|
||||
});
|
||||
|
||||
// Debug Rahmen (Server Hitboxen)
|
||||
|
||||
/*
|
||||
// --- DEBUG ---
|
||||
ctx.strokeStyle = isGameOver ? "red" : "lime";
|
||||
ctx.lineWidth = 2;
|
||||
serverObstacles.forEach(srvObs => {
|
||||
ctx.strokeRect(srvObs.x, srvObs.y, srvObs.w, srvObs.h);
|
||||
});
|
||||
*/
|
||||
|
||||
// Spieler
|
||||
// --- PLAYER ---
|
||||
const drawY = isCrouching ? player.y + 25 : player.y;
|
||||
const drawH = isCrouching ? 25 : 50;
|
||||
|
||||
// Hier war der Check auch schon korrekt
|
||||
if (playerSprite.complete && playerSprite.naturalHeight !== 0) {
|
||||
ctx.drawImage(playerSprite, player.x, drawY, player.w, drawH);
|
||||
} else {
|
||||
@@ -91,13 +98,31 @@ function drawGame() {
|
||||
ctx.fillRect(player.x, drawY, player.w, drawH);
|
||||
}
|
||||
|
||||
// Game Over Overlay (Dunkelheit)
|
||||
// --- POWERUP UI (Oben Links) ---
|
||||
if (isGameRunning && !isGameOver) {
|
||||
ctx.fillStyle = "black";
|
||||
ctx.font = "bold 10px monospace";
|
||||
ctx.textAlign = "left";
|
||||
let statusText = "";
|
||||
if(godModeLives > 0) statusText += `🛡️ x${godModeLives} `;
|
||||
if(hasBat) statusText += `⚾ BAT `;
|
||||
if(bootTicks > 0) statusText += `👟 ${(bootTicks/60).toFixed(1)}s`;
|
||||
|
||||
// Drift Anzeige
|
||||
if (obstacles.length > 0 && serverObstacles.length > 0) {
|
||||
const drift = Math.abs(obstacles[0].x - serverObstacles[0].x).toFixed(1);
|
||||
statusText += ` | Drift: ${drift}px`;
|
||||
}
|
||||
ctx.fillText(statusText, 10, 40);
|
||||
}
|
||||
|
||||
if (isGameOver) {
|
||||
ctx.fillStyle = "rgba(0,0,0,0.7)";
|
||||
ctx.fillRect(0,0,GAME_WIDTH, GAME_HEIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
// Sprechblasen Helper
|
||||
function drawSpeechBubble(x, y, text) {
|
||||
const bX = x-20; const bY = y-40; const bW = 120; const bH = 30;
|
||||
ctx.fillStyle="white"; ctx.fillRect(bX,bY,bW,bH);
|
||||
|
||||
@@ -12,6 +12,14 @@ let lastSentTick = 0;
|
||||
let inputLog = [];
|
||||
let isCrouching = false;
|
||||
|
||||
// Powerups Client State
|
||||
let godModeLives = 0;
|
||||
let hasBat = false;
|
||||
let bootTicks = 0;
|
||||
|
||||
// Hintergrund
|
||||
let currentBgIndex = 0;
|
||||
|
||||
// Tick Time
|
||||
let lastTime = 0;
|
||||
let accumulator = 0;
|
||||
|
||||
Reference in New Issue
Block a user