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

101 lines
3.9 KiB
Lua

local World = require("game.world")
local Palette = require("rendering.palette")
local Fonts = require("rendering.fonts")
local HUD = {}
-- Tiny top-down tank icon (like the original lives display)
local function drawTankIcon(cx, cy, s)
-- Hull outline
love.graphics.line(cx - s*1.2, cy - s*0.5, cx + s*0.6, cy - s*0.5)
love.graphics.line(cx + s*0.6, cy - s*0.5, cx + s*0.8, cy)
love.graphics.line(cx + s*0.8, cy, cx + s*0.6, cy + s*0.5)
love.graphics.line(cx + s*0.6, cy + s*0.5, cx - s*1.2, cy + s*0.5)
love.graphics.line(cx - s*1.2, cy + s*0.5, cx - s*1.2, cy - s*0.5)
-- Barrel
love.graphics.line(cx + s*0.4, cy - s*0.15, cx + s*1.8, cy - s*0.15)
love.graphics.line(cx + s*0.4, cy + s*0.15, cx + s*1.8, cy + s*0.15)
end
local function bearingLabel(player, enemy)
-- Returns an arcade-style directional message: "ENEMY IN RANGE",
-- "ENEMY TO LEFT", "ENEMY TO RIGHT", or "ENEMY TO REAR". Returns nil
-- when there is no targetable enemy (e.g. only a saucer present).
if not (enemy and enemy.alive) then return nil end
local dx, dz = World.wrappedDist(player.x, player.z, enemy.x, enemy.z)
local cosA = math.cos(player.angle)
local sinA = math.sin(player.angle)
local rx = dx * cosA - dz * sinA -- rightward
local rz = dx * sinA + dz * cosA -- forward
local dist = math.sqrt(rx * rx + rz * rz)
local bearing = math.atan2(rx, rz)
if rz > 0 and math.abs(bearing) < 0.25 and dist < 800 then
return "ENEMY IN RANGE"
end
if math.abs(bearing) > 3 * math.pi / 4 then return "ENEMY TO REAR" end
if bearing > 0 then return "ENEMY TO RIGHT" end
return "ENEMY TO LEFT"
end
function HUD.draw(player, enemy)
local p = Palette.get()
local sw = World.screenW
local sh = World.screenH
local rh = World.radarH
local sc = sw / 1024
-- === LIVES (top-left) as tank icons ===
love.graphics.setColor(p.bright)
love.graphics.setLineWidth(1.3 * sc)
local iconSize = 7 * sc
local iconSpacing = 26 * sc
local livesStartX = 30 * sc
local livesY = rh * 0.25
for i = 1, math.min(World.lives, 6) do
drawTankIcon(livesStartX + (i - 1) * iconSpacing, livesY, iconSize)
end
-- === STATUS MESSAGES (authentic arcade: top-left of display) ===
-- "MOTION BLOCKED BY OBJECT" takes priority while active, then falls back to
-- the directional enemy text ("ENEMY IN RANGE" / "ENEMY TO LEFT/RIGHT/REAR").
if player and player.alive then
love.graphics.setFont(Fonts.small)
if (player.blockedMessageTimer or 0) > 0 then
local alpha = math.min(1, player.blockedMessageTimer / 0.3)
love.graphics.setColor(p.bright[1], p.bright[2], p.bright[3], alpha)
love.graphics.print("MOTION BLOCKED BY OBJECT", livesStartX, rh * 0.55)
else
local label = bearingLabel(player, enemy)
if label then
love.graphics.setColor(p.bright)
love.graphics.print(label, livesStartX, rh * 0.55)
end
end
end
-- === SCORE (top-right, right-aligned) ===
love.graphics.setFont(Fonts.medium)
love.graphics.setColor(p.bright)
local scoreStr = string.format("%09d", World.score)
local rightEdge = sw - 30 * sc
local valW = Fonts.medium:getWidth(scoreStr)
local scoreY = rh * 0.35
love.graphics.print(scoreStr, rightEdge - valW, scoreY)
-- === HIGH SCORE (below score) ===
love.graphics.setFont(Fonts.small)
love.graphics.setColor(p.bright[1], p.bright[2], p.bright[3], 0.7)
local hsY = scoreY + Fonts.medium:getHeight() + 2 * sc
local hsStr = string.format("%09d", World.highScore)
local hsW = Fonts.small:getWidth(hsStr)
love.graphics.print(hsStr, rightEdge - hsW, hsY)
-- === SEPARATOR LINE ===
love.graphics.setColor(p.dim[1], p.dim[2], p.dim[3], 0.5)
love.graphics.setLineWidth(1)
love.graphics.line(0, rh, sw, rh)
end
return HUD