restore: clean roulette implementation with dynamic GPU size
This commit is contained in:
@@ -1,158 +1,71 @@
|
|||||||
-- 3D rotating cube (temporary test - roulette code commented out below)
|
|
||||||
print("Scanning peripherals...")
|
|
||||||
for _, name in ipairs(peripheral.getNames()) do
|
|
||||||
print(name .. " = " .. peripheral.getType(name))
|
|
||||||
end
|
|
||||||
|
|
||||||
local tris = {
|
|
||||||
-- SOUTH
|
|
||||||
{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0 },
|
|
||||||
{ 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0 },
|
|
||||||
-- NORTH
|
|
||||||
{ 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0 },
|
|
||||||
{ 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0 },
|
|
||||||
-- EAST
|
|
||||||
{ 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0 },
|
|
||||||
{ 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0 },
|
|
||||||
-- WEST
|
|
||||||
{ 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0 },
|
|
||||||
{ 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
|
|
||||||
-- TOP
|
|
||||||
{ 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0 },
|
|
||||||
{ 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0 },
|
|
||||||
-- BOTTOM
|
|
||||||
{ 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
|
|
||||||
{ 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
|
|
||||||
}
|
|
||||||
|
|
||||||
local gpu
|
|
||||||
for _, name in ipairs(peripheral.getNames()) do
|
|
||||||
local t = peripheral.getType(name)
|
|
||||||
if t and t:find("gpu") then
|
|
||||||
gpu = peripheral.wrap(name)
|
|
||||||
print("Found GPU: " .. name)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not gpu then error("No GPU peripheral found.") end
|
|
||||||
gpu.refreshSize()
|
|
||||||
gpu.setSize(64)
|
|
||||||
local pw, ph, bx, by, sb = gpu.getSize()
|
|
||||||
print(("GPU pixel size: %sx%s blocks: %sx%s per-block: %s"):format(tostring(pw), tostring(ph), tostring(bx), tostring(by), tostring(sb)))
|
|
||||||
local gl = gpu.createWindow3D(1, 1, pw, ph)
|
|
||||||
gl.glFrustum(90, 0.1, 1000)
|
|
||||||
gl.glDirLight(0, 0, -1)
|
|
||||||
local rot = 0
|
|
||||||
while true do
|
|
||||||
gl.clear()
|
|
||||||
gl.glDisable(0xDE1)
|
|
||||||
gl.glTranslate(0, 1, 3)
|
|
||||||
gl.glRotate(rot, 0, 1, 0)
|
|
||||||
gl.glRotate(rot, 0, 0, 1)
|
|
||||||
rot = rot + 3
|
|
||||||
gl.glBegin()
|
|
||||||
for k, v in pairs(tris) do
|
|
||||||
local ci = math.floor((k - 1) / 2)
|
|
||||||
gl.glVertex(v[1], v[2], v[3])
|
|
||||||
local cv = 255
|
|
||||||
if ci % 2 == 0 then cv = 127 end
|
|
||||||
if math.floor(ci / 2) == 0 then
|
|
||||||
gl.glColor(cv, 0, 0)
|
|
||||||
elseif math.floor(ci / 2) == 1 then
|
|
||||||
gl.glColor(0, cv, 0)
|
|
||||||
else
|
|
||||||
gl.glColor(0, 0, cv)
|
|
||||||
end
|
|
||||||
gl.glVertex(v[4], v[5], v[6])
|
|
||||||
gl.glVertex(v[7], v[8], v[9])
|
|
||||||
end
|
|
||||||
gl.glEnd()
|
|
||||||
gl.render()
|
|
||||||
gl.sync()
|
|
||||||
gpu.sync()
|
|
||||||
sleep(0.01)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- =============================================================================
|
|
||||||
-- ROULETTE CODE (commented out)
|
|
||||||
-- =============================================================================
|
|
||||||
--[[
|
|
||||||
|
|
||||||
-- Roulette Machine
|
-- Roulette Machine
|
||||||
-- Designed for Tom's Peripherals GPU + screen blocks (multi-screen wall).
|
-- Designed for Tom's Peripherals GPU + screen blocks (multi-screen wall).
|
||||||
-- Falls back to vanilla CC:Tweaked monitor if no GPU is attached.
|
|
||||||
--
|
--
|
||||||
-- Behaviour:
|
-- Behaviour:
|
||||||
-- * On boot, sizes itself to the full screen surface.
|
-- * On boot, discovers the GPU peripheral by scanning all attached peripherals.
|
||||||
|
-- * Calls refreshSize() + setSize(64) to bind the full monitor wall.
|
||||||
-- * Draws a red/black/green pocket ring around the perimeter.
|
-- * Draws a red/black/green pocket ring around the perimeter.
|
||||||
-- * Waits for a redstone signal on any side, then spins the "ball"
|
-- * Waits for a redstone signal on any side, then spins the "ball"
|
||||||
-- around the ring with random duration and ease-out deceleration,
|
-- around the ring with random duration and ease-out deceleration,
|
||||||
-- finally landing on a uniformly random pocket.
|
-- finally landing on a uniformly random pocket.
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
-- Backend abstraction (GPU vs vanilla monitor)
|
-- GPU discovery
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
-- All drawing goes through a small `gfx` table with these methods:
|
|
||||||
-- gfx.size() -> width, height in cells/pixels
|
|
||||||
-- gfx.fillRect(x, y, w, h, col) -- col is a backend-specific color
|
|
||||||
-- gfx.text(x, y, str, fg, bg)
|
|
||||||
-- gfx.clear(col)
|
|
||||||
-- gfx.sync() -- present frame (no-op for monitor)
|
|
||||||
-- gfx.colors -- table with .red .black .green .white .bg
|
|
||||||
-- gfx.cellSize -- pixel size of one "pocket cell"
|
|
||||||
|
|
||||||
local gfx
|
|
||||||
|
|
||||||
local function findGPU()
|
local function findGPU()
|
||||||
-- Tom's Peripherals registers GPUs as tm_gpu_0, tm_gpu_1, ... so
|
print("[roulette] Scanning peripherals...")
|
||||||
-- scan all peripherals for any type that starts with "tm_gpu" or
|
for _, name in ipairs(peripheral.getNames()) do
|
||||||
-- exposes the GPU API surface.
|
local t = peripheral.getType(name)
|
||||||
for _, side in ipairs(peripheral.getNames()) do
|
print(" " .. name .. " = " .. tostring(t))
|
||||||
local t = peripheral.getType(side)
|
if t and t:find("gpu") then
|
||||||
if t and t:sub(1, 6) == "tm_gpu" then
|
print("[roulette] Using GPU: " .. name)
|
||||||
return peripheral.wrap(side), t
|
return peripheral.wrap(name)
|
||||||
end
|
|
||||||
end
|
|
||||||
for _, side in ipairs(peripheral.getNames()) do
|
|
||||||
local p = peripheral.wrap(side)
|
|
||||||
if p and p.refreshSize and p.filledRectangle and p.sync then
|
|
||||||
return p, peripheral.getType(side)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local function makeGpuBackend(gpu)
|
----------------------------------------------------------------------
|
||||||
-- refreshSize() schedules a server-thread monitor scan asynchronously.
|
-- Backend
|
||||||
-- sleep(0) yields back to the server, giving it a tick to finish.
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
local gfx
|
||||||
|
|
||||||
|
local function initBackend()
|
||||||
|
local gpu = findGPU()
|
||||||
|
if not gpu then
|
||||||
|
error("No GPU peripheral found. Attach a Tom's Peripherals GPU adjacent to the monitor wall.")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Let the server tick resolve the monitor wall geometry before setSize.
|
||||||
gpu.refreshSize()
|
gpu.refreshSize()
|
||||||
sleep(0)
|
sleep(0)
|
||||||
gpu.setSize(64)
|
gpu.setSize(64)
|
||||||
|
|
||||||
|
-- getSize() -> pixelW, pixelH, blocksX, blocksY, sizePerBlock
|
||||||
local pw, ph, bx, by, sb = gpu.getSize()
|
local pw, ph, bx, by, sb = gpu.getSize()
|
||||||
print(("[roulette] GPU pixel size: %sx%s blocks: %sx%s per-block: %s")
|
print(("[roulette] GPU: %dx%d px | %dx%d blocks | %d px/block")
|
||||||
:format(tostring(pw), tostring(ph), tostring(bx), tostring(by), tostring(sb)))
|
:format(pw, ph, bx or 0, by or 0, sb or 0))
|
||||||
|
|
||||||
if not pw or not ph or pw < 8 or ph < 8 then
|
if not pw or pw < 8 or ph < 8 then
|
||||||
error(("GPU reports unusable pixel size %sx%s.")
|
error(("GPU pixel size %dx%d is too small. Is the monitor wall placed and facing correctly?")
|
||||||
:format(tostring(pw), tostring(ph)))
|
:format(pw or 0, ph or 0))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Pick a cell size that gives at least 16x16 cells, capped at 16 px.
|
-- Cell size: target at least 16 cells on the short axis, max 16 px each.
|
||||||
local cell = math.max(2, math.min(16, math.floor(math.min(pw, ph) / 16)))
|
local cell = math.max(2, math.min(16, math.floor(math.min(pw, ph) / 16)))
|
||||||
print(("[roulette] Using cell size: %d px -> %dx%d cells"):format(
|
local cw = math.floor(pw / cell)
|
||||||
cell, math.floor(pw / cell), math.floor(ph / cell)))
|
local ch = math.floor(ph / cell)
|
||||||
|
print(("[roulette] Cell size: %d px -> grid: %dx%d cells"):format(cell, cw, ch))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
kind = "gpu",
|
kind = "gpu",
|
||||||
cellSize = cell,
|
cellSize = cell,
|
||||||
pixelW = pw,
|
pixelW = pw,
|
||||||
pixelH = ph,
|
pixelH = ph,
|
||||||
size = function()
|
size = function() return cw, ch end,
|
||||||
return math.floor(pw / cell), math.floor(ph / cell)
|
|
||||||
end,
|
|
||||||
fillRect = function(x, y, w, h, col)
|
fillRect = function(x, y, w, h, col)
|
||||||
-- 1-indexed pixel coords, per gpuDraw.lua example.
|
|
||||||
local px = (x - 1) * cell + 1
|
local px = (x - 1) * cell + 1
|
||||||
local py = (y - 1) * cell + 1
|
local py = (y - 1) * cell + 1
|
||||||
gpu.filledRectangle(px, py, w * cell, h * cell, col)
|
gpu.filledRectangle(px, py, w * cell, h * cell, col)
|
||||||
@@ -160,15 +73,12 @@ local function makeGpuBackend(gpu)
|
|||||||
text = function(x, y, str, fg, bg)
|
text = function(x, y, str, fg, bg)
|
||||||
local px = (x - 1) * cell + 1
|
local px = (x - 1) * cell + 1
|
||||||
local py = (y - 1) * cell + 1
|
local py = (y - 1) * cell + 1
|
||||||
-- drawText(x, y, text, textColor, bgColor, size, padding)
|
pcall(gpu.drawText, px, py, str, fg or 0xFFFFFF, bg or 0x000000, 1, 0)
|
||||||
pcall(gpu.drawText, px, py, str, fg, bg, 1, 0)
|
|
||||||
end,
|
end,
|
||||||
clear = function(col)
|
clear = function(col)
|
||||||
if col then gpu.fill(col) else gpu.fill() end
|
if col then gpu.fill(col) else gpu.fill() end
|
||||||
end,
|
end,
|
||||||
sync = function()
|
sync = function() gpu.sync() end,
|
||||||
gpu.sync()
|
|
||||||
end,
|
|
||||||
colors = {
|
colors = {
|
||||||
red = 0xE53935,
|
red = 0xE53935,
|
||||||
black = 0x101010,
|
black = 0x101010,
|
||||||
@@ -179,58 +89,12 @@ local function makeGpuBackend(gpu)
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
local function makeMonitorBackend()
|
|
||||||
local mon = peripheral.find("monitor")
|
|
||||||
if not mon then return nil end
|
|
||||||
mon.setTextScale(0.5)
|
|
||||||
return {
|
|
||||||
kind = "monitor",
|
|
||||||
cellSize = 1,
|
|
||||||
size = function() return mon.getSize() end,
|
|
||||||
fillRect = function(x, y, w, h, col)
|
|
||||||
mon.setBackgroundColor(col)
|
|
||||||
for dy = 0, h - 1 do
|
|
||||||
mon.setCursorPos(x, y + dy)
|
|
||||||
mon.write(string.rep(" ", w))
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
text = function(x, y, str, fg, bg)
|
|
||||||
mon.setBackgroundColor(bg or colors.black)
|
|
||||||
mon.setTextColor(fg or colors.white)
|
|
||||||
mon.setCursorPos(x, y)
|
|
||||||
mon.write(str)
|
|
||||||
end,
|
|
||||||
clear = function(col)
|
|
||||||
mon.setBackgroundColor(col)
|
|
||||||
mon.clear()
|
|
||||||
end,
|
|
||||||
sync = function() end,
|
|
||||||
colors = {
|
|
||||||
red = colors.red,
|
|
||||||
black = colors.black,
|
|
||||||
green = colors.green,
|
|
||||||
white = colors.white,
|
|
||||||
bg = colors.black,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local function initBackend()
|
|
||||||
local gpu = findGPU()
|
|
||||||
if gpu then
|
|
||||||
return makeGpuBackend(gpu)
|
|
||||||
end
|
|
||||||
local mon = makeMonitorBackend()
|
|
||||||
if mon then return mon end
|
|
||||||
error("No GPU or monitor attached. Connect a Tom's Peripherals GPU + screens, or a CC monitor.")
|
|
||||||
end
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
-- Wheel state
|
-- Wheel state
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
local W, H -- size in cells
|
local W, H
|
||||||
local perimeter -- ordered list of {x, y, color, label}
|
local perimeter
|
||||||
local ballIndex
|
local ballIndex
|
||||||
local lastBallIndex
|
local lastBallIndex
|
||||||
|
|
||||||
@@ -243,10 +107,10 @@ local function buildPerimeter()
|
|||||||
perimeter = {}
|
perimeter = {}
|
||||||
local function add(x, y) table.insert(perimeter, { x = x, y = y }) end
|
local function add(x, y) table.insert(perimeter, { x = x, y = y }) end
|
||||||
|
|
||||||
for x = 1, W do add(x, 1) end -- top
|
for x = 1, W do add(x, 1) end -- top L->R
|
||||||
for y = 2, H do add(W, y) end -- right
|
for y = 2, H do add(W, y) end -- right T->B
|
||||||
for x = W - 1, 1, -1 do add(x, H) end -- bottom
|
for x = W - 1, 1, -1 do add(x, H) end -- bottom R->L
|
||||||
for y = H - 1, 2, -1 do add(1, y) end -- left
|
for y = H - 1, 2, -1 do add(1, y) end -- left B->T
|
||||||
|
|
||||||
for i, c in ipairs(perimeter) do
|
for i, c in ipairs(perimeter) do
|
||||||
if i == 1 then
|
if i == 1 then
|
||||||
@@ -271,7 +135,6 @@ local function drawWheel()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function drawCenter(lines)
|
local function drawCenter(lines)
|
||||||
-- Clear interior
|
|
||||||
if W > 2 and H > 2 then
|
if W > 2 and H > 2 then
|
||||||
gfx.fillRect(2, 2, W - 2, H - 2, gfx.colors.bg)
|
gfx.fillRect(2, 2, W - 2, H - 2, gfx.colors.bg)
|
||||||
end
|
end
|
||||||
@@ -325,19 +188,17 @@ local function spin()
|
|||||||
local delay = START_DELAY + (END_DELAY - START_DELAY) * eased
|
local delay = START_DELAY + (END_DELAY - START_DELAY) * eased
|
||||||
local jump = math.max(1, math.floor((1 - eased) * 4 + 0.5))
|
local jump = math.max(1, math.floor((1 - eased) * 4 + 0.5))
|
||||||
|
|
||||||
local nextIdx = ((ballIndex - 1 + jump) % n) + 1
|
moveBall(((ballIndex - 1 + jump) % n) + 1)
|
||||||
moveBall(nextIdx)
|
|
||||||
sleep(delay)
|
sleep(delay)
|
||||||
elapsed = elapsed + delay
|
elapsed = elapsed + delay
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Final settle: pick a uniformly random pocket and crawl to it.
|
-- Crawl to a uniformly random final pocket.
|
||||||
local finalIdx = math.random(1, n)
|
local finalIdx = math.random(1, n)
|
||||||
local steps = ((finalIdx - ballIndex) % n)
|
local steps = (finalIdx - ballIndex) % n
|
||||||
if steps == 0 then steps = n end
|
if steps == 0 then steps = n end
|
||||||
for i = 1, steps do
|
for i = 1, steps do
|
||||||
local nextIdx = ((ballIndex - 1 + 1) % n) + 1
|
moveBall(((ballIndex - 1 + 1) % n) + 1)
|
||||||
moveBall(nextIdx)
|
|
||||||
sleep(END_DELAY + i * 0.04)
|
sleep(END_DELAY + i * 0.04)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -346,8 +207,8 @@ end
|
|||||||
|
|
||||||
local function announce(pocket)
|
local function announce(pocket)
|
||||||
local name = "BLACK"
|
local name = "BLACK"
|
||||||
if pocket.color == gfx.colors.red then name = "RED"
|
if pocket.color == gfx.colors.red then name = "RED" end
|
||||||
elseif pocket.color == gfx.colors.green then name = "GREEN" end
|
if pocket.color == gfx.colors.green then name = "GREEN" end
|
||||||
drawCenter({ "WINNER", name .. " " .. pocket.label })
|
drawCenter({ "WINNER", name .. " " .. pocket.label })
|
||||||
gfx.sync()
|
gfx.sync()
|
||||||
end
|
end
|
||||||
@@ -401,5 +262,3 @@ local function main()
|
|||||||
end
|
end
|
||||||
|
|
||||||
return { start = start, stop = stop, main = main }
|
return { start = start, stop = stop, main = main }
|
||||||
|
|
||||||
--]]
|
|
||||||
|
|||||||
Reference in New Issue
Block a user