singe/docs/learn/18-qte.singe
2026-09-22 21:57:42 -05:00

159 lines
3.4 KiB
Text

-- Learn to Program with Singe -- Lesson 18: Quick-Time Events
dofile("Singe/Framework.singe")
overlaySetResolution(discGetWidth(), discGetHeight())
local FIRST_FRAME = 20
local WRONG_FRAME = 362
local LATE_FRAME = 388
local LAST_FRAME = 415
local PROMPT_COL = 54
local PROMPT_ROW = 22
local GAUGE_X = 210
local GAUGE_Y = 330
local GAUGE_W = 300
local GAUGE_H = 18
local RIGHT_SCORE = 100
local moves = {
{ first = 60, last = 105, switch = SWITCH_LEFT, name = "LEFT" },
{ first = 140, last = 185, switch = SWITCH_UP, name = "UP" },
{ first = 220, last = 265, switch = SWITCH_RIGHT, name = "RIGHT" },
{ first = 300, last = 345, switch = SWITCH_DOWN, name = "DOWN" },
}
local score = 0
local runOver = false
local verdict = ""
local function fillBar(x, y, width, height)
if width < 1 then
return
end
for row = 0, height - 1 do
overlayLine(x, y + row, x + width - 1, y + row)
end
end
local function isDirection(what)
return what == SWITCH_UP or what == SWITCH_DOWN or what == SWITCH_LEFT or what == SWITCH_RIGHT
end
local function moveAt(frame)
for _, move in ipairs(moves) do
if move.answer == nil and frame >= move.first and frame <= move.last then
return move
end
end
return nil
end
local function startRun()
score = 0
runOver = false
verdict = ""
for _, move in ipairs(moves) do
move.answer = nil
end
discSkipToFrame(FIRST_FRAME)
end
local function endRun(reason, frame)
verdict = reason
runOver = true
discSkipToFrame(frame)
end
function onInputPressed(what)
if runOver then
if what == SWITCH_START1 then
startRun()
end
return
end
if not isDirection(what) then
return
end
local move = moveAt(discGetFrame())
if move == nil then
return
end
if what == move.switch then
move.answer = "right"
score = score + RIGHT_SCORE
else
move.answer = "wrong"
endRun("WRONG WAY", WRONG_FRAME)
end
end
function onOverlayUpdate()
local frame = discGetFrame()
if not runOver then
for _, move in ipairs(moves) do
if move.answer == nil and frame > move.last then
move.answer = "late"
endRun("TOO SLOW", LATE_FRAME)
break
end
end
end
if frame >= LAST_FRAME and discGetState() == DISC_PLAYING then
if not runOver then
verdict = "CLEAR"
runOver = true
end
discSearch(LAST_FRAME)
end
overlayClear()
local move = nil
if not runOver then
move = moveAt(frame)
end
if move ~= nil then
local left = move.last - frame
local span = move.last - move.first
overlayPrint(PROMPT_COL, PROMPT_ROW, "PRESS " .. move.name)
colorForeground(255, 255, 255, 255)
overlayBox(GAUGE_X - 2, GAUGE_Y - 2, GAUGE_X + GAUGE_W + 1, GAUGE_Y + GAUGE_H + 1)
if left * 3 < span then
colorForeground(255, 60, 60, 255)
else
colorForeground(255, 200, 0, 255)
end
fillBar(GAUGE_X, GAUGE_Y, GAUGE_W * left // span, GAUGE_H)
end
overlayPrint(2, 1, "SCORE " .. score .. " FRAME " .. frame)
if runOver then
overlayPrint(2, 3, verdict)
overlayPrint(2, 4, "PRESS 1 TO RUN IT AGAIN")
end
return OVERLAY_UPDATED
end
startRun()