function updateGameLogic() { 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; 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; } let nextObstacles = []; let rightmostX = 0; for (let obs of obstacles) { obs.x -= GAME_SPEED; 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"); } } 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; // Spawning 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; let possibleObs = []; gameConfig.obstacles.forEach(def => { if (def.id === "eraser") { if (score >= 500) possibleObs.push(def); } else possibleObs.push(def); }); const def = rng.pick(possibleObs); let speech = null; if (def && def.canTalk) { if (rng.nextFloat() > 0.7) speech = rng.pick(def.speechLines); } 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); }