singe/testScripts/scene52.singe
2026-09-13 23:48:15 -05:00

95 lines
3.6 KiB
Text

-- Authoring tools (PLAN section 58): compiles both sample descriptions and plays the platformer.
--
-- The two descriptions share nothing -- one is physics with a character, the other is disc frames
-- and time windows -- so compiling both here is the test that the core has no genre baked into it.
-- Nothing in this scene knows what a platformer is; it drives keys and looks at the result.
dofile("Forge/AuthorCompile.singe")
local font = fontLoad("Singe/FreeSansBold.ttf", 18)
local built = {}
local out = singeGetDataPath()
fontSelect(font)
fontQuality(FONT_QUALITY_BLENDED)
overlaySetResolution(720, 480)
built.platformer = authorBuild("testScripts/author/platformer.game", out .. "platformer.singe")
built.qte = authorBuild("testScripts/author/qte.game", out .. "qte.singe")
debugPrint("AUTHOR built " .. built.platformer)
debugPrint("AUTHOR built " .. built.qte)
-- Both must at least load as Lua; a compiler that emits something unparseable is the failure this
-- catches earliest, and it costs nothing to check both even though only one is played.
for name, path in pairs(built) do
local chunk, problem = loadfile(path)
if chunk == nil then
debugPrint("AUTHOR FAIL " .. name .. " does not parse: " .. tostring(problem))
else
debugPrint("AUTHOR parsed " .. name)
end
end
-- Now run the platformer, as the engine would if it had been launched directly.
dofile(built.platformer)
local gameUpdate = onOverlayUpdate
local frames = 0
-- Driving by what the game is doing, not by a clock or a frame count.
--
-- Both of those were tried and both are wrong. A frame count is wrong because the authored game
-- moves in seconds. A clock is wrong because physics steps at most MAX_STEPS_PER_FRAME (4, so
-- 66 ms) per frame and deliberately falls behind wall clock on a machine that cannot keep up --
-- which the sanitizer build, with a disc to decode, does. A test that watches the hero instead
-- gives the same answer at any frame rate, and exercises onGround from this side as well.
local JUMP_AT = 340 -- Where the hero leaves the ground to reach the ledge.
local GIVE_UP = 20.0 -- Seconds. Generous: this is a stuck detector, not a schedule.
local held = false
local jumped = false
local won = 0
local shots = 0
function onOverlayUpdate()
local t = authorTime()
local hero = authorEntity("hero")
local hx, hy = authorPosition(hero)
frames = frames + 1
if not held then
authorKeyDown(0, SCANCODE.RIGHT.value)
held = true
end
-- Jump once, when the hero is under way and has ground beneath it.
if (not jumped) and (hx >= JUMP_AT) and playerIsOnGround(hero.node) then
authorKeyDown(0, SCANCODE.SPACE.value)
jumped = true
elseif jumped then
authorKeyUp(0, SCANCODE.SPACE.value)
end
local r = gameUpdate()
-- The engine's own text over the authored game, so a screenshot says what it is looking at.
colorForeground(255, 255, 255, 255)
fontPrint(12, 450, string.format("authored platformer, %.1fs, score %d", t, AUTHOR_SCORE))
-- Four shots at the moments that matter, found by watching rather than by timing: the start,
-- the run, the jump, and the frame after the prize was taken.
if (shots == 0) or (shots == 1 and hx > 250) or (shots == 2 and jumped and not playerIsOnGround(hero.node)) or (shots == 3 and won > 0 and t > won + 0.3) then
shots = shots + 1
singeScreenshot()
end
if AUTHOR_FLAG["won"] and won == 0 then
won = t
end
if (won > 0 and t > won + 0.6) or (t > GIVE_UP) then
debugPrint("AUTHOR RESULT score=" .. AUTHOR_SCORE .. " flagWon=" .. tostring(AUTHOR_FLAG["won"]) .. " after=" .. string.format("%.1fs", t))
singeQuit()
end
return r
end