singe/docs/learn/08-when-it-goes-wrong.singe
2026-09-22 21:57:42 -05:00

179 lines
4.3 KiB
Text

-- Learn to Program with Singe -- Lesson 8: When It Goes Wrong
dofile("Singe/Framework.singe")
local DEBUG = true
local REPORT_FRAMES = 120
local PLAYER_WIDTH = 40
local PLAYER_HEIGHT = 8
local PLAYER_SPEED = 4
local PLAYER_MARGIN = 4
local BLOCK_SIZE = 12
local BLOCK_SPEED = 1.5
local BLOCK_FASTER = 0.05
local SPAWN_FRAMES = 40
local START_LIVES = 3
local TEXT_COLUMNS = 60
local screenWidth = overlayGetWidth()
local screenHeight = overlayGetHeight()
local playerY = screenHeight - PLAYER_HEIGHT - PLAYER_MARGIN
local playerX = (screenWidth - PLAYER_WIDTH) / 2
local goingLeft = false
local goingRight = false
local blocks = {}
local blockSpeed = BLOCK_SPEED
local spawnTimer = 0
local score = 0
local lives = START_LIVES
local state = "waiting"
local reportTimer = 0
function debugLog(text)
if DEBUG then
debugPrint(text)
end
end
function debugReport()
debugLog(state .. ": score " .. score .. ", lives " .. lives .. ", blocks " .. #blocks)
debugLog(" speed " .. blockSpeed .. ", left " .. tostring(goingLeft) .. ", right " .. tostring(goingRight))
end
function drawGame()
colorForeground(80, 255, 160)
overlayBox(playerX, playerY, playerX + PLAYER_WIDTH - 1, playerY + PLAYER_HEIGHT - 1)
colorForeground(255, 90, 90)
for i, block in ipairs(blocks) do
overlayBox(block.x, block.y, block.x + BLOCK_SIZE - 1, block.y + BLOCK_SIZE - 1)
end
overlayPrint(1, 1, "SCORE " .. score .. " LIVES " .. lives)
if DEBUG then
overlayPrint(1, 3, "BLOCKS " .. #blocks .. " PLAYER " .. playerX)
end
end
function drawOverText()
printCentered(7, "GAME OVER")
printCentered(9, "PRESS SPACE TO PLAY AGAIN")
end
function drawWaitingText()
printCentered(6, "DODGE THE BLOCKS")
printCentered(8, "ARROW KEYS TO MOVE")
printCentered(10, "PRESS SPACE TO START")
end
function printCentered(row, text)
overlayPrint(math.floor((TEXT_COLUMNS - #text) / 2), row, text)
end
function spawnBlock()
local block = {}
block.x = math.random(0, screenWidth - BLOCK_SIZE)
block.y = -BLOCK_SIZE
table.insert(blocks, block)
end
function startGame()
playerX = (screenWidth - PLAYER_WIDTH) / 2
blocks = {}
blockSpeed = BLOCK_SPEED
spawnTimer = 0
score = 0
lives = START_LIVES
state = "playing"
debugLog("startGame: lives " .. lives .. ", speed " .. blockSpeed)
end
function updatePlaying()
if goingLeft then
playerX = playerX - PLAYER_SPEED
end
if goingRight then
playerX = playerX + PLAYER_SPEED
end
if playerX < 0 then
playerX = 0
elseif playerX > screenWidth - PLAYER_WIDTH then
playerX = screenWidth - PLAYER_WIDTH
end
spawnTimer = spawnTimer - 1
if spawnTimer <= 0 then
spawnBlock()
spawnTimer = SPAWN_FRAMES
end
for i = #blocks, 1, -1 do
local block = blocks[i]
block.y = block.y + blockSpeed
if collideRects(block.x, block.y, BLOCK_SIZE, BLOCK_SIZE, playerX, playerY, PLAYER_WIDTH, PLAYER_HEIGHT) then
table.remove(blocks, i)
lives = lives - 1
debugLog("hit at y " .. math.floor(block.y) .. ", lives now " .. lives)
if lives == 0 then
state = "over"
debugLog("game over with score " .. score)
end
elseif block.y > screenHeight then
table.remove(blocks, i)
score = score + 1
blockSpeed = blockSpeed + BLOCK_FASTER
end
end
end
function onInputPressed(what)
if what == SWITCH_LEFT then
goingLeft = true
elseif what == SWITCH_RIGHT then
goingRight = true
elseif what == SWITCH_BUTTON1 and state ~= "playing" then
startGame()
end
end
function onInputReleased(what)
if what == SWITCH_LEFT then
goingLeft = false
elseif what == SWITCH_RIGHT then
goingRight = false
end
end
function onOverlayUpdate()
reportTimer = reportTimer - 1
if reportTimer <= 0 then
debugReport()
reportTimer = REPORT_FRAMES
end
if state == "playing" then
updatePlaying()
end
overlayClear()
drawGame()
if state == "waiting" then
drawWaitingText()
elseif state == "over" then
drawOverText()
end
return OVERLAY_UPDATED
end