ElijahCrafter

ATM

Jul 16th, 2026
30
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 12.04 KB | Source Code | 0 0
  1. -- PocketOS ATM - touch-screen deposits into the PocketOS bank.
  2. --
  3. -- Hardware: an ADVANCED computer with a wireless modem, a Create
  4. -- display target ("create_target") whose display lists the deposit
  5. -- chest's contents, and redstone (or an Advanced Peripherals redstone
  6. -- integrator) that moves the deposited items away when pulsed.
  7. --
  8. -- Flow: player taps START, types their pocket computer ID, puts iron/
  9. -- gold/diamonds in the chest, taps DEPOSIT ITEMS, confirms - the items
  10. -- get taken and the value lands in their bank account (they also get a
  11. -- receipt in Messages). Runs once and takes over startup as a kiosk.
  12.  
  13. -------------------------------------------------- CONFIG - edit me --
  14.  
  15. local RATES = { iron = 1, gold = 5, diamond = 25 } -- $ per item
  16. local ACCEPT_SIDE = "bottom" -- redstone side that accepts the items
  17. local IDLE_TIME = 45         -- seconds before returning to the start
  18.  
  19. -- Runs when a deposit is accepted: move the items out of the chest.
  20. local redint = peripheral.find("redstoneIntegrator")
  21. local function acceptItems()
  22.     local out = redint or redstone
  23.     out.setOutput(ACCEPT_SIDE, true)
  24.     sleep(1)
  25.     out.setOutput(ACCEPT_SIDE, false)
  26. end
  27.  
  28. -----------------------------------------------------------------------
  29.  
  30. local NICE = { iron = "Iron", gold = "Gold", diamond = "Diamond" }
  31.  
  32. if not term.isColor or not term.isColor() then
  33.     print("The ATM needs an advanced (gold) computer.")
  34.     return
  35. end
  36.  
  37. local target = peripheral.find("create_target")
  38. local hasModem = false
  39. for _, side in ipairs(peripheral.getNames()) do
  40.     if peripheral.getType(side) == "modem" then
  41.         if pcall(rednet.open, side) then hasModem = true end
  42.     end
  43. end
  44.  
  45. local w, h = term.getSize()
  46.  
  47. -- take over startup so the ATM boots as a kiosk
  48. do
  49.     local cur = ""
  50.     if fs.exists("startup.lua") then
  51.         local f = fs.open("startup.lua", "r")
  52.         cur = f.readAll() or ""
  53.         f.close()
  54.     end
  55.     if not cur:find("atm.lua", 1, true) then
  56.         local f = fs.open("startup.lua", "w")
  57.         f.write("-- ATM kiosk (created by atm.lua)\n")
  58.         f.write("shell.run(\"atm.lua\")\n")
  59.         f.close()
  60.     end
  61. end
  62.  
  63. local function fill(x, y, wd, ht, bg)
  64.     term.setBackgroundColor(bg)
  65.     for i = 0, ht - 1 do
  66.         term.setCursorPos(x, y + i)
  67.         term.write(string.rep(" ", wd))
  68.     end
  69. end
  70.  
  71. local function center(y, s, fg)
  72.     term.setCursorPos(math.floor((w - #s) / 2) + 1, y)
  73.     term.setTextColor(fg)
  74.     term.setBackgroundColor(colors.black)
  75.     term.write(s)
  76. end
  77.  
  78. local function button(x, y, label, bg)
  79.     fill(x, y, #label + 2, 1, bg)
  80.     term.setCursorPos(x + 1, y)
  81.     term.setTextColor(colors.white)
  82.     term.write(label)
  83.     return { x1 = x, y1 = y, x2 = x + #label + 1, y2 = y }
  84. end
  85.  
  86. local function hit(r, x, y)
  87.     return r and x >= r.x1 and x <= r.x2 and y >= r.y1 and y <= r.y2
  88. end
  89.  
  90. local function screenMsg(title, lines, buttons)
  91.     term.setBackgroundColor(colors.black)
  92.     term.clear()
  93.     center(4, title, colors.white)
  94.     for i, s in ipairs(lines) do
  95.         center(6 + i, s, colors.lightGray)
  96.     end
  97.     local regions = {}
  98.     local total = #buttons - 1
  99.     for _, b in ipairs(buttons) do total = total + #b[1] + 2 end
  100.     local bx = math.floor((w - total) / 2) + 1
  101.     for i, b in ipairs(buttons) do
  102.         regions[i] = button(bx, 9 + #lines, b[1], b[2])
  103.         bx = bx + #b[1] + 3
  104.     end
  105.     while true do
  106.         local _, _, mx, my = os.pullEvent("mouse_click")
  107.         for i, r in ipairs(regions) do
  108.             if hit(r, mx, my) then return i end
  109.         end
  110.     end
  111. end
  112.  
  113. ------------------------------------------------------------- bank --
  114.  
  115. local bankId
  116. local function findBank()
  117.     if bankId then return bankId end
  118.     local ok, cfg = pcall(dofile, "/pocketos/config.lua")
  119.     if ok and type(cfg) == "table" and tonumber(cfg.bank) then
  120.         bankId = tonumber(cfg.bank)
  121.         return bankId
  122.     end
  123.     bankId = rednet.lookup("pocketos.bank")
  124.     return bankId
  125. end
  126.  
  127. local function request(msg)
  128.     if not hasModem then return nil, "No modem on the ATM" end
  129.     local bid = findBank()
  130.     if not bid then return nil, "No bank found" end
  131.     rednet.send(bid, msg, "pocketos.bank")
  132.     local id, resp = rednet.receive("pocketos.bank", 3)
  133.     if id == bid and type(resp) == "table" then return resp end
  134.     return nil, "Bank not answering"
  135. end
  136.  
  137. -- read the Create display target listing the deposit chest
  138. local function scanDeposit()
  139.     local found, total = {}, 0
  140.     if not target then return found, 0 end
  141.     for i = 1, 8 do
  142.         local ok, line = pcall(target.getLine, i)
  143.         if ok and type(line) == "string" and line ~= "" then
  144.             local amount = tonumber(line:match("(%d+)"))
  145.             if amount then
  146.                 local low = line:lower()
  147.                 local kind
  148.                 if low:find("iron", 1, true) then
  149.                     kind = "iron"
  150.                 elseif low:find("gold", 1, true) then
  151.                     kind = "gold"
  152.                 elseif low:find("diamond", 1, true) then
  153.                     kind = "diamond"
  154.                 end
  155.                 if kind and RATES[kind] then
  156.                     local value = amount * RATES[kind]
  157.                     found[#found + 1] = { n = amount, kind = kind, value = value }
  158.                     total = total + value
  159.                 end
  160.             end
  161.         end
  162.     end
  163.     return found, total
  164. end
  165.  
  166. --------------------------------------------------------------- ui --
  167.  
  168. local function keypad(title)
  169.     local entry = ""
  170.     local layout = {
  171.         { "1", "2", "3" },
  172.         { "4", "5", "6" },
  173.         { "7", "8", "9" },
  174.         { "C", "0", "OK" },
  175.     }
  176.     while true do
  177.         term.setBackgroundColor(colors.black)
  178.         term.clear()
  179.         center(2, title, colors.white)
  180.         center(4, entry == "" and "_" or entry, colors.yellow)
  181.         local regions = {}
  182.         local x0 = math.floor(w / 2) - 8
  183.         for r = 1, 4 do
  184.             for cn = 1, 3 do
  185.                 local label = layout[r][cn]
  186.                 local bx = x0 + (cn - 1) * 6
  187.                 local by = 4 + r * 2
  188.                 fill(bx, by, 5, 1, label == "OK" and colors.green or colors.gray)
  189.                 term.setCursorPos(bx + math.floor((5 - #label) / 2), by)
  190.                 term.setTextColor(colors.white)
  191.                 term.write(label)
  192.                 regions[#regions + 1] = { x1 = bx, x2 = bx + 4, y = by, label = label }
  193.             end
  194.         end
  195.         center(14, "[ Cancel ]", colors.red)
  196.         center(h, "Your ID is shown in Settings on your pocket", colors.gray)
  197.  
  198.         local e, a, b, c = os.pullEvent()
  199.         if e == "mouse_click" then
  200.             if c == 14 then return nil end
  201.             for _, r in ipairs(regions) do
  202.                 if c == r.y and b >= r.x1 and b <= r.x2 then
  203.                     if r.label == "OK" then
  204.                         if #entry > 0 then return tonumber(entry) end
  205.                     elseif r.label == "C" then
  206.                         entry = ""
  207.                     elseif #entry < 7 then
  208.                         entry = entry .. r.label
  209.                     end
  210.                 end
  211.             end
  212.         elseif e == "char" and a:match("^%d$") and #entry < 7 then
  213.             entry = entry .. a
  214.         elseif e == "key" then
  215.             if a == keys.enter and #entry > 0 then return tonumber(entry) end
  216.             if a == keys.backspace then entry = entry:sub(1, -2) end
  217.         end
  218.     end
  219. end
  220.  
  221. local function idleScreen()
  222.     term.setBackgroundColor(colors.black)
  223.     term.clear()
  224.     center(3, "PocketOS ATM", colors.yellow)
  225.     center(5, string.format("Iron $%d   Gold $%d   Diamond $%d",
  226.         RATES.iron, RATES.gold, RATES.diamond), colors.lightGray)
  227.     local bw, bh = 15, 3
  228.     local bx = math.floor((w - bw) / 2) + 1
  229.     fill(bx, 8, bw, bh, colors.green)
  230.     term.setCursorPos(bx + math.floor((bw - 5) / 2), 9)
  231.     term.setTextColor(colors.white)
  232.     term.write("START")
  233.     if not target then
  234.         center(13, "! No Create target attached", colors.red)
  235.     end
  236.     if not hasModem then
  237.         center(14, "! No wireless modem", colors.red)
  238.     end
  239.     center(h, "ATM #" .. os.getComputerID(), colors.gray)
  240.     while true do
  241.         local e = os.pullEvent("mouse_click")
  242.         if e then return end
  243.     end
  244. end
  245.  
  246. local function depositFlow(account)
  247.     local items, total = scanDeposit()
  248.     if not target then
  249.         screenMsg("ATM Error", { "No Create display", "target attached." },
  250.             { { "OK", colors.blue } })
  251.         return nil
  252.     end
  253.     if total <= 0 then
  254.         screenMsg("Nothing to Deposit", {
  255.             "Put iron, gold or",
  256.             "diamonds into the",
  257.             "deposit chest first.",
  258.         }, { { "OK", colors.blue } })
  259.         return nil
  260.     end
  261.  
  262.     term.setBackgroundColor(colors.black)
  263.     term.clear()
  264.     center(2, "Deposit for #" .. account, colors.white)
  265.     local y = 4
  266.     for _, it in ipairs(items) do
  267.         center(y, it.n .. " " .. NICE[it.kind] .. " = $" .. it.value, colors.lightGray)
  268.         y = y + 1
  269.     end
  270.     center(y + 1, "Total: $" .. total, colors.yellow)
  271.     local accept = button(math.floor(w / 2) - 11, y + 3, "Accept", colors.green)
  272.     local cancel = button(math.floor(w / 2) + 3, y + 3, "Cancel", colors.red)
  273.     while true do
  274.         local _, _, mx, my = os.pullEvent("mouse_click")
  275.         if hit(accept, mx, my) then break end
  276.         if hit(cancel, mx, my) then return nil end
  277.     end
  278.  
  279.     center(y + 5, "Processing...", colors.gray)
  280.     pcall(acceptItems)
  281.     local resp, err
  282.     for _ = 1, 3 do
  283.         resp, err = request({ type = "deposit", account = account, amount = total })
  284.         if resp then break end
  285.         sleep(1)
  286.     end
  287.     if resp and resp.ok then
  288.         screenMsg("Deposited!", {
  289.             "$" .. total .. " added.",
  290.             "New balance: $" .. (resp.balance or "?"),
  291.         }, { { "OK", colors.green } })
  292.         return resp.balance
  293.     end
  294.     screenMsg("Bank Offline!", {
  295.         "Items were taken but",
  296.         "not credited. Tell an",
  297.         "admin: #" .. account .. " +$" .. total,
  298.     }, { { "OK", colors.red } })
  299.     return nil
  300. end
  301.  
  302. local function accountScreen(account, info)
  303.     local balance = info.balance or 0
  304.     local label = info.label
  305.     while true do
  306.         term.setBackgroundColor(colors.black)
  307.         term.clear()
  308.         center(2, "#" .. account .. (label and (" " .. label) or ""), colors.white)
  309.         center(4, "Balance: $" .. balance, colors.yellow)
  310.         local bw, bh = 25, 5
  311.         local bx = math.floor((w - bw) / 2) + 1
  312.         local by = 7
  313.         fill(bx, by, bw, bh, colors.green)
  314.         term.setCursorPos(bx + math.floor((bw - 13) / 2), by + 2)
  315.         term.setTextColor(colors.white)
  316.         term.write("DEPOSIT ITEMS")
  317.         fill(w - 7, h - 1, 6, 1, colors.gray)
  318.         term.setCursorPos(w - 6, h - 1)
  319.         term.setTextColor(colors.white)
  320.         term.write("Done")
  321.         center(h, "Insert items into the chest first", colors.gray)
  322.  
  323.         local t = os.startTimer(IDLE_TIME)
  324.         local e, a, b, c = os.pullEvent()
  325.         if e == "timer" and a == t then
  326.             return
  327.         elseif e == "mouse_click" then
  328.             if b >= bx and b <= bx + bw - 1 and c >= by and c <= by + bh - 1 then
  329.                 local nb = depositFlow(account)
  330.                 if nb then balance = nb end
  331.             elseif c == h - 1 and b >= w - 7 then
  332.                 return
  333.             end
  334.         end
  335.     end
  336. end
  337.  
  338. --------------------------------------------------------------- run --
  339.  
  340. while true do
  341.     idleScreen()
  342.     local account = keypad("Enter your pocket ID")
  343.     if account then
  344.         term.setBackgroundColor(colors.black)
  345.         term.clear()
  346.         center(9, "Checking...", colors.gray)
  347.         local info, err = request({ type = "info", account = account })
  348.         if info and info.ok then
  349.             accountScreen(account, info)
  350.         else
  351.             screenMsg("No Bank", { tostring((info and info.err) or err or "Offline") },
  352.                 { { "OK", colors.red } })
  353.         end
  354.     end
  355. end
  356.  
Advertisement
Add Comment
Please, Sign In to add comment