Unify editor input with room preview transform
This commit is contained in:
parent
3c5e6daf06
commit
8fae34b803
5 changed files with 82 additions and 29 deletions
|
|
@ -58,21 +58,14 @@ local boxPaletteSections = {}
|
||||||
-- erase that tile more than once.
|
-- erase that tile more than once.
|
||||||
local function roomCellAt(x, y)
|
local function roomCellAt(x, y)
|
||||||
if x < 0 or x >= width or y < 0 or y >= height then return nil end
|
if x < 0 or x >= width or y < 0 or y >= height then return nil end
|
||||||
local cell = currentRoom:getCellFromMousePos(x, y)
|
return currentRoom:getCellFromMousePos(x, y)
|
||||||
cell.x = constrain(cell.x, 1, gridWidth)
|
|
||||||
cell.y = constrain(cell.y, 1, gridHeight)
|
|
||||||
return cell
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function paintRoomCell(cell, button)
|
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
|
if button == 1 and selectedAsset then
|
||||||
currentRoom:attemptPlace(selectedAsset, gameAssets[selectedAsset], x, y, selectedColor)
|
currentRoom:attemptPlaceAtCell(selectedAsset, gameAssets[selectedAsset], cell, selectedColor)
|
||||||
elseif button == 2 then
|
elseif button == 2 then
|
||||||
currentRoom:attemptDelete(x, y, selectedColor)
|
currentRoom:attemptDeleteAtCell(cell, selectedColor)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
5
main.lua
5
main.lua
|
|
@ -129,7 +129,10 @@ function love.draw()
|
||||||
-- Blitting the canvas 1:1 afterwards means no rescaling of the final image
|
-- Blitting the canvas 1:1 afterwards means no rescaling of the final image
|
||||||
-- (crisp, nearest-neighbor throughout — nothing is averaged).
|
-- (crisp, nearest-neighbor throughout — nothing is averaged).
|
||||||
love.graphics.setCanvas(sideBarCanvas)
|
love.graphics.setCanvas(sideBarCanvas)
|
||||||
love.graphics.clear()
|
-- The gameplay view now intentionally extends beyond its old room bounds.
|
||||||
|
-- Keep the editor panel opaque so that neighbourhood preview never shows
|
||||||
|
-- through empty areas of the UI.
|
||||||
|
love.graphics.clear(0, 0, 0, 1)
|
||||||
love.graphics.push()
|
love.graphics.push()
|
||||||
love.graphics.scale(editorRenderScale, editorRenderScale)
|
love.graphics.scale(editorRenderScale, editorRenderScale)
|
||||||
editorDraw()
|
editorDraw()
|
||||||
|
|
|
||||||
28
room.lua
28
room.lua
|
|
@ -841,14 +841,18 @@ function Room:registerNpc(entity)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Room:getCellFromMousePos(mouseX, mouseY)
|
function Room:getCellFromMousePos(mouseX, mouseY)
|
||||||
local cell = {}
|
if gameWorld and gameWorld.levels and gameWorld.levels[self.name] == self and
|
||||||
cell.x = math.ceil(mouseX * gridWidth / width)
|
not gameWorld.drawingRoomNeighborhood then
|
||||||
cell.y = math.ceil(mouseY * gridHeight / height)
|
return gameWorld:screenCellInRoom(self, mouseX, mouseY)
|
||||||
return cell
|
end
|
||||||
|
return {
|
||||||
|
x = math.floor(mouseX * gridWidth / width) + 1,
|
||||||
|
y = math.floor(mouseY * gridHeight / height) + 1,
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function Room:attemptDelete(mouseX, mouseY, color)
|
function Room:attemptDeleteAtCell(cell, color)
|
||||||
local cell = self:getCellFromMousePos(mouseX, mouseY)
|
if not cell then return end
|
||||||
|
|
||||||
-- "white" (or nil) deletes every channel; otherwise just the picked one
|
-- "white" (or nil) deletes every channel; otherwise just the picked one
|
||||||
local colors
|
local colors
|
||||||
|
|
@ -915,9 +919,13 @@ function Room:attemptDelete(mouseX, mouseY, color)
|
||||||
self:tick()
|
self:tick()
|
||||||
end
|
end
|
||||||
|
|
||||||
function Room:attemptPlace(name, props, mouseX, mouseY, color)
|
function Room:attemptDelete(mouseX, mouseY, color)
|
||||||
|
return self:attemptDeleteAtCell(self:getCellFromMousePos(mouseX, mouseY), color)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Room:attemptPlaceAtCell(name, props, cell, color)
|
||||||
if not props or not props.sprites then return end
|
if not props or not props.sprites then return end
|
||||||
local cell = self:getCellFromMousePos(mouseX, mouseY)
|
if not cell then return end
|
||||||
-- Jorge is one dialogable NPC with three display channels, not three
|
-- Jorge is one dialogable NPC with three display channels, not three
|
||||||
-- independently placeable color-layer entities.
|
-- independently placeable color-layer entities.
|
||||||
if name == "jorge" then
|
if name == "jorge" then
|
||||||
|
|
@ -944,6 +952,10 @@ function Room:attemptPlace(name, props, mouseX, mouseY, color)
|
||||||
self:tick()
|
self:tick()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Room:attemptPlace(name, props, mouseX, mouseY, color)
|
||||||
|
return self:attemptPlaceAtCell(name, props, self:getCellFromMousePos(mouseX, mouseY), color)
|
||||||
|
end
|
||||||
|
|
||||||
function Room:load(name, preferRuntime)
|
function Room:load(name, preferRuntime)
|
||||||
-- Editor/startup loads must use the project source level. Runtime saves are
|
-- Editor/startup loads must use the project source level. Runtime saves are
|
||||||
-- only consulted for an in-progress room-to-room game transition, otherwise
|
-- only consulted for an in-progress room-to-room game transition, otherwise
|
||||||
|
|
|
||||||
|
|
@ -391,6 +391,21 @@ test("WASD room browsing changes view without moving players", function()
|
||||||
currentRoom, setEditorRoomName = oldCurrentRoom, oldSetEditorRoomName
|
currentRoom, setEditorRoomName = oldCurrentRoom, oldSetEditorRoomName
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
test("screen-to-cell mapping uses the neighbourhood render transform", function()
|
||||||
|
local oldWidth, oldHeight, oldGridWidth, oldGridHeight = width, height, gridWidth, gridHeight
|
||||||
|
width, height, gridWidth, gridHeight = 110, 110, 11, 11
|
||||||
|
local room = {name = "a"}
|
||||||
|
local world = setmetatable({}, {__index = World})
|
||||||
|
world.roomNeighborhoodTransform = function()
|
||||||
|
return {zoom = 0.78, originX = 22, originY = 22, cameraX = 0, cameraY = 0}
|
||||||
|
end
|
||||||
|
local cell = world:screenCellInRoom(room,
|
||||||
|
22 + width * 0.78 * (4.5 / 11), 22 + height * 0.78 * (7.5 / 11))
|
||||||
|
assertTrue(cell.x == 5 and cell.y == 8, "rendered cell did not map back to itself")
|
||||||
|
assertTrue(world:screenCellInRoom(room, 1, 1) == nil, "preview margin mapped into active room")
|
||||||
|
width, height, gridWidth, gridHeight = oldWidth, oldHeight, oldGridWidth, oldGridHeight
|
||||||
|
end)
|
||||||
|
|
||||||
test("one run cannot terminate at two different switches", function()
|
test("one run cannot terminate at two different switches", function()
|
||||||
local second = switch("red", 1, 2)
|
local second = switch("red", 1, 2)
|
||||||
local world, _, roomB = makeWorld(second)
|
local world, _, roomB = makeWorld(second)
|
||||||
|
|
|
||||||
50
world.lua
50
world.lua
|
|
@ -908,13 +908,12 @@ function World:drawOverlaps(room, color)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Render the active room at a small zoom-out, together with the eight world
|
-- The neighbourhood renderer and editor pointer mapping share this exact
|
||||||
-- neighbours around it. This is display-only: individual rooms still own
|
-- transform. Keeping it in one place prevents a visual zoom adjustment from
|
||||||
-- their data, collision, and authored boundary walls.
|
-- silently shifting placement/deletion by a tile.
|
||||||
function World:drawRoomNeighborhood(room)
|
function World:roomNeighborhoodTransform(room)
|
||||||
if self.drawingRoomNeighborhood then return end
|
|
||||||
local location = self:roomLocation(room.name)
|
local location = self:roomLocation(room.name)
|
||||||
if not location then return end
|
if not location then return nil end
|
||||||
|
|
||||||
-- Leave enough margin to read several cells of each neighbour, not merely
|
-- Leave enough margin to read several cells of each neighbour, not merely
|
||||||
-- its boundary wall. This keeps a nearby separated player discoverable.
|
-- its boundary wall. This keeps a nearby separated player discoverable.
|
||||||
|
|
@ -932,16 +931,47 @@ function World:drawRoomNeighborhood(room)
|
||||||
cameraY = transition.dy * (1 - eased)
|
cameraY = transition.dy * (1 - eased)
|
||||||
if progress >= 1 then self.roomTransition = nil end
|
if progress >= 1 then self.roomTransition = nil end
|
||||||
end
|
end
|
||||||
|
return {location = location, zoom = zoom, originX = originX, originY = originY,
|
||||||
|
cameraX = cameraX, cameraY = cameraY}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Convert a gameplay-screen coordinate to a cell in the centered room. The
|
||||||
|
-- surrounding rooms are preview-only while editing; browse to one with WASD
|
||||||
|
-- before changing it. Returns nil for the preview margin or any other room.
|
||||||
|
function World:screenCellInRoom(room, mouseX, mouseY)
|
||||||
|
local transform = self:roomNeighborhoodTransform(room)
|
||||||
|
if not transform then return nil end
|
||||||
|
local roomOffsetX = (mouseX - transform.originX) / (width * transform.zoom) - transform.cameraX
|
||||||
|
local roomOffsetY = (mouseY - transform.originY) / (height * transform.zoom) - transform.cameraY
|
||||||
|
local offsetX, offsetY = math.floor(roomOffsetX), math.floor(roomOffsetY)
|
||||||
|
if offsetX ~= 0 or offsetY ~= 0 then return nil end
|
||||||
|
local localX = (roomOffsetX - offsetX) * width
|
||||||
|
local localY = (roomOffsetY - offsetY) * height
|
||||||
|
local cell = {
|
||||||
|
x = math.floor(localX * gridWidth / width) + 1,
|
||||||
|
y = math.floor(localY * gridHeight / height) + 1,
|
||||||
|
}
|
||||||
|
if cell.x < 1 or cell.x > gridWidth or cell.y < 1 or cell.y > gridHeight then return nil end
|
||||||
|
return cell
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Render the active room at a small zoom-out, together with the eight world
|
||||||
|
-- neighbours around it. This is display-only: individual rooms still own
|
||||||
|
-- their data, collision, and authored boundary walls.
|
||||||
|
function World:drawRoomNeighborhood(room)
|
||||||
|
if self.drawingRoomNeighborhood then return end
|
||||||
|
local transform = self:roomNeighborhoodTransform(room)
|
||||||
|
if not transform then return end
|
||||||
self.drawingRoomNeighborhood = true
|
self.drawingRoomNeighborhood = true
|
||||||
for roomY = -1, 1 do
|
for roomY = -1, 1 do
|
||||||
for roomX = -1, 1 do
|
for roomX = -1, 1 do
|
||||||
local name = self:roomAt(location.x + roomX, location.y + roomY)
|
local name = self:roomAt(transform.location.x + roomX, transform.location.y + roomY)
|
||||||
local adjacent = name and self.levels[name]
|
local adjacent = name and self.levels[name]
|
||||||
if adjacent then
|
if adjacent then
|
||||||
love.graphics.push()
|
love.graphics.push()
|
||||||
love.graphics.translate(originX + (roomX + cameraX) * width * zoom,
|
love.graphics.translate(transform.originX + (roomX + transform.cameraX) * width * transform.zoom,
|
||||||
originY + (roomY + cameraY) * height * zoom)
|
transform.originY + (roomY + transform.cameraY) * height * transform.zoom)
|
||||||
love.graphics.scale(zoom, zoom)
|
love.graphics.scale(transform.zoom, transform.zoom)
|
||||||
adjacent:draw()
|
adjacent:draw()
|
||||||
love.graphics.pop()
|
love.graphics.pop()
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue