function updateGameLogic() {
    let currentSpeed = BASE_SPEED + (currentTick / 1000.0) * 1.5;
    if (currentSpeed > 36.0) currentSpeed = 36.0;

    updateParticles();
    if (typeof setMusicSpeed === "function") {
        setMusicSpeed(currentSpeed);
    }


    player.prevY = player.y;

    obstacleBuffer.forEach(o => o.prevX = o.x);
    platformBuffer.forEach(p => p.prevX = p.x);

    const originalHeight = 50;
    const crouchHeight = 25;

    player.h = isCrouching ? crouchHeight : originalHeight;
    let drawY = isCrouching ? player.y + (originalHeight - crouchHeight) : player.y;

    const oldY = player.y;

    player.vy += GRAVITY;
    if (isCrouching && !player.grounded) player.vy += 2.0;

    let newY = player.y + player.vy;
    let landed = false;


    if (player.vy > 0) {
        for (let plat of platformBuffer) {
            if (plat.x < GAME_WIDTH + 100 && plat.x > -100) {
                if (player.x + 30 > plat.x && player.x < plat.x + plat.w) {
                    const feetOld = oldY + originalHeight;
                    const feetNew = newY + originalHeight;
                    if (feetOld <= plat.y && feetNew >= plat.y) {
                        newY = plat.y - originalHeight;
                        player.vy = 0;
                        landed = true;
                        sendPhysicsSync(newY, 0);
                        break;
                    }
                }
            }
        }
    }


    if (!landed && newY + originalHeight >= GROUND_Y) {
        newY = GROUND_Y - originalHeight;
        player.vy = 0;
        landed = true;
    }

    if (currentTick % 10 === 0) {
        sendPhysicsSync(player.y, player.vy);
    }

    player.y = newY;
    player.grounded = landed;


    obstacleBuffer.forEach(o => o.x -= currentSpeed);
    platformBuffer.forEach(p => p.x -= currentSpeed);


    obstacleBuffer = obstacleBuffer.filter(o => o.x + (o.w||30) > -200); // Muss -200 sein
    platformBuffer = platformBuffer.filter(p => p.x + (p.w||100) > -200); // Muss -200 sein


    obstacles = [];
    platforms = [];
    const RENDER_LIMIT = 900;


    const pHitbox = { x: player.x, y: drawY, w: player.w, h: player.h };


    obstacleBuffer.forEach(obs => {

        if (obs.x < RENDER_LIMIT) {


            if (!obs.def) {
                let baseDef = null;
                if(gameConfig && gameConfig.obstacles) {
                    baseDef = gameConfig.obstacles.find(x => x.id === obs.id);
                }
                obs.def = {
                    id: obs.id,
                    type: obs.type || (baseDef ? baseDef.type : "obstacle"),
                    width: obs.w || (baseDef ? baseDef.width : 30),
                    height: obs.h || (baseDef ? baseDef.height : 30),
                    color: obs.color || (baseDef ? baseDef.color : "red"),
                    image: baseDef ? baseDef.image : null,
                    imgScale: baseDef ? baseDef.imgScale : 1.0,
                    imgOffsetX: baseDef ? baseDef.imgOffsetX : 0,
                    imgOffsetY: baseDef ? baseDef.imgOffsetY : 0
                };
            }

            if (!obs.collected && !isGameOver) {
                if (checkCollision(pHitbox, obs)) {

                    const type = obs.def.type;
                    const id = obs.def.id;

                    // 1. COIN
                    if (type === "coin") {
                        score += 2000;
                        obs.collected = true;
                        playSound('coin');
                        spawnParticles(obs.x + 15, obs.y + 15, 'sparkle', 10);
                    }
                    // 2. POWERUP
                    else if (type === "powerup") {
                        if (id === "p_god") godModeLives = 3;
                        if (id === "p_bat") hasBat = true;
                        if (id === "p_boot") bootTicks = 600;
                        playSound('powerup');
                        spawnParticles(obs.x + 15, obs.y + 15, 'sparkle', 20); // Mehr Partikel

                        obs.collected = true;
                    }

                    else {

                        if (hasBat && type === "teacher") {
                            hasBat = false;
                            obs.collected = true;
                            playSound('hit');
                            spawnParticles(obs.x, obs.y, 'explosion', 5);

                        }
                        // Godmode (Schild)
                        else if (godModeLives > 0) {
                            godModeLives--;
                            playSound('hit');
                            spawnParticles(obs.x, obs.y, 'explosion', 5);
                            obs.collected = true;
                        }
                        // TOT
                        else {
                            console.log("💥 Kollision!");
                            player.color = "darkred";
                            gameOver("Kollision");
                            playSound('hit');
                            spawnParticles(player.x + 15, player.y + 25, 'explosion', 50); // Riesige Explosion
                            if (typeof sendInput === "function") sendInput("input", "DEATH");
                        }
                    }
                }
            }

            if (!obs.collected) {
                obstacles.push(obs);
            }
        }
    });


    platformBuffer.forEach(plat => {
        if (plat.x < RENDER_LIMIT) {
            platforms.push(plat);
        }
    });
}


function checkCollision(p, obs) {
    const def = obs.def || {};
    const w = def.width || obs.w || 30;
    const h = def.height || obs.h || 30;


    const padX = 8;
    const padY = (def.type === "teacher" || def.type === "principal") ? 20 : 5;


    const pL = p.x + padX;
    const pR = p.x + p.w - padX;
    const pT = p.y + padY;
    const pB = p.y + p.h - 5;

    const oL = obs.x + padX;
    const oR = obs.x + w - padX;
    const oT = obs.y + padY;
    const oB = obs.y + h - 5;

    return (pR > oL && pL < oR && pB > oT && pT < oB);
}