-- Forge tutorial 7, "A point-and-click adventure": a painted yard, a walk area drawn on it, -- a walker sent by clicks, hotspots with verbs, an inventory, a guard with a dialogue, and a -- door to a second painted room. Starts from New game. TUTORIAL = { number = 7, name = "07-adventure", tag = "TUTORIAL7" } dofile("testScripts/tutorialDrive.singe") local frames = 0 local stage = 0 local game = nil local talked = 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 kitType(name, file, anchor) key(SCANCODE.MAIN_2) key(SCANCODE.A) form("name", name) form("look", "sprite") form("look.file", file) if anchor then form("look.anchor", anchor) 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 -- Up or down the dialogue outline to a row: a dialogue by name, a node by name, or a choice by -- its text, as a reader would with the arrows. local function dialogueGo(level, name) local guard = 0 local function here() for _, row in ipairs(forgeDialogueRows()) do local selected = (row.dialogue == FORGE.dialogue) and (row.node == FORGE.dnode) and ((row.choice or 0) == (FORGE.dchoice or 0)) and ((row.act or 0) == (FORGE.dact or 0)) if selected and (row.level == level) then if (level == "dialogue") and (row.dialogue == name) then return true elseif (level == "node") and (row.node == name) then return true elseif (level == "choice") and row.label:find(name, 1, true) then return true end end end return false end -- Straight to the top, then down until the row is the one. while not here() and (guard < 40) do key(SCANCODE.UP) guard = guard + 1 if FORGE.dnode == nil and level == "dialogue" and FORGE.dialogue == name then break end end guard = 0 while not here() and (guard < 40) do key(SCANCODE.DOWN) guard = guard + 1 end if not here() then debugPrint(string.format("%s FAIL the outline never reached %s %s", TUTORIAL.tag, level, name)) end end -- A walk area: V, a press on each corner, V again. -- The panel steps aside on its own while the corners are pressed. local function walkArea(corners) key(SCANCODE.MAIN_1) forgeSelect(nil) key(SCANCODE.V) for _, corner in ipairs(corners) do forgePress(corner[1], corner[2]) end key(SCANCODE.V) 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", "The Yard") form("vars", "score=0, allowed=false") form("verbs", "walk, look, take, use, open, talk") form("layers", "overlay") form("name", "yard") form("depthSort", "true") form("scaleBy", "300:0.6, 460:1.0") elseif stage == 2 then -- The painting, the walk area on its floor, and the walker. kitType("backdrop", "yard.png") place(360, 240) walkArea({ { 60, 320 }, { 660, 320 }, { 700, 460 }, { 40, 460 } }) forgePanelShow(false) tutorialShot("walkArea") forgePanelShow(true) kitType("hero", "hero.png", "feet") form("look.frames", 8) form("behaviours", "walker, frames") form("walker.speed", 160) form("frames.fps", 10) form("frames.states", "idle=1-1, walk=2-5") place(150, 440) elseif stage == 3 then -- The things a verb can be used on. kitType("door", "door.png", "feet") form("behaviours", "hotspot") form("hotspot.name", "door") form("hotspot.walkX", 590) form("hotspot.walkY", 330) place(590, 300) kitType("key", "key.png") form("behaviours", "hotspot") form("hotspot.name", "key") form("hotspot.walkX", 200) form("hotspot.walkY", 400) place(200, 380) kitType("guard", "hero.png", "feet") form("look.frames", 8) form("look.faces", "left") form("behaviours", "hotspot") form("hotspot.name", "guard") form("hotspot.walkX", 480) form("hotspot.walkY", 360) place(520, 340) tutorialShot("hotspots") elseif stage == 4 then -- The guard's dialogue. key(SCANCODE.MAIN_5) key(SCANCODE.A) form("name", "guard") key(SCANCODE.DOWN) form("who", "Guard") form("text", "Nobody passes.") key(SCANCODE.A) form("text", "Why not?") form("next", "why") key(SCANCODE.A) form("text", "Fine.") dialogueGo("dialogue", "guard") key(SCANCODE.A) form("name", "why") form("who", "Guard") form("text", "The door is locked, and I have no key.") key(SCANCODE.A) form("text", "I have a key.") form("when", 'has("key")') form("next", "ok") key(SCANCODE.T) pick("setVar") form("name", "allowed") form("value", "true") clear("entity") dialogueGo("node", "why") key(SCANCODE.A) form("text", "I see.") dialogueGo("dialogue", "guard") key(SCANCODE.A) form("name", "ok") form("who", "Guard") form("text", "Then go ahead.") tutorialShot("dialogue") elseif stage == 5 then key(SCANCODE.MAIN_3) rule("A click on the floor walks there", "pressed", { { "switch", "SWITCH_BUTTON3" }, { "interrupt", "true" } }) part(SCANCODE.T, "walkToPointer", { { "entity", "hero" }, { "player", 1 } }) rule("Look at anything", "verb", { { "verb", "look" } }) part(SCANCODE.T, "say", { { "text", '"It is a " .. event.target .. "."' }, { "seconds", 1 } }) rule("Take the key", "verb", { { "verb", "take" }, { "target", "key" } }) part(SCANCODE.T, "walkToHotspot", { { "entity", "hero" } }) part(SCANCODE.T, "give", { { "item", "key" } }) part(SCANCODE.T, "addScore", { { "amount", 1 } }) part(SCANCODE.T, "destroy", {}) rule("Talk to the guard", "verb", { { "verb", "talk" }, { "target", "guard" } }) part(SCANCODE.T, "walkToHotspot", { { "entity", "hero" } }) part(SCANCODE.T, "face", { { "entity", "hero" }, { "target", "self" } }) part(SCANCODE.T, "talk", { { "dialogue", "guard" } }) tutorialShot("verbRules") elseif stage == 6 then rule("The door, while the guard objects", "verb", { { "verb", "open" }, { "target", "door" } }) part(SCANCODE.C, "test", { { "expr", "not allowed" } }) part(SCANCODE.T, "say", { { "text", '"The guard shakes his head."' }, { "seconds", 1 } }) rule("The door opens", "verb", { { "verb", "open" }, { "target", "door" }, { "controls", "false" } }) part(SCANCODE.C, "test", { { "expr", "allowed" } }) part(SCANCODE.T, "walkToHotspot", { { "entity", "hero" } }) part(SCANCODE.T, "playSound", { { "file", "door.wav" } }) part(SCANCODE.T, "fade", { { "seconds", 0.5 }, { "out", "true" } }) part(SCANCODE.T, "goTo", { { "room", "hall" }, { "entity", "hero" }, { "x", 120 }, { "y", 420 } }) part(SCANCODE.T, "fade", { { "seconds", 0.5 }, { "out", "false" } }) part(SCANCODE.T, "addScore", { { "amount", 1 } }) rule("Anything else", "verb", {}) part(SCANCODE.T, "say", { { "text", '"That does not work."' }, { "seconds", 1 } }) rule("The verb keys", "pressed", { { "key", "L" } }) part(SCANCODE.T, "setVerb", { { "verb", "look" } }) rule("Take", "pressed", { { "key", "T" } }) part(SCANCODE.T, "setVerb", { { "verb", "take" } }) rule("Open", "pressed", { { "key", "O" } }) part(SCANCODE.T, "setVerb", { { "verb", "open" } }) rule("Talk", "pressed", { { "key", "K" } }) part(SCANCODE.T, "setVerb", { { "verb", "talk" } }) ruleGo(1, 1) form("text", '(sentence or "") .. " score " .. score') elseif stage == 7 then -- The hall: a second painting, its floor, and the readout. key(SCANCODE.MAIN_1) key(SCANCODE.N) form("name", "hall") form("depthSort", "true") key(SCANCODE.MAIN_2) lightType("backdrop") place(360, 240) walkArea({ { 40, 300 }, { 680, 300 }, { 700, 460 }, { 40, 460 } }) key(SCANCODE.MAIN_1) selectEntity("backdrop") form("type", "backdrop") key(SCANCODE.MAIN_2) lightType("readout") place(12, 12) tutorialShot("hall") elseif stage == 8 then key(SCANCODE.S) local built = tutorialEnd() local mine = onOverlayUpdate dofile(built) game = onOverlayUpdate onOverlayUpdate = mine elseif stage == 10 then -- Take the key: the take verb on the key hotspot. authorSetVerb("take") authorSetPointer(1, 200, 380) authorSwitchDown(SWITCH_BUTTON3) authorSwitchUp(SWITCH_BUTTON3) elseif stage == 16 then tutorialShot("playing") local hx, hy = authorPosition(authorEntity("hero")) debugPrint(string.format("%s the key is %s, the score %s, the hero at %.0f, %.0f", TUTORIAL.tag, authorHas("key") and "taken" or "still there", tostring(AUTHOR_VARS.score), hx, hy)) if not authorHas("key") then debugPrint(TUTORIAL.tag .. " FAIL the take verb on the key should walk there and take it") end elseif stage == 17 then debugPrint(TUTORIAL.tag .. " RESULT done") singeQuit() end end function onOverlayUpdate() frames = frames + 1 if frames % 12 == 0 and stage < 17 then stage = stage + 1 step() end if game ~= nil then return game() end forgeDraw(nil, nil) return OVERLAY_UPDATED end