singe/testScripts/scene89.singe
2026-09-14 22:32:53 -05:00

353 lines
9.9 KiB
Text

-- 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