function updateGameLogic() { // 1. Speed Berechnung (Sync mit Server!) let currentSpeed = BASE_SPEED + (score / 1000); if (currentSpeed > 10.0) currentSpeed = 10.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 = 10; const realRightEdge = obs.x + obs.def.width - paddingX; if (realRightEdge < p.x + 5) { return false; } const paddingY_Top = (obs.def.type === "teacher") ? 25 : 10; const paddingY_Bottom = 5; // Geschwindigkeit schätzen (oder global holen) für CCD let currentSpeed = 5 + (score / 5000.0) * 0.5; // /5000 weil score hier /10 ist? // Moment, in main.js ist 'score' der rohe Wert. Also /500. // Da wir score global haben: currentSpeed = 5 + (score / 500.0) * 0.5; if (currentSpeed > 12.0) currentSpeed = 12.0; const pLeft = p.x + paddingX; const pRight = p.x + p.w - paddingX; const pTop = p.y + paddingY_Top; const pBottom = p.y + p.h - paddingY_Bottom; const oLeft = obs.x + paddingX; // CCD Erweiterung const oRight = obs.x + obs.def.width - paddingX + currentSpeed; const oTop = obs.y + paddingY_Top; const oBottom = obs.y + obs.def.height - paddingY_Bottom; return (pRight > oLeft && pLeft < oRight && pBottom > oTop && pTop < oBottom); }