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()

View file

@ -190,12 +190,17 @@ function love.keypressed(key)
end
function love.mousepressed(x, y, button)
if editorView == "room" and button == 2 then
currentRoom:attemptDelete(x, y, selectedColor)
end
editorMouseHandler(x, y, button)
end
function love.mousemoved(x, y)
editorMouseMoved(x, y)
end
function love.mousereleased(x, y, button)
editorMouseReleased(x, y, button)
end
function love.textinput(t)
response = response .. t
editorTextHandler(response)

760
rooms/ok.sav Normal file
View file

@ -0,0 +1,760 @@
{
objects={
{
x=11,
y=11,
class="static_wall",
color="green"
},
{
x=11,
y=10,
class="static_wall",
color="green"
},
{
x=11,
y=9,
class="static_wall",
color="green"
},
{
x=11,
y=8,
class="static_wall",
color="green"
},
{
x=11,
y=7,
class="static_wall",
color="green"
},
{
x=11,
y=6,
class="static_wall",
color="green"
},
{
x=11,
y=4,
class="static_wall",
color="green"
},
{
x=11,
y=3,
class="static_wall",
color="green"
},
{
x=11,
y=2,
class="static_wall",
color="green"
},
{
x=11,
y=1,
class="static_wall",
color="green"
},
{
x=10,
y=1,
class="static_wall",
color="green"
},
{
x=9,
y=1,
class="static_wall",
color="green"
},
{
x=8,
y=1,
class="static_wall",
color="green"
},
{
x=7,
y=1,
class="static_wall",
color="green"
},
{
x=6,
y=1,
class="static_wall",
color="green"
},
{
x=5,
y=1,
class="static_wall",
color="green"
},
{
x=4,
y=1,
class="static_wall",
color="green"
},
{
x=3,
y=1,
class="static_wall",
color="green"
},
{
x=2,
y=1,
class="static_wall",
color="green"
},
{
x=1,
y=1,
class="static_wall",
color="green"
},
{
x=1,
y=2,
class="static_wall",
color="green"
},
{
x=1,
y=3,
class="static_wall",
color="green"
},
{
x=1,
y=4,
class="static_wall",
color="green"
},
{
x=1,
y=5,
class="static_wall",
color="green"
},
{
x=1,
y=6,
class="static_wall",
color="green"
},
{
x=1,
y=7,
class="static_wall",
color="green"
},
{
x=1,
y=8,
class="static_wall",
color="green"
},
{
x=1,
y=9,
class="static_wall",
color="green"
},
{
x=1,
y=10,
class="static_wall",
color="green"
},
{
x=1,
y=11,
class="static_wall",
color="green"
},
{
x=2,
y=11,
class="static_wall",
color="green"
},
{
x=3,
y=11,
class="static_wall",
color="green"
},
{
x=4,
y=11,
class="static_wall",
color="green"
},
{
x=5,
y=11,
class="static_wall",
color="green"
},
{
x=6,
y=11,
class="static_wall",
color="green"
},
{
x=7,
y=11,
class="static_wall",
color="green"
},
{
x=8,
y=11,
class="static_wall",
color="green"
},
{
x=9,
y=11,
class="static_wall",
color="green"
},
{
x=10,
y=11,
class="static_wall",
color="green"
},
{
x=11,
y=5,
class="static_wall",
color="green"
},
{
x=5,
y=7,
class="box",
color="green"
},
{
x=11,
y=11,
class="static_wall",
color="blue"
},
{
x=11,
y=10,
class="static_wall",
color="blue"
},
{
x=11,
y=9,
class="static_wall",
color="blue"
},
{
x=11,
y=8,
class="static_wall",
color="blue"
},
{
x=11,
y=7,
class="static_wall",
color="blue"
},
{
x=11,
y=6,
class="static_wall",
color="blue"
},
{
x=11,
y=4,
class="static_wall",
color="blue"
},
{
x=11,
y=3,
class="static_wall",
color="blue"
},
{
x=11,
y=2,
class="static_wall",
color="blue"
},
{
x=11,
y=1,
class="static_wall",
color="blue"
},
{
x=10,
y=1,
class="static_wall",
color="blue"
},
{
x=9,
y=1,
class="static_wall",
color="blue"
},
{
x=8,
y=1,
class="static_wall",
color="blue"
},
{
x=7,
y=1,
class="static_wall",
color="blue"
},
{
x=6,
y=1,
class="static_wall",
color="blue"
},
{
x=5,
y=1,
class="static_wall",
color="blue"
},
{
x=4,
y=1,
class="static_wall",
color="blue"
},
{
x=3,
y=1,
class="static_wall",
color="blue"
},
{
x=2,
y=1,
class="static_wall",
color="blue"
},
{
x=1,
y=1,
class="static_wall",
color="blue"
},
{
x=1,
y=2,
class="static_wall",
color="blue"
},
{
x=1,
y=3,
class="static_wall",
color="blue"
},
{
x=1,
y=4,
class="static_wall",
color="blue"
},
{
x=1,
y=5,
class="static_wall",
color="blue"
},
{
x=1,
y=6,
class="static_wall",
color="blue"
},
{
x=1,
y=7,
class="static_wall",
color="blue"
},
{
x=1,
y=8,
class="static_wall",
color="blue"
},
{
x=1,
y=9,
class="static_wall",
color="blue"
},
{
x=1,
y=10,
class="static_wall",
color="blue"
},
{
x=1,
y=11,
class="static_wall",
color="blue"
},
{
x=2,
y=11,
class="static_wall",
color="blue"
},
{
x=3,
y=11,
class="static_wall",
color="blue"
},
{
x=4,
y=11,
class="static_wall",
color="blue"
},
{
x=5,
y=11,
class="static_wall",
color="blue"
},
{
x=6,
y=11,
class="static_wall",
color="blue"
},
{
x=7,
y=11,
class="static_wall",
color="blue"
},
{
x=8,
y=11,
class="static_wall",
color="blue"
},
{
x=9,
y=11,
class="static_wall",
color="blue"
},
{
x=10,
y=11,
class="static_wall",
color="blue"
},
{
x=11,
y=5,
class="static_wall",
color="blue"
},
{
x=6,
y=6,
class="box",
color="blue"
},
{
x=11,
y=11,
class="static_wall",
color="red"
},
{
x=11,
y=10,
class="static_wall",
color="red"
},
{
x=11,
y=9,
class="static_wall",
color="red"
},
{
x=11,
y=8,
class="static_wall",
color="red"
},
{
x=11,
y=7,
class="static_wall",
color="red"
},
{
x=11,
y=6,
class="static_wall",
color="red"
},
{
x=11,
y=4,
class="static_wall",
color="red"
},
{
x=11,
y=3,
class="static_wall",
color="red"
},
{
x=11,
y=2,
class="static_wall",
color="red"
},
{
x=11,
y=1,
class="static_wall",
color="red"
},
{
x=10,
y=1,
class="static_wall",
color="red"
},
{
x=9,
y=1,
class="static_wall",
color="red"
},
{
x=8,
y=1,
class="static_wall",
color="red"
},
{
x=7,
y=1,
class="static_wall",
color="red"
},
{
x=6,
y=1,
class="static_wall",
color="red"
},
{
x=5,
y=1,
class="static_wall",
color="red"
},
{
x=4,
y=1,
class="static_wall",
color="red"
},
{
x=3,
y=1,
class="static_wall",
color="red"
},
{
x=2,
y=1,
class="static_wall",
color="red"
},
{
x=1,
y=1,
class="static_wall",
color="red"
},
{
x=1,
y=2,
class="static_wall",
color="red"
},
{
x=1,
y=3,
class="static_wall",
color="red"
},
{
x=1,
y=4,
class="static_wall",
color="red"
},
{
x=1,
y=5,
class="static_wall",
color="red"
},
{
x=1,
y=6,
class="static_wall",
color="red"
},
{
x=1,
y=7,
class="static_wall",
color="red"
},
{
x=1,
y=8,
class="static_wall",
color="red"
},
{
x=1,
y=9,
class="static_wall",
color="red"
},
{
x=1,
y=10,
class="static_wall",
color="red"
},
{
x=1,
y=11,
class="static_wall",
color="red"
},
{
x=2,
y=11,
class="static_wall",
color="red"
},
{
x=3,
y=11,
class="static_wall",
color="red"
},
{
x=4,
y=11,
class="static_wall",
color="red"
},
{
x=5,
y=11,
class="static_wall",
color="red"
},
{
x=6,
y=11,
class="static_wall",
color="red"
},
{
x=7,
y=11,
class="static_wall",
color="red"
},
{
x=8,
y=11,
class="static_wall",
color="red"
},
{
x=9,
y=11,
class="static_wall",
color="red"
},
{
x=10,
y=11,
class="static_wall",
color="red"
},
{
x=11,
y=5,
class="static_wall",
color="red"
},
{
x=5,
y=6,
class="box",
color="red"
},
{
x=6,
y=8,
class="player",
color="green"
},
{
x=6,
y=8,
class="player",
color="blue"
},
{
x=6,
y=8,
class="player",
color="red"
}
}
}

