added plinko
This commit is contained in:
504
programs/plinko.lua
Normal file
504
programs/plinko.lua
Normal file
@@ -0,0 +1,504 @@
|
|||||||
|
-- Plinko Machine (fully physics-based)
|
||||||
|
-- Tom's Peripherals GPU + screen wall (any size).
|
||||||
|
--
|
||||||
|
-- A ball is dropped from a random X position near the top.
|
||||||
|
-- It falls under gravity and collides with circular pegs using
|
||||||
|
-- proper elastic circle-circle collision response (reflect velocity
|
||||||
|
-- along the contact normal). The bucket it lands in is determined
|
||||||
|
-- entirely by the physics — no predetermined outcome.
|
||||||
|
--
|
||||||
|
-- Trigger: redstone pulse on any side.
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
-- GPU discovery
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
local function findGPU()
|
||||||
|
print("[plinko] Scanning peripherals...")
|
||||||
|
for _, name in ipairs(peripheral.getNames()) do
|
||||||
|
local t = peripheral.getType(name)
|
||||||
|
print(" " .. name .. " = " .. tostring(t))
|
||||||
|
if t and t:find("gpu") then
|
||||||
|
print("[plinko] Using GPU: " .. name)
|
||||||
|
return peripheral.wrap(name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
-- Physics / display constants
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
local FRAME_DELAY = 0.02 -- s (~50 fps)
|
||||||
|
local DT = FRAME_DELAY
|
||||||
|
local GRAVITY = 700 -- px/s²
|
||||||
|
local RESTITUTION = 0.55 -- fraction of normal speed kept after bounce
|
||||||
|
local FRICTION = 0.88 -- fraction of tangential speed kept after bounce
|
||||||
|
local WALL_RESTITUTION = 0.35
|
||||||
|
local MAX_SPEED = 1200 -- px/s (clamp to avoid tunnelling)
|
||||||
|
|
||||||
|
local PEG_RADIUS = 7 -- px
|
||||||
|
local BALL_RADIUS = 9 -- px
|
||||||
|
local COMBINED_R = PEG_RADIUS + BALL_RADIUS
|
||||||
|
|
||||||
|
local PEG_ROWS = 9
|
||||||
|
local PEG_COLS_TOP = 3 -- pegs in first (narrowest) row
|
||||||
|
-- bottom row has PEG_COLS_TOP + PEG_ROWS - 1 pegs
|
||||||
|
|
||||||
|
local BUCKET_H = 60 -- px
|
||||||
|
local BOARD_MARGIN = 48 -- px left/right margin
|
||||||
|
|
||||||
|
-- Colours
|
||||||
|
local COL_BG = 0x080C14
|
||||||
|
local COL_BOARD = 0x0E1620
|
||||||
|
local COL_PEG = 0xCFD8DC
|
||||||
|
local COL_PEG_HIT = 0xFFFFFF -- flash colour when struck
|
||||||
|
local COL_BALL = 0xFFD600
|
||||||
|
local COL_BALL_SHADE = 0xBB8800
|
||||||
|
local COL_WALL = 0x1C2840
|
||||||
|
local COL_TEXT = 0xFFFFFF
|
||||||
|
|
||||||
|
-- Buckets: defined centre-outward; mirrored.
|
||||||
|
local BUCKET_DEFS = {
|
||||||
|
-- { label, colour, multiplier }
|
||||||
|
{ "100x", 0xF9A825, 100 },
|
||||||
|
{ "10x", 0x1565C0, 10 },
|
||||||
|
{ "5x", 0x2E7D32, 5 },
|
||||||
|
{ "2x", 0x4CAF50, 2 },
|
||||||
|
{ "1x", 0x37474F, 1 },
|
||||||
|
}
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
-- Module-level GPU state
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
local gpu
|
||||||
|
local PW, PH
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
-- Drawing helpers
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
local function px_rect(x, y, w, h, col)
|
||||||
|
x = math.floor(x); y = math.floor(y)
|
||||||
|
w = math.floor(w); h = math.floor(h)
|
||||||
|
if x < 1 then w = w + x - 1; x = 1 end
|
||||||
|
if y < 1 then h = h + y - 1; y = 1 end
|
||||||
|
if x + w - 1 > PW then w = PW - x + 1 end
|
||||||
|
if y + h - 1 > PH then h = PH - y + 1 end
|
||||||
|
if w < 1 or h < 1 then return end
|
||||||
|
gpu.filledRectangle(x, y, w, h, col)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function px_circle(cx, cy, r, col)
|
||||||
|
cx = math.floor(cx); cy = math.floor(cy); r = math.floor(r)
|
||||||
|
for dy = -r, r do
|
||||||
|
local half = math.floor(math.sqrt(r * r - dy * dy) + 0.5)
|
||||||
|
px_rect(cx - half, cy + dy, half * 2 + 1, 1, col)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function px_text(str, x, y, fg, bg, size)
|
||||||
|
pcall(gpu.drawText, math.floor(x), math.floor(y), str, fg, bg, size or 1, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function px_text_centre(str, y, fg, bg, size)
|
||||||
|
size = size or 2
|
||||||
|
local w = #str * 7 * size
|
||||||
|
px_text(str, math.max(1, math.floor((PW - w) / 2)), y, fg, bg, size)
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
-- Board geometry
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
local pegs = {}
|
||||||
|
local buckets = {}
|
||||||
|
local NUM_BUCKETS
|
||||||
|
local colSpacing, rowSpacing
|
||||||
|
local boardLeft, boardRight, boardTop, boardBottom
|
||||||
|
local bucketTop -- y of the top of bucket row
|
||||||
|
|
||||||
|
local function buildBoard()
|
||||||
|
pegs = {}
|
||||||
|
buckets = {}
|
||||||
|
|
||||||
|
local lastRowPegs = PEG_COLS_TOP + PEG_ROWS - 1
|
||||||
|
NUM_BUCKETS = lastRowPegs + 1
|
||||||
|
|
||||||
|
boardLeft = BOARD_MARGIN
|
||||||
|
boardRight = PW - BOARD_MARGIN
|
||||||
|
|
||||||
|
colSpacing = math.floor((boardRight - boardLeft) / lastRowPegs)
|
||||||
|
-- Reserve top 55px for title, bottom BUCKET_H + 10 for buckets.
|
||||||
|
rowSpacing = math.floor((PH - BUCKET_H - 75) / (PEG_ROWS + 1))
|
||||||
|
boardTop = 65
|
||||||
|
boardBottom = boardTop + (PEG_ROWS - 1) * rowSpacing
|
||||||
|
|
||||||
|
for row = 1, PEG_ROWS do
|
||||||
|
local pegsInRow = PEG_COLS_TOP + row - 1
|
||||||
|
local rowW = (pegsInRow - 1) * colSpacing
|
||||||
|
local startX = math.floor(PW / 2 - rowW / 2)
|
||||||
|
local py = boardTop + (row - 1) * rowSpacing
|
||||||
|
for col = 1, pegsInRow do
|
||||||
|
table.insert(pegs, {
|
||||||
|
x = startX + (col - 1) * colSpacing,
|
||||||
|
y = py,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Bucket top is one rowSpacing below the last peg row.
|
||||||
|
bucketTop = boardBottom + rowSpacing - math.floor(rowSpacing * 0.2)
|
||||||
|
|
||||||
|
local bucketW = colSpacing
|
||||||
|
local totalBW = NUM_BUCKETS * bucketW
|
||||||
|
local startBX = math.floor(PW / 2 - totalBW / 2)
|
||||||
|
local halfB = math.floor(NUM_BUCKETS / 2)
|
||||||
|
|
||||||
|
for i = 1, NUM_BUCKETS do
|
||||||
|
local dist
|
||||||
|
if NUM_BUCKETS % 2 == 1 then
|
||||||
|
dist = math.abs(i - (halfB + 1))
|
||||||
|
else
|
||||||
|
dist = math.max(0, math.abs(i - halfB) - 1)
|
||||||
|
end
|
||||||
|
local def = BUCKET_DEFS[math.min(dist + 1, #BUCKET_DEFS)]
|
||||||
|
table.insert(buckets, {
|
||||||
|
x = startBX + (i - 1) * bucketW,
|
||||||
|
y = bucketTop,
|
||||||
|
w = bucketW,
|
||||||
|
label = def[1],
|
||||||
|
color = def[2],
|
||||||
|
mult = def[3],
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
-- Identify which bucket an X coordinate falls in (physics outcome)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
local function bucketForX(x)
|
||||||
|
for i, b in ipairs(buckets) do
|
||||||
|
if x >= b.x and x < b.x + b.w then return i end
|
||||||
|
end
|
||||||
|
-- Clamp to edges
|
||||||
|
if x < buckets[1].x then return 1 end
|
||||||
|
return NUM_BUCKETS
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
-- Static board draw
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
local function drawBucket(i, glowing)
|
||||||
|
local b = buckets[i]
|
||||||
|
local bg = glowing and 0xFFFFFF or b.color
|
||||||
|
local fg = glowing and b.color or COL_TEXT
|
||||||
|
px_rect(b.x, b.y, b.w - 1, BUCKET_H, bg)
|
||||||
|
-- Divider
|
||||||
|
px_rect(b.x + b.w - 1, b.y, 1, BUCKET_H, COL_BOARD)
|
||||||
|
-- Centred label
|
||||||
|
local lw = #b.label * 7
|
||||||
|
local lx = b.x + math.floor((b.w - lw) / 2)
|
||||||
|
local ly = b.y + math.floor((BUCKET_H - 9) / 2)
|
||||||
|
px_text(b.label, lx, ly, fg, bg, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function drawAllBuckets(glowIdx)
|
||||||
|
for i = 1, NUM_BUCKETS do
|
||||||
|
drawBucket(i, i == glowIdx)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function drawPeg(p, col)
|
||||||
|
px_circle(p.x, p.y, PEG_RADIUS, col or COL_PEG)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function drawBoard()
|
||||||
|
px_rect(1, 1, PW, PH, COL_BG)
|
||||||
|
-- Board area background
|
||||||
|
px_rect(boardLeft - 8, boardTop - 18,
|
||||||
|
boardRight - boardLeft + 16, bucketTop - boardTop + 18 + BUCKET_H + 4,
|
||||||
|
COL_BOARD)
|
||||||
|
-- Left/right walls
|
||||||
|
px_rect(boardLeft - 8, boardTop - 18, 8, bucketTop - boardTop + 18, COL_WALL)
|
||||||
|
px_rect(boardRight, boardTop - 18, 8, bucketTop - boardTop + 18, COL_WALL)
|
||||||
|
|
||||||
|
for _, p in ipairs(pegs) do drawPeg(p) end
|
||||||
|
drawAllBuckets()
|
||||||
|
|
||||||
|
px_text_centre("PLINKO", 8, 0xFFD600, COL_BG, 3)
|
||||||
|
px_text_centre("Pull lever to drop", 38, 0x607080, COL_BG, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
-- Physics simulation — returns final bucket index
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Erase the ball at (lx,ly) and restore any pegs it was covering.
|
||||||
|
local function eraseBall(lx, ly)
|
||||||
|
local r = BALL_RADIUS + 3
|
||||||
|
px_circle(lx, ly, r, COL_BOARD)
|
||||||
|
-- Restore wall stripes if needed
|
||||||
|
if lx - r < boardLeft then
|
||||||
|
px_rect(boardLeft - 8, ly - r, 8, r * 2 + 1, COL_WALL)
|
||||||
|
end
|
||||||
|
if lx + r > boardRight then
|
||||||
|
px_rect(boardRight, ly - r, 8, r * 2 + 1, COL_WALL)
|
||||||
|
end
|
||||||
|
-- Restore any pegs within the erased disc
|
||||||
|
for _, p in ipairs(pegs) do
|
||||||
|
local dx = lx - p.x
|
||||||
|
local dy = ly - p.y
|
||||||
|
if dx*dx + dy*dy <= (r + PEG_RADIUS + 1)^2 then
|
||||||
|
drawPeg(p)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function drawBall(bx, by)
|
||||||
|
local ix, iy = math.floor(bx), math.floor(by)
|
||||||
|
px_circle(ix + 2, iy + 2, BALL_RADIUS, 0x1A1200) -- shadow
|
||||||
|
px_circle(ix, iy, BALL_RADIUS, COL_BALL)
|
||||||
|
-- simple highlight
|
||||||
|
px_circle(ix - 3, iy - 3, math.max(2, math.floor(BALL_RADIUS * 0.35)), 0xFFF8C0)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function physicsLoop()
|
||||||
|
-- Random drop X between the leftmost and rightmost peg in the first row
|
||||||
|
-- (first row has PEG_COLS_TOP pegs; their positions were added first).
|
||||||
|
local firstRowPegs = PEG_COLS_TOP
|
||||||
|
local firstPeg = pegs[1]
|
||||||
|
local lastFirst = pegs[firstRowPegs]
|
||||||
|
local dropX = firstPeg.x + math.random() * (lastFirst.x - firstPeg.x)
|
||||||
|
local dropY = boardTop - rowSpacing * 0.6
|
||||||
|
|
||||||
|
local bx, by = dropX, dropY
|
||||||
|
local vx, vy = (math.random() - 0.5) * 40, 60 -- tiny initial nudge
|
||||||
|
|
||||||
|
local lastBx, lastBy = bx, by
|
||||||
|
local elapsed = 0
|
||||||
|
local MAX_TIME = 12.0
|
||||||
|
|
||||||
|
-- Per-peg cooldown: after hitting a peg, ignore it for a short time
|
||||||
|
-- to prevent the ball getting stuck vibrating against one peg.
|
||||||
|
local pegCooldown = {} -- peg index -> time remaining
|
||||||
|
|
||||||
|
-- Draw initial ball
|
||||||
|
drawBall(bx, by)
|
||||||
|
gpu.sync()
|
||||||
|
|
||||||
|
while elapsed < MAX_TIME do
|
||||||
|
-- Step
|
||||||
|
vy = vy + GRAVITY * DT
|
||||||
|
bx = bx + vx * DT
|
||||||
|
by = by + vy * DT
|
||||||
|
|
||||||
|
-- Clamp speed
|
||||||
|
local spd = math.sqrt(vx*vx + vy*vy)
|
||||||
|
if spd > MAX_SPEED then
|
||||||
|
local s = MAX_SPEED / spd
|
||||||
|
vx = vx * s; vy = vy * s
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Tick peg cooldowns
|
||||||
|
for k, v in pairs(pegCooldown) do
|
||||||
|
pegCooldown[k] = v - DT
|
||||||
|
if pegCooldown[k] <= 0 then pegCooldown[k] = nil end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Peg collisions (circle vs circle)
|
||||||
|
for i, p in ipairs(pegs) do
|
||||||
|
if not pegCooldown[i] then
|
||||||
|
local dx = bx - p.x
|
||||||
|
local dy = by - p.y
|
||||||
|
local distSq = dx*dx + dy*dy
|
||||||
|
if distSq < COMBINED_R * COMBINED_R and distSq > 0 then
|
||||||
|
local dist = math.sqrt(distSq)
|
||||||
|
-- Contact normal (from peg centre to ball centre)
|
||||||
|
local nx = dx / dist
|
||||||
|
local ny = dy / dist
|
||||||
|
|
||||||
|
-- Push ball out of overlap
|
||||||
|
local overlap = COMBINED_R - dist
|
||||||
|
bx = bx + nx * overlap
|
||||||
|
by = by + ny * overlap
|
||||||
|
|
||||||
|
-- Decompose velocity into normal and tangential components
|
||||||
|
local vn = vx * nx + vy * ny -- normal component (scalar)
|
||||||
|
local vnx = vn * nx
|
||||||
|
local vny = vn * ny
|
||||||
|
local vtx = vx - vnx
|
||||||
|
local vty = vy - vny
|
||||||
|
|
||||||
|
-- Reflect normal component, damp tangential (friction)
|
||||||
|
vx = -vnx * RESTITUTION + vtx * FRICTION
|
||||||
|
vy = -vny * RESTITUTION + vty * FRICTION
|
||||||
|
|
||||||
|
-- Ensure ball moves away from peg
|
||||||
|
local newVn = vx * nx + vy * ny
|
||||||
|
if newVn < 0 then
|
||||||
|
vx = vx - newVn * nx
|
||||||
|
vy = vy - newVn * ny
|
||||||
|
end
|
||||||
|
|
||||||
|
pegCooldown[i] = 0.08 -- ignore this peg for 80 ms
|
||||||
|
|
||||||
|
-- Flash peg
|
||||||
|
drawPeg(p, COL_PEG_HIT)
|
||||||
|
-- (will be restored on next erase)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Left/right wall collisions
|
||||||
|
if bx - BALL_RADIUS < boardLeft then
|
||||||
|
bx = boardLeft + BALL_RADIUS
|
||||||
|
vx = math.abs(vx) * WALL_RESTITUTION
|
||||||
|
end
|
||||||
|
if bx + BALL_RADIUS > boardRight then
|
||||||
|
bx = boardRight - BALL_RADIUS
|
||||||
|
vx = -math.abs(vx) * WALL_RESTITUTION
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Reached bucket level?
|
||||||
|
if by + BALL_RADIUS >= bucketTop then
|
||||||
|
by = bucketTop - BALL_RADIUS
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Render
|
||||||
|
eraseBall(math.floor(lastBx), math.floor(lastBy))
|
||||||
|
drawBall(bx, by)
|
||||||
|
gpu.sync()
|
||||||
|
sleep(DT)
|
||||||
|
|
||||||
|
lastBx, lastBy = bx, by
|
||||||
|
elapsed = elapsed + DT
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Final render at rest
|
||||||
|
eraseBall(math.floor(lastBx), math.floor(lastBy))
|
||||||
|
drawBall(bx, by)
|
||||||
|
gpu.sync()
|
||||||
|
sleep(0.3)
|
||||||
|
|
||||||
|
-- Erase ball
|
||||||
|
eraseBall(math.floor(bx), math.floor(by))
|
||||||
|
gpu.sync()
|
||||||
|
|
||||||
|
return bucketForX(bx)
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
-- Win flash
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
local function flashBucket(idx)
|
||||||
|
for i = 1, 8 do
|
||||||
|
drawBucket(idx, i % 2 == 1)
|
||||||
|
gpu.sync()
|
||||||
|
sleep(0.12)
|
||||||
|
end
|
||||||
|
drawBucket(idx, false)
|
||||||
|
gpu.sync()
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
-- Result banner
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
local function showResult(mult)
|
||||||
|
local msg = mult .. "x MULTIPLIER!"
|
||||||
|
local bh = 40
|
||||||
|
local by_ = math.floor(PH / 2 - bh / 2)
|
||||||
|
local marg = 40
|
||||||
|
px_rect(marg, by_, PW - marg * 2, bh, 0x000000)
|
||||||
|
px_text_centre(msg, by_ + math.floor((bh - 18) / 2), 0xFFD600, 0x000000, 2)
|
||||||
|
gpu.sync()
|
||||||
|
sleep(3)
|
||||||
|
-- Clear banner
|
||||||
|
px_rect(marg, by_, PW - marg * 2, bh, COL_BOARD)
|
||||||
|
-- Restore any pegs behind the banner
|
||||||
|
for _, p in ipairs(pegs) do
|
||||||
|
if p.y >= by_ - PEG_RADIUS and p.y <= by_ + bh + PEG_RADIUS then
|
||||||
|
drawPeg(p)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
gpu.sync()
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
-- Redstone helper
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
local function waitForRedstonePulse()
|
||||||
|
while true do
|
||||||
|
os.pullEvent("redstone")
|
||||||
|
for _, side in ipairs(redstone.getSides()) do
|
||||||
|
if redstone.getInput(side) then return end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
-- Lifecycle
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
local function start()
|
||||||
|
math.randomseed(os.epoch("utc"))
|
||||||
|
|
||||||
|
gpu = findGPU()
|
||||||
|
if not gpu then error("[plinko] No GPU peripheral found.") end
|
||||||
|
|
||||||
|
gpu.refreshSize()
|
||||||
|
sleep(0)
|
||||||
|
gpu.setSize(64)
|
||||||
|
|
||||||
|
PW, PH = gpu.getSize()
|
||||||
|
print(("[plinko] GPU %dx%d px"):format(PW, PH))
|
||||||
|
if not PW or PW < 128 or PH < 128 then
|
||||||
|
error(("GPU size %dx%d too small."):format(PW or 0, PH or 0))
|
||||||
|
end
|
||||||
|
|
||||||
|
buildBoard()
|
||||||
|
print(("[plinko] %d pegs, %d buckets"):format(#pegs, NUM_BUCKETS))
|
||||||
|
|
||||||
|
drawBoard()
|
||||||
|
gpu.sync()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function stop()
|
||||||
|
if gpu then gpu.fill(COL_BG); gpu.sync() end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function main()
|
||||||
|
while true do
|
||||||
|
waitForRedstonePulse()
|
||||||
|
|
||||||
|
-- Erase subtitle, show "dropping"
|
||||||
|
px_text_centre("Pull lever to drop", 38, COL_BG, COL_BG, 1)
|
||||||
|
px_text_centre(" DROPPING... ", 38, 0xFFD600, COL_BG, 1)
|
||||||
|
gpu.sync()
|
||||||
|
sleep(0.25)
|
||||||
|
px_text_centre(" DROPPING... ", 38, COL_BG, COL_BG, 1)
|
||||||
|
px_text_centre("Pull lever to drop", 38, 0x607080, COL_BG, 1)
|
||||||
|
gpu.sync()
|
||||||
|
|
||||||
|
-- Run physics — outcome determined by simulation
|
||||||
|
local winIdx = physicsLoop()
|
||||||
|
|
||||||
|
-- Celebrate
|
||||||
|
flashBucket(winIdx)
|
||||||
|
showResult(buckets[winIdx].mult)
|
||||||
|
|
||||||
|
-- Redraw clean board
|
||||||
|
drawBoard()
|
||||||
|
gpu.sync()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return { start = start, stop = stop, main = main }
|
||||||
Reference in New Issue
Block a user