diff --git a/game_ui.lua b/game_ui.lua new file mode 100644 index 0000000..7242112 --- /dev/null +++ b/game_ui.lua @@ -0,0 +1,88 @@ +-- 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 + +function GameUI.drawColorVenn(room) + if not room or not gameUiBox then return end + GameUI.drawBacking() + + local cellSize = width / gridWidth + local sidebarWidth = cellSize * 2 + if gameUiBox.w < sidebarWidth or not uiVennCanvas then return end + + love.graphics.setCanvas(uiVennCanvas) + love.graphics.clear(0, 0, 0, 0) + love.graphics.setBlendMode("screen") + local radius = 9 + local dotRadius = 1 + local active = {} + for _, color in ipairs(room:getActiveColors()) do active[color] = true end + + for _, color in ipairs({"red", "green", "blue"}) do + if room.player[color] then + local layout = vennLayout[color] + local x, y = layout.x, layout.y + local rgb = channelColor[color] + if active[color] then + love.graphics.setColor(rgb[1], rgb[2], rgb[3], 1) + love.graphics.circle("fill", x, y, radius) + else + drawDottedCircle(x, y, radius, rgb, dotRadius) + end + end + end + + love.graphics.setCanvas(gameCanvas) + GameUI.drawVennOverlay() +end + +return GameUI diff --git a/level_editor/editor.lua b/level_editor/editor.lua index b55c092..7c957cf 100644 --- a/level_editor/editor.lua +++ b/level_editor/editor.lua @@ -1138,10 +1138,10 @@ local function openWorldRoom(cellX, cellY) end function editorMouseHandler(x, y, button) - if x > width then + if x >= sideBarBox.x and x < sideBarBox.x + sideBarBox.w then -- map from real window coords into the sidebar's fixed virtual space -- (same transform used to scale the sidebar canvas up in love.draw) - x = (x - width) * sb.w / sideBarBox.w + x = (x - sideBarBox.x) * sb.w / sideBarBox.w y = y * sb.h / sideBarBox.h if editorView == "room" then diff --git a/main.lua b/main.lua index c8877ba..f2c8ec2 100644 --- a/main.lua +++ b/main.lua @@ -15,6 +15,7 @@ require "room" require "world" require "world_seed" require "shaders" +GameUI = require "game_ui" require "level_editor/editor" -- Ink dialogue: ship the pre-parsed book (LÖVE has no lpeg to parse .ink at @@ -110,6 +111,7 @@ function love.draw() else currentRoom:draw() end + GameUI.drawColorVenn(currentRoom) love.graphics.setCanvas(finalCanvas) love.graphics.clear() @@ -158,6 +160,10 @@ function love.draw() love.graphics.setBlendMode("alpha") love.graphics.setColor(1, 1, 1, 1) love.graphics.draw(finalCanvas, 0, 0) + GameUI.drawBacking() + GameUI.drawVennOverlay() + love.graphics.setBlendMode("alpha") + love.graphics.setColor(1, 1, 1, 1) if DEBUG then love.graphics.draw(sideBarCanvas, sideBarBox.x, sideBarBox.y) end love.graphics.print("Current FPS: "..tostring(love.timer.getFPS( )), 10, 10) PerfProfiler:draw() @@ -265,7 +271,7 @@ end function love.wheelmoved(dx, dy) if not DEBUG then return end -- Scroll the editor's asset/folder list when the cursor is over the sidebar. - if editorView == "room" and love.mouse.getX() > width then + if editorView == "room" and love.mouse.getX() >= sideBarBox.x then editorScrollRoomList(dy) end end @@ -304,7 +310,10 @@ function _computeDimensions() height = width end - sideBarBox = {x = width, y = 0, w = love.graphics.getWidth() - width, h = height} + local availableWidth = love.graphics.getWidth() - width + local hudWidth = math.min(availableWidth, width / (gridWidth or 11) * 2) + gameUiBox = {x = width, y = 0, w = hudWidth, h = height} + sideBarBox = {x = gameUiBox.x + gameUiBox.w, y = 0, w = availableWidth - gameUiBox.w, h = height} -- The editor sidebar is authored in a fixed virtual space and scaled up to -- fill the real panel, so every element keeps the same size relative to the @@ -333,6 +342,7 @@ function _initCanvases() porousRoomCanvas = love.graphics.newCanvas() sideBarCanvas = love.graphics.newCanvas(math.max(1, sideBarBox.w), math.max(1, sideBarBox.h)) sideBarCanvas:setFilter("nearest", "nearest") -- never average when sampled + GameUI.init() end function _initGridVars()