-- The Forge port of a KarisFramework game, driven and traced as the original is. local here = ((debug.getinfo(1, "S").source:gsub("^@", "")):match("^(.*[/\\])") or "") QTE_LEVEL_FINISH = 110 local library = os.getenv("SINGE_LIBRARY") or (os.getenv("HOME") .. "/claude/singetest/ported") local game = os.getenv("PORT_GAME") or "TimeGal" local name = (os.getenv("PORT_SCRIPT") or "Timegal.singe"):gsub("%.singe$", "") local stem = name:match("([^/\\]+)$") dofile("Forge/AuthorCompile.singe") dofile(here .. "karisDrive.singe") local realSkip = discSkipToFrame discSkipToFrame = function(frame) karisTraceSeek(frame) return realSkip(frame) end dofile(authorBuild(library .. "/" .. game .. "/" .. name .. ".forge", singeGetDataPath() .. stem .. ".port.singe")) local realUpdate = onOverlayUpdate local frames = 0 function karisPress(switch, down) if down then authorSwitchDown(switch) else authorSwitchUp(switch) end end function karisMouse(x, y) onMouseMoved(x, y, 0, 0, 0) end local function view() local q = authorEntity("game").q -- The style the qte behaviour plays, as the flags the view reports. local held = (q.qteStyle == "held") local checkpoints = (q.qteStyle == "checkpoints") local segmented = (q.qteStyle == "segments") or checkpoints or (q.qteStyle == "trophies") local m = q.move[q.currentMove] local window = nil local chosen = nil if checkpoints then if (q.currentLevel == 103) and m and (m[2] ~= nil) then window = { from = m[2], to = m[3], kind = m[7], death = 0, alt = 0, alt2 = 0 } end elseif (q.currentLevel == 103) and m and (m[1] ~= nil) then window = { from = m[1], to = m[2], kind = m[3], death = m[4], alt = m[5], alt2 = m[6] } end local zone = (m and (m[3] == q.ACTDOWN) and (m[7] ~= nil)) and { m[7], m[8], m[9], m[10] } or nil local mouse = q.ratiox and { q.ratiox, q.ratioy, q.ratioxOffset, q.ratioyOffset } or nil local pairs = nil if segmented and q.ACTUP then pairs = karisPairs(q) end if ((q.lvlState == 19) or (segmented and (q.lvlState == 11))) and q.choice then chosen = {} for index, c in ipairs(q.choice) do chosen[index] = { right = c[2] } end end if checkpoints and (q.lvlState == 12) and q.opt then chosen = {} for index, c in ipairs(q.opt) do chosen[index] = { right = c[2] } end end -- Kimmy's new-game menu is a level the framework never numbered (nil there); the port -- numbers it, and reports it as the original does. local screen = q.currentLevel if held and (screen == q.levelNG) then screen = nil end -- The map-mode lineage never numbered its finish screen either. if segmented and (screen == QTE_LEVEL_FINISH) then screen = nil end return { screen = screen, step = q.lvlState, level = q.iLevel or q.iCurPos or q.iStageIndex, scene = q.iScene or q.iSegPointer, move = q.currentMove, frame = discGetFrame(), frames = frames, score = q.iScore, lives = q.iLives, window = window, choices = chosen, order = q.optorder, choice = q.iChoice, kimmy = held, rdg = segmented, timegal = checkpoints, multi = held and q.multi[q.currentMove] or nil, path = held and q.path[q.currentMove] or nil, timed = held and q.timed or nil, freeplay = (q.dip_CoinsPerCredit == q.DOPT_FREEPLAY), credits = q.iCredits, zone = zone, mouse = mouse, pairs = pairs, mashKind = segmented and q.MASH or nil, hud = karisHudFlags(function(name) return q[name] end), service = karisServiceState(screen, function(name) return q[name] end) } end function onOverlayUpdate() frames = frames + 1 if authorEntity("game").q.gameflow == "running" then karisDrive(view()) end local r = realUpdate() if (frames > 100) and karisDone(view()) or (frames > KARIS_FRAMES) then debugPrint("TRACE end after " .. frames .. " frames") singeQuit() end return r end