function updateGameLogic() { // 1. Speed Berechnung (Sync mit Server!) let currentSpeed = BASE_SPEED + (score / 750.0) * 0.5; if (currentSpeed > 14.0) currentSpeed = 14.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; 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; for (let obs of obstacles) { obs.x -= currentSpeed; const playerHitbox = { x: player.x, y: drawY, w: player.w, h: player.h }; if (checkCollision(playerHitbox, obs)) { // 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; } } obstacles = nextObstacles; // 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 (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); } }); let def = rng.pick(possibleObs); // Speech Sync let speech = null; 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 }); } } } function checkCollision(p, obs) { const paddingX = 5; const paddingY_Top = 5; const paddingY_Bottom = 5; return (p.x + p.w - paddingX > obs.x + paddingX && p.x + paddingX < obs.x + obs.def.width - paddingX && p.y + p.h - paddingY_Bottom > obs.y + paddingY_Top && p.y + paddingY_Top < obs.y + obs.def.height - paddingY_Bottom); }