2026-08-12 14:52:06 -04:00
|
|
|
-- Player-facing HUD drawn into gameCanvas. Keeping it here (rather than in
|
|
|
|
|
-- the editor sidebar) means its RGB circles use the same screen blend as the
|
|
|
|
|
-- room, so their overlaps are the real additive colour combinations.
|
|
|
|
|
local GameUI = {}
|
|
|
|
|
|
|
|
|
|
local channelColor = {
|
|
|
|
|
red = {1, 0, 0},
|
|
|
|
|
green = {0, 1, 0},
|
|
|
|
|
blue = {0, 0, 1},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
local vennSize = 32
|
|
|
|
|
local vennLayout = {
|
|
|
|
|
red = {x = 12, y = 10},
|
|
|
|
|
green = {x = 20, y = 10},
|
|
|
|
|
blue = {x = 16, y = 18},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
local function drawDottedCircle(x, y, radius, color, dotRadius)
|
|
|
|
|
local dots = math.max(14, math.floor((2 * math.pi * radius) / (dotRadius * 4)))
|
|
|
|
|
love.graphics.setColor(color[1], color[2], color[3], 1)
|
|
|
|
|
for index = 0, dots - 1 do
|
|
|
|
|
local angle = index / dots * math.pi * 2
|
|
|
|
|
love.graphics.circle("fill", x + math.cos(angle) * radius, y + math.sin(angle) * radius, dotRadius)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function GameUI.init()
|
|
|
|
|
uiVennCanvas = love.graphics.newCanvas(vennSize, vennSize)
|
|
|
|
|
uiVennCanvas:setFilter("nearest", "nearest")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function GameUI.drawBacking()
|
|
|
|
|
if not gameUiBox then return end
|
|
|
|
|
love.graphics.setBlendMode("alpha")
|
|
|
|
|
love.graphics.setColor(0, 0, 0, 1)
|
|
|
|
|
love.graphics.rectangle("fill", gameUiBox.x, gameUiBox.y, gameUiBox.w, gameUiBox.h)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function GameUI.drawVennOverlay()
|
|
|
|
|
if not gameUiBox or not uiVennCanvas then return end
|
|
|
|
|
local cellSize = width / gridWidth
|
|
|
|
|
local sidebarWidth = cellSize * 2
|
|
|
|
|
if gameUiBox.w < sidebarWidth then return end
|
|
|
|
|
|
|
|
|
|
love.graphics.setBlendMode("screen")
|
|
|
|
|
love.graphics.setColor(1, 1, 1, 1)
|
|
|
|
|
local scale = cellSize / 22
|
|
|
|
|
local vennWidth = vennSize * scale
|
|
|
|
|
local left = gameUiBox.x + (gameUiBox.w - vennWidth) / 2
|
|
|
|
|
love.graphics.draw(uiVennCanvas, left, cellSize * 1.1, 0, scale, scale)
|
|
|
|
|
end
|
|
|
|
|
|
2026-08-21 10:57:32 -04:00
|
|
|
-- Render the venn from explicit colour sets: `has` are the channels the player
|
|
|
|
|
-- possesses (drawn as a circle), `active` the subset shown filled (the rest
|
|
|
|
|
-- dotted). Restores whatever canvas was bound, so callers drawing straight to
|
|
|
|
|
-- the screen (e.g. the map overlay) work as well as the in-game HUD pass.
|
|
|
|
|
function GameUI.drawColorVennChannels(has, active)
|
|
|
|
|
if not gameUiBox then return end
|
2026-08-12 14:52:06 -04:00
|
|
|
GameUI.drawBacking()
|
|
|
|
|
|
|
|
|
|
local cellSize = width / gridWidth
|
|
|
|
|
local sidebarWidth = cellSize * 2
|
|
|
|
|
if gameUiBox.w < sidebarWidth or not uiVennCanvas then return end
|
|
|
|
|
|
2026-08-21 10:57:32 -04:00
|
|
|
local previousCanvas = love.graphics.getCanvas()
|
2026-08-12 14:52:06 -04:00
|
|
|
love.graphics.setCanvas(uiVennCanvas)
|
|
|
|
|
love.graphics.clear(0, 0, 0, 0)
|
|
|
|
|
love.graphics.setBlendMode("screen")
|
|
|
|
|
local radius = 9
|
|
|
|
|
local dotRadius = 1
|
|
|
|
|
|
|
|
|
|
for _, color in ipairs({"red", "green", "blue"}) do
|
2026-08-21 10:57:32 -04:00
|
|
|
if has[color] then
|
2026-08-12 14:52:06 -04:00
|
|
|
local layout = vennLayout[color]
|
|
|
|
|
local rgb = channelColor[color]
|
|
|
|
|
if active[color] then
|
|
|
|
|
love.graphics.setColor(rgb[1], rgb[2], rgb[3], 1)
|
2026-08-21 10:57:32 -04:00
|
|
|
love.graphics.circle("fill", layout.x, layout.y, radius)
|
2026-08-12 14:52:06 -04:00
|
|
|
else
|
2026-08-21 10:57:32 -04:00
|
|
|
drawDottedCircle(layout.x, layout.y, radius, rgb, dotRadius)
|
2026-08-12 14:52:06 -04:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2026-08-21 10:57:32 -04:00
|
|
|
love.graphics.setCanvas(previousCanvas)
|
2026-08-12 14:52:06 -04:00
|
|
|
GameUI.drawVennOverlay()
|
|
|
|
|
end
|
|
|
|
|
|
2026-08-21 10:57:32 -04:00
|
|
|
function GameUI.drawColorVenn(room)
|
|
|
|
|
if not room or not gameUiBox then return end
|
|
|
|
|
local has, active = {}, {}
|
|
|
|
|
for _, color in ipairs({"red", "green", "blue"}) do
|
|
|
|
|
if room.player[color] then has[color] = true end
|
|
|
|
|
end
|
|
|
|
|
for _, color in ipairs(room:getActiveColors()) do active[color] = true end
|
|
|
|
|
GameUI.drawColorVennChannels(has, active)
|
|
|
|
|
end
|
|
|
|
|
|
2026-08-12 14:52:06 -04:00
|
|
|
return GameUI
|