-- Forge tutorial 5, "A quick-time event": the kit's clip as the disc, two windows that want a -- key, a hit and a miss for each, lives, and the clip looping. Starts from New game. TUTORIAL = { number = 5, name = "05-qte", tag = "TUTORIAL5" } dofile("testScripts/tutorialDrive.singe") local frames = 0 local stage = 0 local game = nil local pressed = false local shot = false tutorialBegin(nil) local function lightType(name) local names = {} local from, to = 1, 1 for typeName in pairs(FORGE.game.types) do names[#names + 1] = typeName end table.sort(names) for at, typeName in ipairs(names) do if typeName == FORGE.typeName then from = at end if typeName == name then to = at end end for _ = 1, math.abs(to - from) do key((to > from) and SCANCODE.DOWN or SCANCODE.UP) end end local function ruleGo(index, part) local guard = 0 while ((FORGE.rule ~= index) or (FORGE.part ~= (part or 0))) and (guard < 60) do local past = (FORGE.rule > index) or ((FORGE.rule == index) and (FORGE.part > (part or 0))) key(past and SCANCODE.UP or SCANCODE.DOWN) guard = guard + 1 end end local function place(x, y) key(SCANCODE.MAIN_1) key(SCANCODE.A) local entry = forgeRoom().entities[FORGE.selected] forgePress(entry.x, entry.y) forgeDrag(x, y) forgeRelease() end local function rule(note, on, filters) key(SCANCODE.N) form("note", note) key(SCANCODE.E) pick(on) for _, pair in ipairs(filters or {}) do form(pair[1], pair[2]) end end local function part(partKey, name, fields) key(partKey) pick(name) for _, pair in ipairs(fields or {}) do form(pair[1], pair[2]) end end -- One window: the prompt while it is open, the hit once, and the miss when it closes unanswered. local function window(name, from, to, keyName, prompt) rule("Window " .. name .. " is open", "frame", {}) part(SCANCODE.C, "discBetween", { { "from", from }, { "to", to } }) part(SCANCODE.T, "setText", { { "entity", "prompt" }, { "text", '"' .. prompt .. '"' } }) rule("Window " .. name .. " answered in time", "frame", {}) part(SCANCODE.C, "discBetween", { { "from", from }, { "to", to } }) part(SCANCODE.C, "keyPressed", { { "key", keyName } }) part(SCANCODE.C, "once", { { "tag", name } }) part(SCANCODE.T, "setVar", { { "name", "hit" .. name }, { "value", "true" } }) clear("entity") part(SCANCODE.T, "addScore", { { "amount", 250 } }) part(SCANCODE.T, "setText", { { "entity", "verdict" }, { "text", '"HIT"' } }) rule("Window " .. name .. " closed unanswered", "frameReached", { { "frame", to } }) part(SCANCODE.C, "test", { { "expr", "not hit" .. name } }) part(SCANCODE.T, "addVar", { { "name", "lives" }, { "amount", -1 } }) clear("entity") part(SCANCODE.T, "setText", { { "entity", "verdict" }, { "text", '"MISS"' } }) part(SCANCODE.T, "setText", { { "entity", "prompt" }, { "text", '""' } }) end local function step() if stage == 1 then key(SCANCODE.MAIN_1) selectEntity("hero") key(SCANCODE.DELETE) selectEntity("ground") key(SCANCODE.DELETE) key(SCANCODE.MAIN_2) lightType("hero") key(SCANCODE.DELETE) lightType("ground") key(SCANCODE.DELETE) key(SCANCODE.MAIN_3) ruleGo(1, 0) key(SCANCODE.DELETE) key(SCANCODE.DELETE) key(SCANCODE.DELETE) key(SCANCODE.MAIN_1) forgeSelect(nil) form("title", "Moments") form("vars", "score=0, lives=3") form("layers", "disc, overlay") form("disc.file", "clip.mkv") tutorialShot("disc") elseif stage == 2 then key(SCANCODE.MAIN_2) key(SCANCODE.A) form("name", "prompt") form("look", "text") form("look.r", 255) form("look.g", 220) form("look.b", 80) place(250, 60) key(SCANCODE.MAIN_2) key(SCANCODE.A) form("name", "verdict") form("look", "text") form("look.r", 160) form("look.g", 255) form("look.b", 180) place(250, 100) tutorialShot("prompts") elseif stage == 3 then key(SCANCODE.MAIN_3) window("one", 72, 120, "SPACE", "PRESS!") tutorialShot("window") elseif stage == 4 then window("two", 150, 200, "UP", "UP!") rule("The clip runs out: round again", "frameReached", { { "frame", 280 } }) part(SCANCODE.T, "setVar", { { "name", "hitone" }, { "value", "false" } }) clear("entity") part(SCANCODE.T, "setVar", { { "name", "hittwo" }, { "value", "false" } }) clear("entity") part(SCANCODE.T, "setText", { { "entity", "verdict" }, { "text", '""' } }) part(SCANCODE.T, "discTo", { { "frame", 1 } }) rule("Out of lives", "frame", {}) part(SCANCODE.C, "test", { { "expr", "lives <= 0" } }) part(SCANCODE.T, "gameOver", {}) ruleGo(1, 1) form("text", '"score " .. score .. " lives " .. lives') tutorialShot("rules") elseif stage == 5 then key(SCANCODE.S) local built = tutorialEnd() local mine = onOverlayUpdate dofile(built) game = onOverlayUpdate onOverlayUpdate = mine end end function onOverlayUpdate() frames = frames + 1 if frames % 12 == 0 and stage < 5 then stage = stage + 1 step() end if game ~= nil then local frame = discGetFrame() if (frame >= 84) and not pressed then pressed = true authorKeyDown(0, SCANCODE.SPACE.value) end if (frame >= 100) and not shot then shot = true tutorialShot("playing") end if frame >= 210 then debugPrint(string.format("%s at frame %d the score is %s with %s lives", TUTORIAL.tag, frame, tostring(AUTHOR_VARS.score), tostring(AUTHOR_VARS.lives))) if (AUTHOR_VARS.score ~= 250) or (AUTHOR_VARS.lives ~= 2) then debugPrint(TUTORIAL.tag .. " FAIL window one should be hit and window two missed") end debugPrint(TUTORIAL.tag .. " RESULT done") singeQuit() end return game() end forgeDraw(nil, nil) return OVERLAY_UPDATED end