updated
This commit is contained in:
@@ -523,6 +523,10 @@ local function start()
|
||||
buildBoard()
|
||||
print(("[plichinko] %d pegs, %d buckets"):format(#pegs, NUM_BUCKETS))
|
||||
|
||||
-- Clear full screen before drawing anything
|
||||
gpu.fill(COL_BG)
|
||||
gpu.sync()
|
||||
|
||||
drawBoard()
|
||||
gpu.sync()
|
||||
end
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
-- Prize Wheel — spinning prize wheel, perspective view
|
||||
-- Prize Wheel — spinning prize wheel, top-down view
|
||||
-- Tom's Peripherals GPU + screen wall.
|
||||
--
|
||||
-- The wheel is drawn as a perspective ellipse (top tilted away from viewer).
|
||||
-- The wheel fills the screen as a circle (top-down, like roulette).
|
||||
-- It physically rotates: angular velocity starts high and decays under
|
||||
-- friction until it stops. A fixed pointer at the top selects the prize.
|
||||
-- friction. A fixed pointer arrow at the top selects the prize.
|
||||
--
|
||||
-- Rendering approach:
|
||||
-- A point at wheel angle `a`, radius fraction `f` maps to screen as:
|
||||
-- sx = CX + f * RX * cos(a + wheelAngle)
|
||||
-- sy = WY + f * RY * sin(a + wheelAngle) (RY = RX * TILT)
|
||||
-- Wedges are rasterised row-by-row using ellipse span math.
|
||||
-- The wheel IS the thing that spins — wedges rotate each frame.
|
||||
-- Centre hub shows the current prize name when stopped.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- GPU discovery
|
||||
@@ -32,69 +29,59 @@ end
|
||||
-- Constants
|
||||
----------------------------------------------------------------------
|
||||
|
||||
local FRAME_DELAY = 0.033 -- ~30 fps
|
||||
local FRAME_DELAY = 0.033
|
||||
local TWO_PI = math.pi * 2
|
||||
|
||||
-- Perspective tilt: RY / RX. 0 = edge-on, 1 = top-down.
|
||||
-- 0.28 ≈ wheel tilted ~74° toward viewer (like a real prize wheel on a stand).
|
||||
local TILT = 0.28
|
||||
|
||||
-- Prize segments — name, colour, relative weight (wider = more likely)
|
||||
-- Prize segments — name, colour, relative weight
|
||||
local PRIZES = {
|
||||
{ name = "$100", col = 0xF44336, weight = 2 }, -- red
|
||||
{ name = "SPIN AGAIN", col = 0xFFEB3B, weight = 3 }, -- yellow
|
||||
{ name = "$500", col = 0x4CAF50, weight = 1 }, -- green
|
||||
{ name = "BANKRUPT", col = 0x212121, weight = 2 }, -- near-black
|
||||
{ name = "$250", col = 0x2196F3, weight = 2 }, -- blue
|
||||
{ name = "$50", col = 0xFF9800, weight = 3 }, -- orange
|
||||
{ name = "JACKPOT", col = 0xE91E63, weight = 1 }, -- pink
|
||||
{ name = "$150", col = 0x9C27B0, weight = 2 }, -- purple
|
||||
{ name = "LOSE TURN", col = 0x607D8B, weight = 2 }, -- grey-blue
|
||||
{ name = "$75", col = 0x00BCD4, weight = 3 }, -- cyan
|
||||
{ name = "$1000", col = 0x8BC34A, weight = 1 }, -- lime
|
||||
{ name = "$25", col = 0xFF5722, weight = 3 }, -- deep orange
|
||||
{ name = "$100", col = 0xF44336, weight = 2 },
|
||||
{ name = "SPIN AGAIN", col = 0xFFEB3B, weight = 3 },
|
||||
{ name = "$500", col = 0x4CAF50, weight = 1 },
|
||||
{ name = "BANKRUPT", col = 0x212121, weight = 2 },
|
||||
{ name = "$250", col = 0x2196F3, weight = 2 },
|
||||
{ name = "$50", col = 0xFF9800, weight = 3 },
|
||||
{ name = "JACKPOT", col = 0xE91E63, weight = 1 },
|
||||
{ name = "$150", col = 0x9C27B0, weight = 2 },
|
||||
{ name = "LOSE TURN", col = 0x607D8B, weight = 2 },
|
||||
{ name = "$75", col = 0x00BCD4, weight = 3 },
|
||||
{ name = "$1000", col = 0x8BC34A, weight = 1 },
|
||||
{ name = "$25", col = 0xFF5722, weight = 3 },
|
||||
}
|
||||
|
||||
-- Build cumulative angle table from weights
|
||||
local function buildSegments()
|
||||
local total = 0
|
||||
for _, p in ipairs(PRIZES) do total = total + p.weight end
|
||||
local segs = {}
|
||||
local cumAngle = 0
|
||||
local cum = 0
|
||||
for i, p in ipairs(PRIZES) do
|
||||
local arc = (p.weight / total) * TWO_PI
|
||||
segs[i] = {
|
||||
name = p.name,
|
||||
col = p.col,
|
||||
startA = cumAngle,
|
||||
endA = cumAngle + arc,
|
||||
midA = cumAngle + arc / 2,
|
||||
name = p.name,
|
||||
col = p.col,
|
||||
startA = cum,
|
||||
endA = cum + arc,
|
||||
midA = cum + arc / 2,
|
||||
}
|
||||
cumAngle = cumAngle + arc
|
||||
cum = cum + arc
|
||||
end
|
||||
return segs
|
||||
end
|
||||
|
||||
-- Physics spin constants
|
||||
local OMEGA_MIN = 4.0 -- rad/s minimum starting spin
|
||||
local OMEGA_MAX = 10.0 -- rad/s maximum starting spin
|
||||
local FRICTION = 0.987 -- angular velocity multiplier per frame
|
||||
local STOP_OMEGA = 0.04 -- rad/s below this we consider it stopped
|
||||
-- Spin physics
|
||||
local OMEGA_MIN = 4.0 -- rad/s
|
||||
local OMEGA_MAX = 10.0
|
||||
local FRICTION = 0.987 -- multiplier per frame
|
||||
local STOP_OMEGA = 0.04 -- rad/s
|
||||
|
||||
-- Colours
|
||||
local COL_BG = 0x050505
|
||||
local COL_RIM = 0x8B6914 -- gold rim
|
||||
local COL_RIM_DARK = 0x5C4400
|
||||
local COL_SPOKE = 0xB8860B
|
||||
local COL_HUB = 0x333333
|
||||
local COL_HUB_RING = 0x8B6914
|
||||
local COL_SEP = 0xFFFFFF -- wedge separator lines
|
||||
local COL_STAND = 0x5D4037 -- wood brown
|
||||
local COL_STAND_DRK = 0x3E2723
|
||||
local COL_POINTER = 0xF5F5F5
|
||||
local COL_POINTER_S = 0x444444
|
||||
local COL_WHITE = 0xFFFFFF
|
||||
local COL_GLOW = 0xFFD600 -- winner highlight
|
||||
local COL_BG = 0x050505
|
||||
local COL_RIM = 0x8B6914
|
||||
local COL_HUB = 0x1A1A1A
|
||||
local COL_HUB_RING = 0x8B6914
|
||||
local COL_SEP = 0xE0E0E0
|
||||
local COL_WHITE = 0xFFFFFF
|
||||
local COL_POINTER = 0xF5F5F5
|
||||
local COL_PTR_SHD = 0x444444
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- GPU / pixel primitives
|
||||
@@ -102,10 +89,7 @@ local COL_GLOW = 0xFFD600 -- winner highlight
|
||||
|
||||
local gpu
|
||||
local PW, PH
|
||||
|
||||
-- Screen geometry (set in start())
|
||||
local CX, WY -- wheel centre x, wheel centre y
|
||||
local RX, RY -- wheel half-width, half-height (perspective)
|
||||
local CX, CY, R_OUTER, R_POCKET_IN
|
||||
|
||||
local function px_rect(x, y, w, h, col)
|
||||
x = math.floor(x); y = math.floor(y)
|
||||
@@ -118,6 +102,29 @@ local function px_rect(x, y, w, h, col)
|
||||
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_annulus(cx, cy, r1, r2, col)
|
||||
cx = math.floor(cx); cy = math.floor(cy)
|
||||
r1 = math.floor(r1); r2 = math.floor(r2)
|
||||
for dy = -r2, r2 do
|
||||
local ho = math.floor(math.sqrt(math.max(0, r2*r2 - dy*dy)) + 0.5)
|
||||
local hi = math.floor(math.sqrt(math.max(0, r1*r1 - dy*dy)) + 0.5)
|
||||
if ho > hi then
|
||||
px_rect(cx - ho, cy + dy, ho - hi, 1, col)
|
||||
px_rect(cx + hi, cy + dy, ho - hi + 1, 1, col)
|
||||
elseif hi == 0 then
|
||||
px_rect(cx - ho, cy + dy, ho*2 + 1, 1, col)
|
||||
end
|
||||
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
|
||||
@@ -128,57 +135,12 @@ local function px_text_centre(str, y, fg, bg, size)
|
||||
px_text(str, CX - math.floor(w / 2), y, fg, bg, size)
|
||||
end
|
||||
|
||||
-- Filled ellipse
|
||||
local function px_ellipse(cx, cy, rx, ry, col)
|
||||
cx = math.floor(cx); cy = math.floor(cy)
|
||||
rx = math.floor(rx); ry = math.floor(ry)
|
||||
if rx < 1 or ry < 1 then return end
|
||||
for dy = -ry, ry do
|
||||
local t = dy / ry
|
||||
local half = math.floor(rx * math.sqrt(math.max(0, 1 - t*t)) + 0.5)
|
||||
if half >= 1 then
|
||||
px_rect(cx - half, cy + dy, half * 2 + 1, 1, col)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Ellipse annulus
|
||||
local function px_ellipse_annulus(cx, cy, rx1, ry1, rx2, ry2, col)
|
||||
cx = math.floor(cx); cy = math.floor(cy)
|
||||
for dy = -ry2, ry2 do
|
||||
local t2 = dy / ry2
|
||||
local ho = math.floor(rx2 * math.sqrt(math.max(0, 1 - t2*t2)) + 0.5)
|
||||
local hi = 0
|
||||
if math.abs(dy) <= ry1 then
|
||||
local t1 = dy / ry1
|
||||
hi = math.floor(rx1 * math.sqrt(math.max(0, 1 - t1*t1)) + 0.5)
|
||||
end
|
||||
if ho > hi then
|
||||
px_rect(cx - ho, cy + dy, ho - hi, 1, col)
|
||||
px_rect(cx + hi, cy + dy, ho - hi + 1, 1, col)
|
||||
elseif hi == 0 then
|
||||
px_rect(cx - ho, cy + dy, ho*2 + 1, 1, col)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- Wheel drawing
|
||||
----------------------------------------------------------------------
|
||||
|
||||
local segments = {}
|
||||
|
||||
-- Convert wheel-local polar (angle, radius fraction) → screen (sx, sy)
|
||||
-- wheelAngle is the current rotation offset
|
||||
local function wheelToScreen(a, f, wheelAngle)
|
||||
local wa = a + wheelAngle
|
||||
return CX + f * RX * math.cos(wa),
|
||||
WY + f * RY * math.sin(wa)
|
||||
end
|
||||
|
||||
-- Draw a single wedge of the perspective ellipse.
|
||||
-- seg.startA / seg.endA are in wheel-local angles.
|
||||
-- wheelAngle rotates the whole wheel.
|
||||
local function drawWedge(seg, wheelAngle, glowing)
|
||||
local col = seg.col
|
||||
if glowing then
|
||||
@@ -188,27 +150,24 @@ local function drawWedge(seg, wheelAngle, glowing)
|
||||
col = r * 0x10000 + g * 0x100 + b
|
||||
end
|
||||
|
||||
-- Rasterise the wedge by scanning screen rows within the ellipse bounding box.
|
||||
local by0 = math.floor(WY - RY) - 1
|
||||
local by1 = math.ceil (WY + RY) + 1
|
||||
local bx0 = math.floor(CX - RX) - 1
|
||||
local bx1 = math.ceil (CX + RX) + 1
|
||||
local ri = R_POCKET_IN
|
||||
local ro = R_OUTER
|
||||
local a0 = seg.startA + wheelAngle
|
||||
local arc = seg.endA - seg.startA
|
||||
|
||||
local a0 = seg.startA + wheelAngle
|
||||
local a1 = seg.endA + wheelAngle
|
||||
local arc = seg.endA - seg.startA -- always positive
|
||||
local bx0 = math.floor(CX - ro) - 1
|
||||
local bx1 = math.ceil (CX + ro) + 1
|
||||
local by0 = math.floor(CY - ro) - 1
|
||||
local by1 = math.ceil (CY + ro) + 1
|
||||
|
||||
for sy = by0, by1 do
|
||||
local dy = sy - WY
|
||||
-- ellipse x half-span at this row
|
||||
if math.abs(dy) <= RY then
|
||||
local t = dy / RY
|
||||
local xhalf = RX * math.sqrt(math.max(0, 1 - t*t))
|
||||
local runStart = nil
|
||||
for sx = math.floor(CX - xhalf), math.ceil(CX + xhalf) do
|
||||
local dx = sx - CX
|
||||
local angle = math.atan2(dy / RY, dx / RX) -- ellipse-normalised angle
|
||||
local rel = (angle - a0) % TWO_PI
|
||||
local dy = sy - CY
|
||||
local runStart = nil
|
||||
for sx = bx0, bx1 do
|
||||
local dx = sx - CX
|
||||
local dist = math.sqrt(dx*dx + dy*dy)
|
||||
if dist >= ri and dist <= ro then
|
||||
local rel = (math.atan2(dy, dx) - a0) % TWO_PI
|
||||
if rel <= arc then
|
||||
if not runStart then runStart = sx end
|
||||
else
|
||||
@@ -217,29 +176,33 @@ local function drawWedge(seg, wheelAngle, glowing)
|
||||
runStart = nil
|
||||
end
|
||||
end
|
||||
else
|
||||
if runStart then
|
||||
px_rect(runStart, sy, sx - runStart, 1, col)
|
||||
runStart = nil
|
||||
end
|
||||
end
|
||||
if runStart then
|
||||
px_rect(runStart, sy, math.ceil(CX + xhalf) - runStart + 1, 1, col)
|
||||
end
|
||||
end
|
||||
if runStart then
|
||||
px_rect(runStart, sy, bx1 - runStart + 1, 1, col)
|
||||
end
|
||||
end
|
||||
|
||||
-- Separator line at startA edge
|
||||
local steps = math.floor(RX)
|
||||
for i = 0, steps do
|
||||
local f = i / steps
|
||||
local sx = CX + f * RX * math.cos(a0)
|
||||
local sy = WY + f * RY * math.sin(a0)
|
||||
-- Separator spoke at startA
|
||||
local a0w = seg.startA + wheelAngle
|
||||
for i = 0, math.floor(ro - ri) do
|
||||
local r = ri + i
|
||||
local sx = CX + math.cos(a0w) * r
|
||||
local sy = CY + math.sin(a0w) * r
|
||||
px_rect(math.floor(sx), math.floor(sy), 2, 1, COL_SEP)
|
||||
end
|
||||
|
||||
-- Label at wedge midpoint, ~70% radius
|
||||
local midA = seg.midA + wheelAngle
|
||||
local lx = CX + 0.70 * RX * math.cos(midA)
|
||||
local ly = WY + 0.70 * RY * math.sin(midA)
|
||||
local label = seg.name
|
||||
local lsize = (RX > 80) and 1 or 1
|
||||
px_text(label, lx - math.floor(#label * 3), ly - 4, COL_WHITE, col, lsize)
|
||||
-- Label at 72% radius, wedge midpoint
|
||||
local midA = seg.midA + wheelAngle
|
||||
local lr = (ri + ro) * 0.72
|
||||
local lx = CX + math.cos(midA) * lr
|
||||
local ly = CY + math.sin(midA) * lr
|
||||
px_text(seg.name, lx - math.floor(#seg.name * 3), ly - 4, COL_WHITE, col, 1)
|
||||
end
|
||||
|
||||
local function drawAllWedges(wheelAngle, glowIdx)
|
||||
@@ -249,129 +212,88 @@ local function drawAllWedges(wheelAngle, glowIdx)
|
||||
end
|
||||
end
|
||||
|
||||
local function drawChrome(wheelAngle)
|
||||
-- Outer rim (ellipse annulus, slightly larger than wheel)
|
||||
local rimRX = RX + 6; local rimRY = RY + math.floor(6 * TILT)
|
||||
px_ellipse_annulus(CX, WY, RX, RY, rimRX, rimRY, COL_RIM)
|
||||
-- Inner hub
|
||||
local hubRX = math.floor(RX * 0.10)
|
||||
local hubRY = math.floor(RY * 0.10)
|
||||
px_ellipse(CX, WY, hubRX + 3, hubRY + 3, COL_HUB_RING)
|
||||
px_ellipse(CX, WY, hubRX, hubRY, COL_HUB)
|
||||
local function drawChrome()
|
||||
px_annulus(CX, CY, R_OUTER, R_OUTER + 7, COL_RIM)
|
||||
-- Hub
|
||||
px_circle(CX, CY, R_POCKET_IN, COL_HUB)
|
||||
px_annulus(CX, CY, R_POCKET_IN - 4, R_POCKET_IN, COL_HUB_RING)
|
||||
px_circle(CX, CY, 6, COL_HUB_RING)
|
||||
px_circle(CX, CY, 3, COL_HUB)
|
||||
end
|
||||
|
||||
local function drawStand()
|
||||
-- Two angled legs below the wheel
|
||||
local baseY = WY + RY + 6
|
||||
local legBot = PH - 4
|
||||
local legW = 8
|
||||
-- Left leg
|
||||
local lx1 = CX - math.floor(RX * 0.3)
|
||||
local lx2 = CX - math.floor(RX * 0.7)
|
||||
-- Draw as a trapezoid approximation with filled rects
|
||||
local steps = legBot - baseY
|
||||
for i = 0, steps do
|
||||
local frac = i / math.max(1, steps)
|
||||
local cx_l = lx1 + math.floor((lx2 - lx1) * frac)
|
||||
px_rect(cx_l - math.floor(legW/2), baseY + i, legW, 1, COL_STAND)
|
||||
end
|
||||
-- Right leg
|
||||
local rx1 = CX + math.floor(RX * 0.3)
|
||||
local rx2 = CX + math.floor(RX * 0.7)
|
||||
for i = 0, steps do
|
||||
local frac = i / math.max(1, steps)
|
||||
local cx_r = rx1 + math.floor((rx2 - rx1) * frac)
|
||||
px_rect(cx_r - math.floor(legW/2), baseY + i, legW, 1, COL_STAND)
|
||||
end
|
||||
-- Horizontal crossbar
|
||||
local barY = baseY + math.floor(steps * 0.55)
|
||||
px_rect(lx2 - math.floor(legW/2), barY, rx2 - lx2 + legW, 6, COL_STAND_DRK)
|
||||
end
|
||||
|
||||
-- Fixed pointer triangle at the top of the wheel (screen top, pointing down)
|
||||
-- Fixed downward-pointing triangle at top of screen, above wheel
|
||||
local function drawPointer()
|
||||
local tipX = CX
|
||||
local tipY = WY - RY - 5 -- just above the rim
|
||||
local baseY = tipY - 18
|
||||
local halfW = 10
|
||||
-- Shadow
|
||||
for i = 0, 18 do
|
||||
local frac = i / 18
|
||||
local tipY = CY - R_OUTER - 8
|
||||
local baseY = tipY - 20
|
||||
local halfW = 11
|
||||
for i = 0, 20 do
|
||||
local frac = i / 20
|
||||
local hw = math.floor(halfW * (1 - frac)) + 1
|
||||
px_rect(tipX - hw + 2, baseY + i + 2, hw*2, 1, COL_POINTER_S)
|
||||
px_rect(tipX - hw + 2, baseY + i + 2, hw*2, 1, COL_PTR_SHD)
|
||||
end
|
||||
-- Arrow
|
||||
for i = 0, 18 do
|
||||
local frac = i / 18
|
||||
for i = 0, 20 do
|
||||
local frac = i / 20
|
||||
local hw = math.floor(halfW * (1 - frac)) + 1
|
||||
px_rect(tipX - hw, baseY + i, hw*2, 1, COL_POINTER)
|
||||
end
|
||||
end
|
||||
|
||||
local function drawHubText(lines)
|
||||
local r = R_POCKET_IN - 5
|
||||
px_circle(CX, CY, r, COL_HUB)
|
||||
local lineH = 12
|
||||
local totalH = #lines * lineH
|
||||
local startY = CY - math.floor(totalH / 2)
|
||||
for i, line in ipairs(lines) do
|
||||
local lx = CX - math.floor(#line * 6 / 2)
|
||||
px_text(line, lx, startY + (i-1) * lineH, COL_WHITE, COL_HUB, 1)
|
||||
end
|
||||
px_annulus(CX, CY, R_POCKET_IN - 4, R_POCKET_IN, COL_HUB_RING)
|
||||
gpu.sync()
|
||||
end
|
||||
|
||||
local function drawWheelFull(wheelAngle, glowIdx)
|
||||
-- Clear wheel area
|
||||
local rimRX = RX + 8; local rimRY = RY + math.floor(8 * TILT)
|
||||
px_ellipse(CX, WY, rimRX, rimRY, COL_BG)
|
||||
px_circle(CX, CY, R_OUTER + 9, COL_BG)
|
||||
drawAllWedges(wheelAngle, glowIdx)
|
||||
drawChrome(wheelAngle)
|
||||
drawChrome()
|
||||
drawPointer()
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- Center / result text (drawn below wheel, above stand)
|
||||
-- Segment under pointer
|
||||
-- Pointer is at screen top = angle -pi/2.
|
||||
-- Wheel-local angle = (-pi/2 - wheelAngle) mod TWO_PI
|
||||
----------------------------------------------------------------------
|
||||
|
||||
local function drawResultText(lines)
|
||||
local y0 = WY + RY + math.floor(RY * 0.15)
|
||||
-- Clear area
|
||||
px_rect(1, y0, PW, 30, COL_BG)
|
||||
for i, line in ipairs(lines) do
|
||||
px_text_centre(line, y0 + (i-1) * 14, COL_WHITE, COL_BG, 1)
|
||||
local function segmentAtPointer(wheelAngle)
|
||||
local a = (-math.pi / 2 - wheelAngle) % TWO_PI
|
||||
for i, seg in ipairs(segments) do
|
||||
if a >= seg.startA and a < seg.endA then return i end
|
||||
end
|
||||
gpu.sync()
|
||||
return 1
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- Spin physics
|
||||
----------------------------------------------------------------------
|
||||
|
||||
-- Find which segment index is currently under the pointer.
|
||||
-- The pointer is at the top of the wheel = screen angle -pi/2.
|
||||
-- In wheel-local space that is angle (-pi/2 - wheelAngle) mod TWO_PI.
|
||||
local function segmentAtPointer(wheelAngle)
|
||||
local pointerLocalAngle = (-math.pi / 2 - wheelAngle) % TWO_PI
|
||||
for i, seg in ipairs(segments) do
|
||||
if pointerLocalAngle >= seg.startA and pointerLocalAngle < seg.endA then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
local function spin()
|
||||
local omega = OMEGA_MIN + math.random() * (OMEGA_MAX - OMEGA_MIN)
|
||||
local angle = math.random() * TWO_PI -- random start rotation
|
||||
local elapsed = 0
|
||||
local MAX_TIME = 30.0
|
||||
local omega = OMEGA_MIN + math.random() * (OMEGA_MAX - OMEGA_MIN)
|
||||
local angle = math.random() * TWO_PI
|
||||
local elapsed = 0
|
||||
|
||||
-- Draw wheel at initial angle
|
||||
drawWheelFull(angle, nil)
|
||||
gpu.sync()
|
||||
drawHubText({ "SPINNING..." })
|
||||
|
||||
while elapsed < MAX_TIME do
|
||||
-- Decay angular velocity
|
||||
while elapsed < 30.0 do
|
||||
omega = omega * FRICTION
|
||||
|
||||
-- Integrate angle
|
||||
angle = angle + omega * FRAME_DELAY
|
||||
-- Keep angle in [0, TWO_PI) to avoid float drift over long spins
|
||||
if angle > TWO_PI * 100 then angle = angle % TWO_PI end
|
||||
|
||||
-- Erase wheel, redraw at new angle
|
||||
local rimRX = RX + 8; local rimRY = RY + math.floor(8 * TILT)
|
||||
px_ellipse(CX, WY, rimRX, rimRY, COL_BG)
|
||||
px_circle(CX, CY, R_OUTER + 9, COL_BG)
|
||||
drawAllWedges(angle, nil)
|
||||
drawChrome(angle)
|
||||
drawChrome()
|
||||
drawPointer()
|
||||
gpu.sync()
|
||||
|
||||
@@ -381,21 +303,19 @@ local function spin()
|
||||
if omega < STOP_OMEGA then break end
|
||||
end
|
||||
|
||||
-- Final angle
|
||||
local winIdx = segmentAtPointer(angle)
|
||||
|
||||
-- Glow animation
|
||||
-- Flash winning wedge
|
||||
for flash = 1, 7 do
|
||||
local rimRX = RX + 8; local rimRY = RY + math.floor(8 * TILT)
|
||||
px_ellipse(CX, WY, rimRX, rimRY, COL_BG)
|
||||
px_circle(CX, CY, R_OUTER + 9, COL_BG)
|
||||
drawAllWedges(angle, flash % 2 == 1 and winIdx or nil)
|
||||
drawChrome(angle)
|
||||
drawChrome()
|
||||
drawPointer()
|
||||
gpu.sync()
|
||||
sleep(0.14)
|
||||
end
|
||||
|
||||
return winIdx
|
||||
return winIdx, angle
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
@@ -431,18 +351,20 @@ local function start()
|
||||
error(("GPU pixel size %dx%d too small."):format(PW or 0, PH or 0))
|
||||
end
|
||||
|
||||
-- Geometry: wheel sits in the upper ~60% of the screen
|
||||
CX = math.floor(PW / 2)
|
||||
RX = math.floor(math.min(PW, PH) / 2) - 12
|
||||
RY = math.floor(RX * TILT)
|
||||
WY = math.floor(PH * 0.38) -- wheel centre sits above mid
|
||||
CX = math.floor(PW / 2)
|
||||
CY = math.floor(PH / 2)
|
||||
local R_MAX = math.floor(math.min(PW, PH) / 2) - 16
|
||||
R_OUTER = R_MAX
|
||||
R_POCKET_IN = math.floor(R_MAX * 0.28)
|
||||
|
||||
segments = buildSegments()
|
||||
|
||||
-- Clear full screen before drawing anything
|
||||
gpu.fill(COL_BG)
|
||||
drawStand()
|
||||
gpu.sync()
|
||||
|
||||
drawWheelFull(0, nil)
|
||||
drawResultText({ "Pull lever to spin!" })
|
||||
drawHubText({ "PRIZE WHEEL", "Pull lever" })
|
||||
end
|
||||
|
||||
local function stop()
|
||||
@@ -453,20 +375,17 @@ local function main()
|
||||
while true do
|
||||
waitForRedstonePulse()
|
||||
|
||||
drawResultText({ "SPINNING..." })
|
||||
local winIdx, finalAngle = spin()
|
||||
local prize = segments[winIdx]
|
||||
|
||||
local winIdx = spin()
|
||||
local prize = segments[winIdx]
|
||||
|
||||
drawResultText({ "WINNER!", prize.name })
|
||||
drawHubText({ "WINNER!", prize.name })
|
||||
|
||||
sleep(5)
|
||||
|
||||
-- Redraw clean with a fresh random idle angle
|
||||
gpu.fill(COL_BG)
|
||||
drawStand()
|
||||
drawWheelFull(math.random() * TWO_PI, nil)
|
||||
drawResultText({ "Pull lever to spin!" })
|
||||
gpu.sync()
|
||||
drawWheelFull(finalAngle, nil)
|
||||
drawHubText({ "PRIZE WHEEL", "Pull lever" })
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -581,7 +581,10 @@ local function start()
|
||||
R_POCKET_IN = math.floor(R_MAX * 0.58)
|
||||
R_HUB = math.floor(R_MAX * 0.38)
|
||||
|
||||
-- Clear full screen before drawing anything
|
||||
gpu.fill(COL_BG)
|
||||
gpu.sync()
|
||||
|
||||
drawWheelFull(nil)
|
||||
drawCenterText({ "ROULETTE", "Pull lever" })
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user