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.
|
||||
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
|
||||
return currentRoom:getCellFromMousePos(x, y)
|
||||
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)
|
||||
currentRoom:attemptPlaceAtCell(selectedAsset, gameAssets[selectedAsset], cell, selectedColor)
|
||||
elseif button == 2 then
|
||||
currentRoom:attemptDelete(x, y, selectedColor)
|
||||
currentRoom:attemptDeleteAtCell(cell, selectedColor)
|
||||
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
|
||||
-- (crisp, nearest-neighbor throughout — nothing is averaged).
|
||||
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.scale(editorRenderScale, editorRenderScale)
|
||||
editorDraw()
|
||||
|
|
|
|||
28
room.lua
28
room.lua
|
|
@ -841,14 +841,18 @@ function Room:registerNpc(entity)
|
|||
end
|
||||
|
||||
function Room:getCellFromMousePos(mouseX, mouseY)
|
||||
local cell = {}
|
||||
cell.x = math.ceil(mouseX * gridWidth / width)
|
||||
cell.y = math.ceil(mouseY * gridHeight / height)
|
||||
return cell
|
||||
if gameWorld and gameWorld.levels and gameWorld.levels[self.name] == self and
|
||||
not gameWorld.drawingRoomNeighborhood then
|
||||
return gameWorld:screenCellInRoom(self, mouseX, mouseY)
|
||||
end
|
||||
return {
|
||||
x = math.floor(mouseX * gridWidth / width) + 1,
|
||||
y = math.floor(mouseY * gridHeight / height) + 1,
|
||||
}
|
||||
end
|
||||
|
||||
function Room:attemptDelete(mouseX, mouseY, color)
|
||||
local cell = self:getCellFromMousePos(mouseX, mouseY)
|
||||
function Room:attemptDeleteAtCell(cell, color)
|
||||
if not cell then return end
|
||||
|
||||
-- "white" (or nil) deletes every channel; otherwise just the picked one
|
||||
local colors
|
||||
|
|
@ -915,9 +919,13 @@ function Room:attemptDelete(mouseX, mouseY, color)
|
|||
self:tick()
|
||||
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
|
||||
local cell = self:getCellFromMousePos(mouseX, mouseY)
|
||||
if not cell then return end
|
||||
-- Jorge is one dialogable NPC with three display channels, not three
|
||||
-- independently placeable color-layer entities.
|
||||
if name == "jorge" then
|
||||
|
|
@ -944,6 +952,10 @@ function Room:attemptPlace(name, props, mouseX, mouseY, color)
|
|||
self:tick()
|
||||
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)
|
||||
-- Editor/startup loads must use the project source level. Runtime saves are
|
||||
-- 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
|
||||
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()
|
||||
local second = switch("red", 1, 2)
|
||||
local world, _, roomB = makeWorld(second)
|
||||
|
|
|
|||
50
world.lua
50
world.lua
|
|
@ -908,13 +908,12 @@ function World:drawOverlaps(room, color)
|
|||
end
|
||||
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
|
||||
-- The neighbourhood renderer and editor pointer mapping share this exact
|
||||
-- transform. Keeping it in one place prevents a visual zoom adjustment from
|
||||
-- silently shifting placement/deletion by a tile.
|
||||
function World:roomNeighborhoodTransform(room)
|
||||
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
|
||||
-- its boundary wall. This keeps a nearby separated player discoverable.
|
||||
|
|
@ -932,16 +931,47 @@ function World:drawRoomNeighborhood(room)
|
|||
cameraY = transition.dy * (1 - eased)
|
||||
if progress >= 1 then self.roomTransition = nil 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
|
||||
for roomY = -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]
|
||||
if adjacent then
|
||||
love.graphics.push()
|
||||
love.graphics.translate(originX + (roomX + cameraX) * width * zoom,
|
||||
originY + (roomY + cameraY) * height * zoom)
|
||||
love.graphics.scale(zoom, zoom)
|
||||
love.graphics.translate(transform.originX + (roomX + transform.cameraX) * width * transform.zoom,
|
||||
transform.originY + (roomY + transform.cameraY) * height * transform.zoom)
|
||||
love.graphics.scale(transform.zoom, transform.zoom)
|
||||
adjacent:draw()
|
||||
love.graphics.pop()
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue