oma-tank/game/obstacles.lua
2026-04-18 11:35:06 +01:00

106 lines
3.4 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

local World = require("game.world")
local Palette = require("rendering.palette")
local Projection = require("rendering.projection")
local Models = require("data.models")
local Player = require("game.player")
local Obstacles = {}
local obstacles = {}
-- Obstacle type mix — four arcade-authentic shapes.
local OBSTACLE_TYPES = {
"pyramid", "pyramid", "pyramid", -- low/wide pyramid (shape 12)
"pyramidTall", "pyramidTall", -- tall/narrow pyramid (shape 0)
"cube", "cube", "cube", -- tall block (shape 1)
"halfPrism", "halfPrism", -- low block/slab (shape 15)
}
local RADIUS_BY_TYPE = {
pyramid = 18, pyramidTall = 13, cube = 20, halfPrism = 14,
}
-- Effective obstacle heights for shot-blocking. Shells that are in flight above
-- the obstacle's top pass over cleanly; below, they hit.
local HEIGHT_BY_TYPE = {
pyramid = 18, pyramidTall = 16, cube = 16, halfPrism = 7,
}
-- The field is a 4000-unit torus; at 900-unit visible radius, ~50 obstacles gives
-- ~57 in view at any time — dense enough to read as an obstacle field, sparse
-- enough that navigation doesn't become a maze.
local OBSTACLE_COUNT = 50
local function tooCloseToExisting(x, z, newRadius, minClearance)
for _, o in ipairs(obstacles) do
local dx = x - o.x
local dz = z - o.z
local gap = math.sqrt(dx * dx + dz * dz) - (o.radius + newRadius)
if gap < minClearance then return true end
end
return false
end
function Obstacles.init()
obstacles = {}
local S = World.FIELD_SIZE
local centre = S / 2
local CLEARANCE = 40 -- minimum gap between any two obstacles' edges
local SPAWN_CLEAR = 150 -- no obstacles within this radius of player spawn
-- Obstacle layout is random per game; global RNG is seeded once in love.load.
local placed = 0
local attempts = 0
while placed < OBSTACLE_COUNT and attempts < 4000 do
attempts = attempts + 1
local x = math.random(50, S - 50)
local z = math.random(50, S - 50)
local dx = x - centre
local dz = z - centre
local otype = OBSTACLE_TYPES[math.random(1, #OBSTACLE_TYPES)]
local r = RADIUS_BY_TYPE[otype] or 20
if math.sqrt(dx*dx + dz*dz) > SPAWN_CLEAR
and not tooCloseToExisting(x, z, r, CLEARANCE) then
table.insert(obstacles, {
x = x, z = z,
otype = otype,
radius = r,
height = HEIGHT_BY_TYPE[otype] or 16,
})
placed = placed + 1
end
end
end
function Obstacles.getAll()
return obstacles
end
function Obstacles.checkCollision(x, z, radius)
for _, o in ipairs(obstacles) do
local dx = World.wrappedDelta(x, o.x, World.FIELD_SIZE)
local dz = World.wrappedDelta(z, o.z, World.FIELD_SIZE)
local dist = math.sqrt(dx*dx + dz*dz)
if dist < radius + o.radius then
return true, o
end
end
return false
end
function Obstacles.draw()
local p = Palette.get()
love.graphics.setColor(p.obstacle)
local pl = Player.get()
local px, pz = pl.x, pl.z
for _, o in ipairs(obstacles) do
local dx, dz, dist = World.wrappedDist(px, pz, o.x, o.z)
if dist < 900 then
local model = Models[o.otype] or Models.cube
Projection.drawModel(model, px + dx, 0, pz + dz)
end
end
end
return Obstacles