-- Forge tutorial 6, "A light gun game": the kit's clip as the disc, a gun on the pointer, hit -- boxes drawn on the timeline where the target and the hand are, ammo and a reload off the -- picture, and a score. Starts from New game. TUTORIAL = { number = 6, name = "06-gun", tag = "TUTORIAL6" } dofile("testScripts/tutorialDrive.singe") local frames = 0 local stage = 0 local game = nil local fired = {} 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 -- The cursor to a frame, by the leap and step keys, as a reader would. local function cursorTo(frame) while FORGE.cursor + 10 <= frame do key(SCANCODE.RIGHTBRACKET) end while FORGE.cursor < frame do key(SCANCODE.PERIOD) end while FORGE.cursor > frame do key(SCANCODE.COMMA) end end -- A key on the selected track at a frame, its box typed. local function keyAt(frame, x, y, w, h) cursorTo(frame) key(SCANCODE.K) form("x", x) form("y", y) form("w", w) form("h", h) 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", "Draw") form("vars", "score=0, misses=0") form("layers", "disc, overlay") form("disc.file", "clip.mkv") elseif stage == 2 then -- The gun, and the two things it can hit. key(SCANCODE.MAIN_2) key(SCANCODE.A) form("name", "gun") form("look", "none") form("vars", "ammo=6") form("behaviours", "gun") form("gun.player", 1) form("gun.ammo", 6) form("gun.reload", "offscreen") key(SCANCODE.A) form("name", "target") form("look", "none") form("vars", "health=1") form("behaviours", "hitbox, health") form("hitbox.track", "target") form("health.max", 1) key(SCANCODE.A) form("name", "hand") form("look", "none") form("behaviours", "hitbox") form("hitbox.track", "hand") tutorialShot("types") elseif stage == 3 then lightType("gun") place(30, 450) key(SCANCODE.MAIN_2) lightType("target") place(450, 210) key(SCANCODE.MAIN_2) lightType("hand") place(600, 210) elseif stage == 4 then -- The tracks: where the target is, frame by frame, and where the hand is. key(SCANCODE.MAIN_4) key(SCANCODE.N) form("name", "target") keyAt(150, 405, 165, 90, 90) tutorialShot("firstKey") elseif stage == 5 then keyAt(210, 405, 165, 90, 90) key(SCANCODE.N) form("name", "hand") keyAt(220, 480, 157, 240, 105) keyAt(260, 480, 157, 240, 105) cursorTo(180) tutorialShot("tracks") elseif stage == 6 then key(SCANCODE.MAIN_3) rule("The target is hit", "hit", { { "type", "target" } }) part(SCANCODE.T, "addScore", { { "amount", 100 } }) part(SCANCODE.T, "damage", { { "amount", 1 } }) part(SCANCODE.T, "playSound", { { "file", "hit.wav" } }) rule("The hand is hit", "hit", { { "type", "hand" } }) part(SCANCODE.T, "addScore", { { "amount", -200 } }) part(SCANCODE.T, "flash", { { "r", 255 }, { "g", 40 }, { "b", 40 }, { "seconds", 0.3 } }) rule("A shot at nothing", "miss", {}) part(SCANCODE.C, "test", { { "expr", "not event.offscreen" } }) part(SCANCODE.T, "addVar", { { "name", "misses" }, { "amount", 1 } }) clear("entity") part(SCANCODE.T, "playSound", { { "file", "shot.wav" } }) rule("Draw!", "frameReached", { { "frame", 150 } }) part(SCANCODE.T, "say", { { "text", '"DRAW!"' }, { "seconds", 1 } }) ruleGo(1, 1) form("text", '"score " .. score .. " ammo " .. gun.ammo .. " misses " .. misses') tutorialShot("rules") elseif stage == 7 then key(SCANCODE.S) local built = tutorialEnd() local mine = onOverlayUpdate dofile(built) game = onOverlayUpdate onOverlayUpdate = mine end end -- A shot from the pointer at a frame of the disc, once. local function fireAt(frame, x, y, now) if (now >= frame) and not fired[frame] then fired[frame] = true authorSetPointer(1, x, y) authorSwitchDown(SWITCH_BUTTON3) authorSwitchUp(SWITCH_BUTTON3) end end function onOverlayUpdate() frames = frames + 1 if frames % 12 == 0 and stage < 7 then stage = stage + 1 step() end if game ~= nil then local frame = discGetFrame() fireAt(170, 450, 210, frame) -- the target fireAt(190, 100, 100, frame) -- the wall fireAt(240, 620, 210, frame) -- the hand if (frame >= 175) and not shot then shot = true tutorialShot("playing") end if frame >= 270 then debugPrint(string.format("%s at frame %d: score %s, misses %s, ammo %s", TUTORIAL.tag, frame, tostring(AUTHOR_VARS.score), tostring(AUTHOR_VARS.misses), tostring(authorEntity("gun").vars.ammo))) if (AUTHOR_VARS.score ~= -100) or (AUTHOR_VARS.misses ~= 1) then debugPrint(TUTORIAL.tag .. " FAIL a hit on the target, a miss on the wall, and a hit on the hand should score -100 with one miss") end debugPrint(TUTORIAL.tag .. " RESULT done") singeQuit() end return game() end forgeDraw(nil, nil) return OVERLAY_UPDATED end