-- 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) 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) jumped = true elseif jumped then authorKeyUp(0, SCANCODE.SPACE) 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