Add drag painting to level editor

This commit is contained in:
Your Name 2026-08-11 14:11:57 -04:00
parent fa0bada313
commit c3f5aafd77
6 changed files with 934 additions and 66 deletions

View file

@ -44,6 +44,57 @@ local assetSearchText = ""
local assetSearchButtons = {}
local worldSearchText = ""
local worldSearchBox = { x = 10, y = 34, w = 0, h = 28 }
local roomBrush = nil
-- Convert a pointer position into a valid room cell. Keeping brush state in
-- cell coordinates means a click that jitters within a tile cannot place or
-- erase that tile more than once.
local function roomCellAt(x, y)
if x < 0 or x >= width or y < 0 or y >= height then return nil end
local cell = currentRoom:getCellFromMousePos(x, y)
cell.x = constrain(cell.x, 1, gridWidth)
cell.y = constrain(cell.y, 1, gridHeight)
return cell
end
local function paintRoomCell(cell, button)
-- Use the centre of the cell so Room's existing pointer-based edit helpers
-- perform the same operation as a normal click.
local x = (cell.x - .5) * width / gridWidth
local y = (cell.y - .5) * height / gridHeight
if button == 1 and selectedAsset then
currentRoom:attemptPlace(selectedAsset, gameAssets[selectedAsset], x, y, selectedColor)
elseif button == 2 then
currentRoom:attemptDelete(x, y, selectedColor)
end
end
local function paintRoomLine(from, to, button)
-- Bresenham keeps a fast mouse movement from leaving holes between the
-- cells that happened to receive mouse-move events.
local x, y = from.x, from.y
local dx, dy = math.abs(to.x - x), math.abs(to.y - y)
local stepX = x < to.x and 1 or -1
local stepY = y < to.y and 1 or -1
local err = dx - dy
while true do
if x ~= from.x or y ~= from.y then paintRoomCell({ x = x, y = y }, button) end
if x == to.x and y == to.y then break end
local twiceErr = err * 2
if twiceErr > -dy then err = err - dy; x = x + stepX end
if twiceErr < dx then err = err + dx; y = y + stepY end
end
end
local function beginRoomBrush(x, y, button)
if button ~= 1 and button ~= 2 then return end
if button == 1 and not selectedAsset then return end
local cell = roomCellAt(x, y)
if not cell then return end
paintRoomCell(cell, button)
roomBrush = { button = button, lastCell = cell }
end
-- ── Room-view sidebar layout ────────────────────────────────────────────────
-- Declared up here (not next to editorDraw) so editorMouseHandler, defined
@ -730,8 +781,8 @@ function editorMouseHandler(x, y, button)
end
else
-- click landed on the grid (game area): place the selected asset
if editorView == "room" and button == 1 and selectedAsset then
currentRoom:attemptPlace(selectedAsset, gameAssets[selectedAsset], x, y, selectedColor)
if editorView == "room" then
beginRoomBrush(x, y, button)
elseif editorView == "world" then
local cellX, cellY = gameWorld:getCellFromMousePos(x, y)
if not gameWorld:isInBounds(cellX, cellY) then return end
@ -748,6 +799,30 @@ function editorMouseHandler(x, y, button)
end
end
function editorMouseMoved(x, y)
if not roomBrush then return end
if editorView ~= "room" then
roomBrush = nil
return
end
local cell = roomCellAt(x, y)
if not cell then
-- Leaving the grid ends this stroke; re-entering should not draw a line
-- across the sidebar or outside of the level.
roomBrush = nil
return
end
if cell.x == roomBrush.lastCell.x and cell.y == roomBrush.lastCell.y then return end
paintRoomLine(roomBrush.lastCell, cell, roomBrush.button)
roomBrush.lastCell = cell
end
function editorMouseReleased(_, _, button)
if roomBrush and roomBrush.button == button then roomBrush = nil end
end
function editorTextHandler(text)
if editorView == "asset" and activeField then
inputText = text
@ -809,6 +884,11 @@ function editorDraw()
item.x, item.y = s.list.pos(i)
item.w = s.listRect.w
item:draw()
if item.name == "asset" and item.text == selectedAsset then
love.graphics.setColor(1, 0.85, 0.2, 1)
love.graphics.setLineWidth(3)
love.graphics.rectangle("line", item.x + 1, item.y + 1, item.w - 2, item.h - 2)
end
end
end
Layout.unclip()