-- 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 local m = q.move[q.currentMove] local window = nil local chosen = nil if q.timegal 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 q.rdg and q.ACTUP then pairs = { [q.ACTUP] = { 5, 1 }, [q.ACTDOWN] = { 5, 2 }, [q.ACTLEFT] = { 5, 3 }, [q.ACTRIGHT] = { 5, 4 }, [q.UPLEFT] = { 1, 3 }, [q.UPRIGHT] = { 1, 4 }, [q.DOWNLEFT] = { 2, 3 }, [q.DOWNRIGHT] = { 2, 4 } } end if ((q.lvlState == 19) or (q.rdg 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 q.timegal 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 q.kimmy and (screen == q.levelNG) then screen = nil end -- The map-mode lineage never numbered its finish screen either. if q.rdg 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 = q.kimmy, rdg = q.rdg, timegal = q.timegal, multi = q.kimmy and q.multi[q.currentMove] or nil, path = q.kimmy and q.path[q.currentMove] or nil, timed = q.kimmy and q.timed or nil, freeplay = (q.dip_CoinsPerCredit == q.DOPT_FREEPLAY), credits = q.iCredits, zone = zone, mouse = mouse, pairs = pairs, mashKind = q.rdg and q.MASH or nil } 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