singe/testScripts/scene54.singe
2026-09-14 22:32:53 -05:00

168 lines
5.4 KiB
Text

-- Forge (FORGE.md): opened on a real description, driven, saved, and the result compiled and
-- played.
--
-- Driven by calling the editor's own functions rather than by a pointer, the way the menu's
-- sanitizer scene drives the menu: what is being tested is that moving something in the editor
-- reaches the description, survives a save, and changes the game that comes out. The pointer path
-- itself was settled by the spike in FORGE.md section 1 and needs a real X server to exercise.
-- As a library: the tail of Forge.singe that launches it is skipped, because this scene
-- drives it rather than being it.
FORGE_LIBRARY = true
dofile("Forge/Forge.singe")
local font = fontLoad("Singe/FreeSansBold.ttf", 16)
local out = singeGetDataPath()
local work = out .. "edited.game"
fontSelect(font)
fontQuality(FONT_QUALITY_BLENDED)
overlaySetResolution(720, 480)
-- Work on a copy; an editor that writes back to the file it was handed is right, but a test that
-- edits the repository's own sample is not.
local source = assert(io.open("testScripts/author/platformer.game", "r"))
local copy = assert(io.open(work, "w"))
copy:write(source:read("a"))
source:close()
copy:close()
if not forgeBegin(work) then
debugPrint("FORGE FAIL could not open " .. work)
singeQuit()
end
local frames = 0
local stage = 0
local pointer = { x = 430, y = 300 }
local before = nil
-- Each stage is one thing the editor must be able to do, checked as it happens.
local function step()
if stage == 1 then
-- Pick the prize up off the ledge, the way a press on the canvas would.
local index = forgePick(520, 300)
if index == nil then
debugPrint("FORGE FAIL nothing under the prize's own position")
else
before = forgeRoom().entities[index].x
debugPrint("FORGE picked " .. forgeRoom().entities[index].id .. " at x=" .. before)
forgePress(520, 300)
end
elseif stage == 2 then
-- Drag it left and down, off the ledge and onto the ground.
forgeDrag(300, 300)
pointer.x = 300
elseif stage == 3 then
forgeRelease()
local moved = forgeRoom().entities[forgePick(300, 300)]
debugPrint("FORGE moved to x=" .. moved.x .. " dirty=" .. tostring(FORGE.dirty))
elseif stage == 4 then
forgeSave()
-- Reload from disc and check the move is in the file, not just in memory.
local reloaded = authorLoad(work)
local found = nil
for _, e in ipairs(reloaded.rooms[1].entities) do
if e.id == "prize" then
found = e.x
end
end
debugPrint("FORGE saved and reloaded, prize x=" .. tostring(found) .. " (was " .. tostring(before) .. ")")
if found == before then
debugPrint("FORGE FAIL the move did not survive the save")
end
elseif stage == 5 then
-- And the edit has to reach the game, not just the file.
local built = forgeBuild(out .. "edited.singe")
local text = assert(io.open(built, "r")):read("a")
if text:find('id = "prize", type = "prize", x = 300') then
debugPrint("FORGE the compiled game carries the new position")
else
debugPrint("FORGE FAIL the compiled game does not carry the new position")
end
elseif stage == 6 then
-- The invariant the whole editor rests on: a description that has been through load and
-- save has to compile to the same game as the one that has not. Without it, opening a
-- game in the editor and saving it unchanged could quietly alter it.
local original = authorLoad("testScripts/author/platformer.game")
local trip = out .. "trip.game"
authorSave(original, trip)
local again = authorLoad(trip)
if authorCompile(original) == authorCompile(again) then
debugPrint("FORGE round trip identical")
else
debugPrint("FORGE FAIL round trip changed the game")
end
elseif stage == 7 then
-- The hero lives at x=120, underneath the chrome. A press there must not reach it.
forgeSelect(nil)
forgePress(120, 380)
if FORGE.selected == nil then
debugPrint("FORGE the panel correctly swallows a press beneath it")
else
debugPrint("FORGE FAIL a press under the panel reached the canvas")
end
elseif stage == 8 then
-- Slide the chrome out of the way by pressing its tab, through forgePress, which is
-- the path a real pointer takes. Grabbing the panel through a GUI handler would not be:
-- the spike found a document can go without pointer input entirely, so the tab is drawn on
-- the canvas and this test goes through the same door a player's mouse does.
local tx, ty, tw, th = forgeTab()
forgePress(tx + tw / 2, ty + th / 2)
if FORGE.panelDrag == nil then
debugPrint("FORGE FAIL pressing the tab did not start a panel drag")
end
forgeDrag(440 + tw / 2, 200)
forgeRelease()
pointer.x = 440
debugPrint("FORGE panel moved to x=" .. FORGE.panelX)
elseif stage == 9 then
-- And now the same press reaches the hero, which is the whole point of a movable panel.
forgePress(120, 380)
if FORGE.selected ~= nil and forgeRoom().entities[FORGE.selected].id == "hero" then
debugPrint("FORGE the hero is reachable once the panel has moved")
else
debugPrint("FORGE FAIL the hero is still unreachable")
end
forgeRelease()
debugPrint("FORGE RESULT done")
end
end
function onOverlayUpdate()
frames = frames + 1
if frames % 12 == 0 and stage < 10 then
stage = stage + 1
step()
end
forgeDraw(pointer.x, pointer.y)
colorForeground(255, 255, 255, 255)
fontPrint(200, 455, "Forge, stage " .. stage)
if frames == 10 or frames == 54 or frames == 88 or frames == 118 then
singeScreenshot()
end
if frames > 130 then
singeQuit()
end
return OVERLAY_UPDATED
end