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

254 lines
6.2 KiB
Text

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