diff --git a/src/game.js b/src/game.js index f6ae17a..ef481f4 100644 --- a/src/game.js +++ b/src/game.js @@ -97,20 +97,83 @@ let particles = []; let lastTime = 0; - const walls = [ - ...brickWall(120, 80, TILE, TILE * 3), - ...brickWall(240, 120, TILE * 4, TILE), - ...steelWall(520, 80, TILE, TILE * 3), - ...brickWall(640, 160, TILE * 2, TILE), - ...brickWall(80, 320, TILE * 3, TILE), - ...brickWall(280, 280, TILE, TILE * 3), - ...brickWall(400, 360, TILE * 4, TILE), - ...steelWall(640, 360, TILE, TILE * 3), - ...steelWall(560, 240, TILE * 2, TILE), - ...brickWall(160, 480, TILE * 2, TILE), - ...brickWall(480, 500, TILE * 3, TILE), + // Static brick layout (kept stable across games). Steel walls are randomized + // on every reset, so they are added separately in resetGame via regenerateWalls. + const BRICK_LAYOUT = [ + [120, 80, TILE, TILE * 3], + [240, 120, TILE * 4, TILE], + [640, 160, TILE * 2, TILE], + [80, 320, TILE * 3, TILE], + [280, 280, TILE, TILE * 3], + [400, 360, TILE * 4, TILE], + [160, 480, TILE * 2, TILE], + [480, 500, TILE * 3, TILE], ]; + // Rectangles that steel walls must avoid so spawns are not blocked or boxed in. + // Each entry is padded around a player/enemy starting position. + const SPAWN_KEEPOUTS = [ + spawnKeepout(384, 536), // player + spawnKeepout(84, 64), + spawnKeepout(384, 64), + spawnKeepout(684, 64), + spawnKeepout(84, 220), + spawnKeepout(684, 260), + ]; + + let walls = []; + + function spawnKeepout(x, y) { + // Generous margin (1.5 tiles) around the spawn keeps lanes open so tanks + // are never immediately boxed in by randomized steel. + const margin = TILE * 1.5; + return { + x: x - margin, + y: y - margin, + w: TANK_SIZE + margin * 2, + h: TANK_SIZE + margin * 2, + }; + } + + function buildBrickWalls() { + const pieces = []; + for (const [x, y, w, h] of BRICK_LAYOUT) { + pieces.push(...brickWall(x, y, w, h)); + } + return pieces; + } + + function randomSteelWalls(occupied) { + // Generate a handful of vertical/horizontal steel segments at random tile + // positions, skipping any that overlap a spawn keep-out zone. + const pieces = []; + const count = 3 + Math.floor(Math.random() * 3); // 3-5 steel walls + const cols = Math.floor(WIDTH / TILE); + const rows = Math.floor(HEIGHT / TILE); + let attempts = 0; + while (pieces.length < count && attempts < count * 20) { + attempts += 1; + const vertical = Math.random() < 0.5; + const length = (2 + Math.floor(Math.random() * 2)) * TILE; // 2-3 tiles long + const x = Math.floor(Math.random() * cols) * TILE; + const y = Math.floor(Math.random() * rows) * TILE; + const w = vertical ? TILE : length; + const h = vertical ? length : TILE; + const candidate = { x, y, w, h }; + if (x + w > WIDTH || y + h > HEIGHT) continue; + if (SPAWN_KEEPOUTS.some((zone) => intersects(candidate, zone))) continue; + if (occupied.some((area) => intersects(candidate, area))) continue; + pieces.push(...steelWall(x, y, w, h)); + occupied.push(candidate); + } + return pieces; + } + + function regenerateWalls() { + const bricks = buildBrickWalls(); + walls = [...bricks, ...randomSteelWalls([...bricks])]; + } + function rect(x, y, w, h, type = "brick") { return { x, y, w, h, type, destructible: type === "brick" }; } @@ -153,6 +216,7 @@ lives = 3; bullets = []; particles = []; + regenerateWalls(); player = tank(384, 536, "#77b255", "up", "player"); enemies = [ tank(84, 64, "#db5a42", "down"), @@ -565,6 +629,7 @@ if (stopAction) stopAction.addEventListener("click", endGame); if (endAction) endAction.addEventListener("click", endGame); + regenerateWalls(); player = tank(384, 536, "#77b255", "up", "player"); updateHud(); setState("menu");