4
rooms/room_01_05.sav Normal file
View file

@ -0,0 +1,4 @@
{
objects={
}
}

4
rooms/room_03_08.sav Normal file
View file

@ -0,0 +1,4 @@
{
objects={
}
}

View file

@ -1,157 +1,172 @@
{
rooms={
{
name="start",
x=9,
y=11,
x=9
name="start"
},
{
name="3",
x=10,
y=10,
x=10
name="3"
},
{
name="5",
x=12,
y=10,
x=12
name="5"
},
{
name="village_1",
x=12,
y=9,
x=12
name="village_1"
},
{
name="village_2",
x=12,
y=8,
x=12
name="village_2"
},
{
name="village_3",
x=13,
y=8,
x=13
name="village_3"
},
{
name="village_4",
x=11,
y=8,
x=11
name="village_4"
},
{
name="yellow_1",
x=11,
y=7,
x=11
name="yellow_1"
},
{
name="yellow_3",
x=11,
y=6,
x=11
name="yellow_3"
},
{
name="yellow_2",
x=12,
y=7,
x=12
name="yellow_2"
},
{
name="yellow_4",
x=11,
y=5,
x=11
name="yellow_4"
},
{
name="yellow_5",
x=11,
y=4,
x=11
name="yellow_5"
},
{
name="yellow_6",
x=10,
y=5,
x=10
name="yellow_6"
},
{
name="default",
x=1,
y=1,
x=1
name="default"
},
{
name="old_yellow_3",
x=3,
y=1,
x=3
name="old_yellow_3"
},
{
name="test_patt",
x=5,
y=1,
x=5
name="test_patt"
},
{
name="test_patt_update",
x=7,
y=1,
x=7
name="test_patt_update"
},
{
name="white_asymmetrical",
x=9,
y=1,
x=9
name="white_asymmetrical"
},
{
name="white_ok",
x=11,
y=1,
x=11
name="white_ok"
},
{
name="white_primary",
x=13,
y=1,
x=13
name="white_primary"
},
{
name="yellow_arches",
x=15,
y=1,
x=15
name="yellow_arches"
},
{
name="yellow_asymmetrical",
x=17,
y=1,
x=17
name="yellow_asymmetrical"
},
{
name="yellow_cc_moustache",
x=1,
y=3,
x=1
name="yellow_cc_moustache"
},
{
name="yellow_color_changing_levvel_1",
x=3,
y=3,
x=3
name="yellow_color_changing_levvel_1"
},
{
name="yellow_smile",
x=7,
y=3,
x=7
name="yellow_smile"
},
{
name="2",
x=9,
y=10,
x=9
name="2"
},
{
name="room_11_09",
x=11,
y=9,
x=11
name="room_11_09"
},
{
name="yellow_skull",
x=9,
y=3,
x=9
name="yellow_skull"
},
{
name="jorge_room",
x=5,
y=5,
x=5
name="jorge_room"
},
{
name="4",
x=11,
y=10,
x=11
name="4"
},
{
x=1,
y=5,
name="room_01_05"
},
{
x=2,
y=8,
name="ok"
},
{
x=3,
y=8,
name="room_03_08"
}
},
lastRoom="yellow_4",
lastRoom="room_03_08",
width=18,
height=18
}