local World = require("game.world") local Palette = require("rendering.palette") local Terrain = {} local points = {} local pads = {} function Terrain.generate() points = {} pads = {} local W = World.WORLD_W local baseline = 1600 local step = 30 -- Place landing pads first local padDefs = { {width = 120, mult = 2, label = "2X"}, {width = 80, mult = 3, label = "3X"}, {width = 50, mult = 5, label = "5X"}, } -- Distribute pads across the terrain local padPositions = {} local usedZones = {} for _, def in ipairs(padDefs) do local attempts = 0 local px repeat px = 400 + math.random() * (W - 800) attempts = attempts + 1 local ok = true for _, used in ipairs(usedZones) do if math.abs(px - used) < 400 then ok = false; break end end if ok then break end until attempts > 50 local py = baseline + (math.random() - 0.5) * 200 table.insert(padPositions, {x = px, y = py, width = def.width, mult = def.mult, label = def.label}) table.insert(usedZones, px) table.insert(pads, { x1 = px - def.width / 2, x2 = px + def.width / 2, y = py, mult = def.mult, label = def.label, }) end -- Sort pads by X for terrain generation table.sort(pads, function(a, b) return a.x1 < b.x1 end) -- Generate terrain points left to right local x = 0 local y = baseline + (math.random() - 0.5) * 100 table.insert(points, {x = x, y = y}) while x < W do -- Check if we're approaching a pad local onPad = false local currentPad = nil for _, pad in ipairs(pads) do if x >= pad.x1 - step and x <= pad.x2 + step then onPad = true currentPad = pad break end end if onPad and currentPad then -- Transition to pad level if x < currentPad.x1 then -- Approach: slope down/up to pad table.insert(points, {x = currentPad.x1 - 5, y = y}) table.insert(points, {x = currentPad.x1, y = currentPad.y}) x = currentPad.x1 end -- Flat pad table.insert(points, {x = currentPad.x2, y = currentPad.y}) x = currentPad.x2 y = currentPad.y -- Resume jagged after pad x = x + step * 0.5 y = y + (math.random() - 0.5) * 60 table.insert(points, {x = x, y = y}) else x = x + step + math.random() * step -- Jagged variation y = y + (math.random() - 0.5) * 120 y = math.max(baseline - 250, math.min(baseline + 250, y)) table.insert(points, {x = x, y = y}) end end -- Ensure last point reaches edge table.insert(points, {x = W, y = points[#points].y}) end function Terrain.getPoints() return points end function Terrain.getPads() return pads end function Terrain.getHeightAt(wx) -- Find terrain height at world X by interpolating between points if #points < 2 then return 1600 end if wx <= points[1].x then return points[1].y end if wx >= points[#points].x then return points[#points].y end for i = 1, #points - 1 do if points[i].x <= wx and points[i+1].x > wx then local t = (wx - points[i].x) / (points[i+1].x - points[i].x) return points[i].y + t * (points[i+1].y - points[i].y) end end return 1600 end function Terrain.getPadAt(wx) for _, pad in ipairs(pads) do if wx >= pad.x1 and wx <= pad.x2 then return pad end end return nil end function Terrain.draw(visMinX, visMaxX) local p = Palette.get() -- Draw terrain surface love.graphics.setColor(p.terrain) love.graphics.setLineWidth(2) local pts = {} for _, pt in ipairs(points) do if pt.x >= visMinX - 100 and pt.x <= visMaxX + 100 then table.insert(pts, pt.x) table.insert(pts, pt.y) end end if #pts >= 4 then love.graphics.line(pts) end -- Draw landing pads (brighter, with labels) love.graphics.setColor(p.pad) love.graphics.setLineWidth(3) for _, pad in ipairs(pads) do if pad.x2 >= visMinX and pad.x1 <= visMaxX then love.graphics.line(pad.x1, pad.y, pad.x2, pad.y) -- Label below pad love.graphics.setColor(p.pad[1], p.pad[2], p.pad[3], 0.7) local labelX = (pad.x1 + pad.x2) / 2 love.graphics.print(pad.label, labelX - 8, pad.y + 5) end end end return Terrain