local World = require("game.world") local Palette = require("rendering.palette") local Radar = {} -- Authentic-arcade circular radar: rotating sweep line, fading blips where the -- sweep last crossed an enemy, forward-cone diagonals, 4 compass ticks. local RADAR_RANGE = 1000 -- world units shown inside the circle local SWEEP_SPEED = 4 -- rad/s -- Blip fade: alpha drops ~0.51 per second after the sweep has passed an enemy local BLIP_FADE_PER_SEC = 0.51 local FORWARD_CONE_HALF = math.rad(30) local sweepAngle = 0 local blips = {} -- [id] = { rx, ry, alpha } in radar-local coords function Radar.init() sweepAngle = 0 blips = {} end function Radar.update(dt, player, enemy) -- Sweep rotates counter-clockwise (angle decreases each tick) sweepAngle = (sweepAngle - SWEEP_SPEED * dt) % (math.pi * 2) -- Fade existing blips for id, b in pairs(blips) do b.alpha = b.alpha - BLIP_FADE_PER_SEC * dt if b.alpha <= 0 then blips[id] = nil end end if not (enemy and enemy.alive and player) then return end -- Enemy position in radar-local (player-facing) coords — forward = +y on the scope local dx, dz, dist = World.wrappedDist(player.x, player.z, enemy.x, enemy.z) if dist > RADAR_RANGE then return end local cosA = math.cos(player.angle) local sinA = math.sin(player.angle) local rx = dx * cosA - dz * sinA -- right on radar local ry = dx * sinA + dz * cosA -- forward on radar (drawn as -y on screen) -- Register a blip if the sweep line passed over the enemy's bearing this frame. -- -- The sweep rotates CCW, so across one frame the sweep angle moves from -- `lastSweep` → `sweepAngle` (a decrease mod 2π). The enemy is "inside" that -- arc when its bearing lies in [sweepAngle, lastSweep]. The arc wraps past 0 -- when sweepAngle > lastSweep (decrementing wrapped around), so there are -- two cases: -- • non-wrap: sweepAngle < lastSweep → enemyBearing in [sweepAngle, lastSweep] -- • wrapped: sweepAngle > lastSweep → enemyBearing in [sweepAngle, 2π) ∪ [0, lastSweep] local enemyBearing = math.atan2(rx, ry) % (math.pi * 2) local lastSweep = (sweepAngle + SWEEP_SPEED * dt) % (math.pi * 2) local swept if sweepAngle <= lastSweep then swept = enemyBearing >= sweepAngle and enemyBearing <= lastSweep else swept = enemyBearing >= sweepAngle or enemyBearing <= lastSweep end if swept then blips.current = { rx = rx / RADAR_RANGE, ry = ry / RADAR_RANGE, alpha = 1.0 } end end function Radar.draw(player, enemy, _obstacles) local p = Palette.get() local sw = World.screenW local rh = World.radarH local sc = sw / 1024 -- Update drift, but in draw so caller doesn't need to plumb a dedicated hook yet. Radar.update(love.timer.getDelta(), player, enemy) -- Radar strip background love.graphics.setColor(p.radar_bg[1], p.radar_bg[2], p.radar_bg[3], 0.4) love.graphics.rectangle("fill", 0, 0, sw, rh) -- Radar circle position — top-centre of the HUD strip local radius = math.min(rh * 0.42, sw * 0.08) local cx = sw / 2 local cy = rh / 2 -- Outer ring love.graphics.setColor(p.bright[1], p.bright[2], p.bright[3], 0.65) love.graphics.setLineWidth(1.5 * sc) love.graphics.circle("line", cx, cy, radius, 48) -- Forward cone (two diagonals upward from centre, spanning ±30°) local coneX = math.sin(FORWARD_CONE_HALF) * radius local coneY = math.cos(FORWARD_CONE_HALF) * radius love.graphics.setColor(p.bright[1], p.bright[2], p.bright[3], 0.35) love.graphics.setLineWidth(1 * sc) love.graphics.line(cx, cy, cx - coneX, cy - coneY) love.graphics.line(cx, cy, cx + coneX, cy - coneY) -- Compass ticks (N/E/S/W) on the rim local tick = radius * 0.12 love.graphics.setColor(p.bright[1], p.bright[2], p.bright[3], 0.5) love.graphics.line(cx, cy - radius, cx, cy - radius + tick) love.graphics.line(cx, cy + radius, cx, cy + radius - tick) love.graphics.line(cx - radius, cy, cx - radius + tick, cy) love.graphics.line(cx + radius, cy, cx + radius - tick, cy) -- Sweep line (bearing 0 = straight up) local sx = cx + math.sin(sweepAngle) * radius local sy = cy - math.cos(sweepAngle) * radius love.graphics.setColor(p.bright[1], p.bright[2], p.bright[3], 0.8) love.graphics.setLineWidth(1.5 * sc) love.graphics.line(cx, cy, sx, sy) -- Fading blips for _, b in pairs(blips) do local bx = cx + b.rx * radius local by = cy - b.ry * radius love.graphics.setColor(p.enemy[1], p.enemy[2], p.enemy[3], math.min(1, b.alpha)) love.graphics.circle("fill", bx, by, 3 * sc) end end return Radar