Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local component = require("component")
- local event = require("event")
- local computer = require("computer")
- local serialization = require("serialization")
- local cfg = {
- title = "GintaForine",
- dataPath = "/home/casino_data.lua",
- initialBalance = 10,
- defaultBet = 1,
- minBet = 1,
- maxBet = 1000,
- idleTimeout = 30,
- animationSteps = 16,
- animationDelay = 0.06,
- preferredWidth = 100,
- preferredHeight = 30
- }
- local colors = {
- black = 0x000000,
- dark = 0x06101a,
- panel = 0x071b2b,
- blue = 0x152cff,
- cyan = 0x00ffff,
- white = 0xffffff,
- gray = 0x777777,
- green = 0x38dd62,
- yellow = 0xffd34d,
- red = 0xff3b4f
- }
- local iconColors = {
- [" "] = colors.dark,
- ["."] = colors.dark,
- K = 0x111111,
- W = 0xffffff,
- R = 0xff2c4d,
- P = 0xff4fa0,
- G = 0x48d85b,
- D = 0x246b2c,
- Y = 0xffd426,
- O = 0xff8d1a,
- L = 0xfff36b,
- V = 0x7c4dff,
- B = 0x5b35c8,
- M = 0x8a4c20
- }
- local fruits = {
- {
- id = "cherry", name = "вишенки", text = "CH", color = 0xff2c4d,
- triple = 16, weight = 16,
- icon = {
- ".....G.....",
- "....G.G....",
- "...G...G...",
- "..R.....R..",
- ".RRR...RRR.",
- ".RRR...RRR.",
- "..R.....R..",
- "...........",
- "..........."
- }
- },
- {
- id = "lemon", name = "лимоны", text = "LM", color = 0xfff36b,
- triple = 12, weight = 18,
- icon = {
- "...........",
- "...KKKKK...",
- "..KLLLLLK..",
- ".KLLWLLLLK.",
- ".KLLLLLLLK.",
- ".KLLLLWLLK.",
- "..KLLLLLK..",
- "...KKKKK...",
- "..........."
- }
- },
- {
- id = "strawberry", name = "клубника", text = "ST", color = 0xff3b4f,
- triple = 15, weight = 15,
- icon = {
- "....GGG....",
- "...GGGGG...",
- "..KRRRRRK..",
- ".KRRWRRRRK.",
- ".KRRRWRRRK.",
- "..KRRRWRK..",
- "...KRRK....",
- "....KK.....",
- "..........."
- }
- },
- {
- id = "orange", name = "апельсины", text = "OR", color = 0xff8d1a,
- triple = 17, weight = 13,
- icon = {
- "....GG.....",
- "...G..G....",
- "...KKK.....",
- "..KOOOK....",
- ".KOOYOK....",
- ".KOOOOOK...",
- "..KOOOK....",
- "...KKK.....",
- "..........."
- }
- },
- {
- id = "apple", name = "яблоки", text = "AP", color = 0x48d85b,
- triple = 20, weight = 10,
- icon = {
- ".....M.....",
- "....MGG....",
- "...KKKKK...",
- "..KGGGGGK..",
- ".KGGWGGGK..",
- ".KGGGGGGK..",
- "..KGGGGK...",
- "...KKKK....",
- "..........."
- }
- },
- {
- id = "watermelon", name = "арбузы", text = "WM", color = 0x38dd62,
- triple = 25, weight = 8,
- icon = {
- "...........",
- "..KKKKKKK..",
- ".KDGGGGDK..",
- "KDGGRGGDK..",
- "KDGGRRGDK..",
- "KDGGRGGDK..",
- ".KDGGGGDK..",
- "..KKKKKKK..",
- "..........."
- }
- },
- {
- id = "banana", name = "бананы", text = "BN", color = 0xffd426,
- triple = 30, weight = 6,
- icon = {
- "...........",
- ".......KK..",
- "......KYYK.",
- ".....KYYYK.",
- "...KKYYYK..",
- "..KYYYYK...",
- ".KYYYYK....",
- "..KKK......",
- "..........."
- }
- },
- {
- id = "grape", name = "виноград", text = "GR", color = 0x7c4dff,
- triple = 40, weight = 3,
- icon = {
- "....GG.....",
- "...G.......",
- "...KVK.....",
- "..KVKVK....",
- ".KVKVKVK...",
- "..KVKVK....",
- "...KVK.....",
- "....K......",
- "..........."
- }
- },
- {
- id = "gold", name = "золотой фрукт", text = "GF", color = 0xffd34d,
- triple = 100, weight = 1,
- icon = {
- "....GG.....",
- "...G..G....",
- "...KKK.....",
- "..KYYYK....",
- ".KYYWYYK...",
- ".KYYYYYK...",
- "..KYYYK....",
- "...KKK.....",
- "..........."
- }
- }
- }
- local gpu
- local sw, sh = 80, 25
- local buttons = {}
- local data
- local playerName
- local player
- local reels = { fruits[1], fruits[2], fruits[3] }
- local locked = { false, false, false }
- local message = "Нажмите на монитор, чтобы начать"
- local spinning = false
- local function roundMoney(value)
- return math.floor((tonumber(value) or 0) * 100 + 0.5) / 100
- end
- local function money(value)
- return string.format("%.2f$", roundMoney(value))
- end
- local function now()
- return computer.uptime()
- end
- local function clamp(value, minValue, maxValue)
- value = tonumber(value) or minValue
- if value < minValue then return minValue end
- if value > maxValue then return maxValue end
- return value
- end
- local function setFg(c) gpu.setForeground(c) end
- local function setBg(c) gpu.setBackground(c) end
- local function write(x, y, text, fg, bg)
- if fg then setFg(fg) end
- if bg then setBg(bg) end
- gpu.set(x, y, tostring(text or ""))
- end
- local function fill(x, y, w, h, bg, ch)
- if w <= 0 or h <= 0 then return end
- setBg(bg or colors.black)
- gpu.fill(x, y, w, h, ch or " ")
- end
- local function centerText(text, width)
- text = tostring(text or "")
- if #text >= width then return text:sub(1, width) end
- return string.rep(" ", math.floor((width - #text) / 2)) .. text
- end
- local function box(x, y, w, h, title)
- setFg(colors.blue)
- setBg(colors.black)
- gpu.fill(x + 1, y, w - 2, 1, "-")
- gpu.fill(x + 1, y + h - 1, w - 2, 1, "-")
- gpu.fill(x, y + 1, 1, h - 2, "|")
- gpu.fill(x + w - 1, y + 1, 1, h - 2, "|")
- write(x, y, "+", colors.blue, colors.black)
- write(x + w - 1, y, "+", colors.blue, colors.black)
- write(x, y + h - 1, "+", colors.blue, colors.black)
- write(x + w - 1, y + h - 1, "+", colors.blue, colors.black)
- if title then
- write(x + 2, y, " " .. title .. " ", colors.cyan, colors.black)
- end
- end
- local function addButton(x1, y1, x2, y2, label, action, value)
- buttons[#buttons + 1] = { x1 = x1, y1 = y1, x2 = x2, y2 = y2, label = label, action = action, value = value }
- end
- local function drawButton(b, active)
- local bg = active and colors.blue or colors.panel
- fill(b.x1, b.y1, b.x2 - b.x1 + 1, b.y2 - b.y1 + 1, bg)
- write(b.x1 + 1, b.y1, centerText(b.label, b.x2 - b.x1 - 1), colors.cyan, bg)
- end
- local function drawFruitIcon(x, y, w, h, fruit)
- fill(x, y, w, h, colors.dark)
- local iconH = #fruit.icon
- local iconW = #(fruit.icon[1] or "")
- for row = 1, h do
- local sourceRow = math.floor((row - 0.5) * iconH / h) + 1
- if sourceRow < 1 then sourceRow = 1 end
- if sourceRow > iconH then sourceRow = iconH end
- local line = fruit.icon[sourceRow]
- for col = 1, w do
- local sourceCol = math.floor((col - 0.5) * iconW / w) + 1
- if sourceCol < 1 then sourceCol = 1 end
- if sourceCol > iconW then sourceCol = iconW end
- fill(x + col - 1, y + row - 1, 1, 1, iconColors[line:sub(sourceCol, sourceCol)] or fruit.color)
- end
- end
- end
- local function drawSlot(x, y, fruit, isLocked)
- fill(x, y, 17, 11, 0x303030)
- box(x, y, 17, 11)
- drawFruitIcon(x + 3, y + 1, 11, 9, fruit)
- if isLocked then
- write(x + 6, y + 10, "OK", colors.green, colors.black)
- end
- end
- local function normalizeBet()
- if not player then return end
- if player.balance >= cfg.minBet then
- player.bet = clamp(player.bet, cfg.minBet, math.min(cfg.maxBet, player.balance))
- else
- player.bet = cfg.minBet
- end
- end
- local function emptyData()
- return { totalSpent = 0, totalWon = 0, players = {} }
- end
- local function loadData()
- local f = io.open(cfg.dataPath, "r")
- if not f then return emptyData() end
- local text = f:read("*a")
- f:close()
- local ok, loaded = pcall(serialization.unserialize, text)
- if not ok or type(loaded) ~= "table" or type(loaded.players) ~= "table" then
- return emptyData()
- end
- loaded.totalSpent = roundMoney(loaded.totalSpent)
- loaded.totalWon = roundMoney(loaded.totalWon)
- return loaded
- end
- local function saveData()
- if not data then return end
- local tmp = cfg.dataPath .. ".tmp"
- local f, reason = io.open(tmp, "w")
- if not f then
- message = "Не удалось сохранить: " .. tostring(reason)
- return
- end
- f:write(serialization.serialize(data))
- f:write("\n")
- f:close()
- os.remove(cfg.dataPath)
- os.rename(tmp, cfg.dataPath)
- end
- local function getPlayer(name)
- name = tostring(name or "")
- if name == "" then name = "Unknown" end
- if type(data.players[name]) ~= "table" then
- data.players[name] = {
- balance = cfg.initialBalance,
- totalSpent = 0,
- totalWon = 0,
- bet = cfg.defaultBet,
- lastActive = now()
- }
- end
- local p = data.players[name]
- p.balance = roundMoney(p.balance)
- p.totalSpent = roundMoney(p.totalSpent)
- p.totalWon = roundMoney(p.totalWon)
- p.bet = roundMoney(p.bet or cfg.defaultBet)
- p.lastActive = tonumber(p.lastActive) or now()
- return name, p
- end
- local function randomFruit()
- local total = 0
- for i = 1, #fruits do total = total + fruits[i].weight end
- local roll = math.random() * total
- local seen = 0
- for i = 1, #fruits do
- seen = seen + fruits[i].weight
- if roll <= seen then return fruits[i] end
- end
- return fruits[#fruits]
- end
- local function evaluate()
- local a, b, c = reels[1], reels[2], reels[3]
- if a.id == b.id and b.id == c.id then
- return a.triple, "Три " .. a.name
- end
- if a.id == b.id or b.id == c.id then
- return 2, "Два фрукта рядом"
- end
- if a.id == c.id then
- return 1, "Два фрукта по краям"
- end
- return 0, "Без совпадений"
- end
- local function buttonAt(x, y)
- for i = #buttons, 1, -1 do
- local b = buttons[i]
- if x >= b.x1 and x <= b.x2 and y >= b.y1 and y <= b.y2 then return b end
- end
- end
- local function secondsLeft()
- if not player then return cfg.idleTimeout end
- return math.max(0, math.ceil(cfg.idleTimeout - (now() - player.lastActive)))
- end
- local function drawWaiting()
- buttons = {}
- fill(1, 1, sw, sh, colors.black)
- box(1, 1, sw, sh, cfg.title)
- write(4, 4, "Нажмите на монитор, чтобы начать", colors.cyan, colors.black)
- write(4, 6, "Фруктовый автомат. Новый баланс: " .. money(cfg.initialBalance), colors.white, colors.black)
- write(4, 7, "2 рядом = x2, 2 по краям = x1, 3 фрукта = большой бонус.", colors.white, colors.black)
- write(4, 9, "Редкие фрукты выпадают реже, но платят больше.", colors.yellow, colors.black)
- local startX = math.max(4, math.floor((sw - 65) / 2))
- local y = math.max(12, math.floor(sh / 2))
- for i = 1, 3 do
- drawSlot(startX + (i - 1) * 24, y, fruits[i + 2], false)
- end
- end
- local function drawGame()
- buttons = {}
- fill(1, 1, sw, sh, colors.black)
- box(1, 1, sw, sh, cfg.title)
- if sw < 90 then
- write(4, 3, playerName .. " Баланс: " .. money(player.balance), colors.cyan, colors.black)
- write(4, 4, "Ставка: " .. money(player.bet), colors.yellow, colors.black)
- write(4, 5, message or "Крутите фрукты.", colors.white, colors.black)
- local slotY = 8
- local slotStart = math.max(3, math.floor((sw - 55) / 2))
- for i = 1, 3 do
- drawSlot(slotStart + (i - 1) * 19, slotY, reels[i], locked[i])
- end
- local betY = sh - 5
- local betX = math.max(2, math.floor((sw - 68) / 2))
- local betButtons = {
- { "-10$", -10 }, { "-5$", -5 }, { "-1$", -1 },
- { "Ставка " .. money(player.bet), 0 },
- { "+1$", 1 }, { "+5$", 5 }, { "+10$", 10 }
- }
- local widths = { 7, 6, 6, 16, 6, 6, 7 }
- local x = betX
- for i = 1, #betButtons do
- local label, value = betButtons[i][1], betButtons[i][2]
- addButton(x, betY, x + widths[i] - 1, betY + 1, label, value == 0 and "spin" or "bet", value)
- x = x + widths[i] + 2
- end
- addButton(slotStart, slotY, slotStart + 54, slotY + 10, "spinArea", "spin")
- addButton(4, sh - 2, 14, sh - 2, "Выход", "exit")
- for i = 1, #buttons do
- if buttons[i].label ~= "spinArea" then drawButton(buttons[i], not spinning) end
- end
- return
- end
- local sideW = sw >= 82 and 30 or 24
- box(3, 3, sideW, sh - 4, "Игрок")
- write(5, 5, "Ник:", colors.blue, colors.black)
- write(5, 6, playerName, colors.cyan, colors.black)
- write(5, 8, "Баланс:", colors.blue, colors.black)
- write(5, 9, money(player.balance), colors.green, colors.black)
- write(5, 11, "Потрачено: " .. money(player.totalSpent), colors.white, colors.black)
- write(5, 12, "Выиграно: " .. money(player.totalWon), colors.white, colors.black)
- write(5, sh - 7, "Авто-выход: " .. tostring(secondsLeft()), colors.blue, colors.black)
- addButton(5, sh - 4, sideW - 2, sh - 3, "Выход", "exit")
- local gameX = sideW + 5
- local gameW = sw - gameX - 2
- box(gameX, 3, gameW, sh - 4, "Фрукты")
- write(gameX + 3, 5, message or "Выберите ставку и крутите.", colors.cyan, colors.black)
- local slotY = 9
- local slotStart = gameX + math.max(2, math.floor((gameW - 55) / 2))
- for i = 1, 3 do
- drawSlot(slotStart + (i - 1) * 19, slotY, reels[i], locked[i])
- end
- local betY = sh - 6
- local betX = gameX + math.max(2, math.floor((gameW - 68) / 2))
- local betButtons = {
- { "-10$", -10 }, { "-5$", -5 }, { "-1$", -1 },
- { "Ставка " .. money(player.bet), 0 },
- { "+1$", 1 }, { "+5$", 5 }, { "+10$", 10 }
- }
- local widths = { 7, 6, 6, 16, 6, 6, 7 }
- local x = betX
- for i = 1, #betButtons do
- local label, value = betButtons[i][1], betButtons[i][2]
- local action = value == 0 and "spin" or "bet"
- addButton(x, betY, x + widths[i] - 1, betY + 1, label, action, value)
- x = x + widths[i] + 2
- end
- addButton(slotStart, slotY, slotStart + 54, slotY + 10, "spinArea", "spin")
- for i = 1, #buttons do
- if buttons[i].label ~= "spinArea" then drawButton(buttons[i], not spinning) end
- end
- end
- local function redraw()
- sw, sh = gpu.getResolution()
- if sw < 70 or sh < 22 then
- buttons = {}
- fill(1, 1, sw, sh, colors.black)
- write(2, 2, "Экран слишком маленький.", colors.red, colors.black)
- write(2, 3, "Нужно хотя бы 70x22.", colors.white, colors.black)
- return
- end
- if not player then drawWaiting() else drawGame() end
- end
- local function login(name)
- playerName, player = getPlayer(name)
- player.lastActive = now()
- normalizeBet()
- message = "Добро пожаловать, " .. playerName
- saveData()
- redraw()
- end
- local function logout()
- playerName, player = nil, nil
- locked = { false, false, false }
- message = "Нажмите на монитор, чтобы начать"
- redraw()
- end
- local function changeBet(delta)
- if spinning or not player then return end
- player.lastActive = now()
- player.bet = roundMoney(player.bet + delta)
- normalizeBet()
- message = "Ставка: " .. money(player.bet)
- saveData()
- redraw()
- end
- local function drainTouches()
- while event.pull(0, "touch") do end
- end
- local function spin()
- if spinning or not player then return end
- player.lastActive = now()
- normalizeBet()
- if player.balance < player.bet or player.balance < cfg.minBet then
- message = "Не хватает денег на ставку"
- redraw()
- return
- end
- spinning = true
- locked = { false, false, false }
- player.balance = roundMoney(player.balance - player.bet)
- player.totalSpent = roundMoney(player.totalSpent + player.bet)
- data.totalSpent = roundMoney(data.totalSpent + player.bet)
- message = "Крутим фрукты..."
- saveData()
- local final = { randomFruit(), randomFruit(), randomFruit() }
- for step = 1, cfg.animationSteps do
- for slot = 1, 3 do
- local stopAt = math.floor(cfg.animationSteps * (0.45 + slot * 0.16))
- if step >= stopAt then
- reels[slot] = final[slot]
- locked[slot] = true
- else
- reels[slot] = randomFruit()
- locked[slot] = false
- end
- end
- redraw()
- os.sleep(cfg.animationDelay)
- end
- reels = final
- locked = { true, true, true }
- local mult, reason = evaluate()
- local payout = roundMoney(player.bet * mult)
- if payout > 0 then
- player.balance = roundMoney(player.balance + payout)
- player.totalWon = roundMoney(player.totalWon + payout)
- data.totalWon = roundMoney(data.totalWon + payout)
- message = reason .. ": +" .. money(payout) .. " x" .. tostring(mult)
- else
- message = "Фрукты не совпали"
- end
- normalizeBet()
- spinning = false
- saveData()
- redraw()
- drainTouches()
- end
- local function handleTouch(x, y, name)
- if not player then
- login(name)
- return
- end
- player.lastActive = now()
- local b = buttonAt(x, y)
- if not b then
- redraw()
- return
- end
- if b.action == "exit" then
- saveData()
- logout()
- elseif b.action == "bet" then
- changeBet(b.value)
- elseif b.action == "spin" then
- spin()
- end
- end
- if not component.isAvailable("gpu") then
- io.stderr:write("GintaForine: не найдена видеокарта GPU.\n")
- return
- end
- if not component.isAvailable("screen") then
- io.stderr:write("GintaForine: не найден экран OpenComputers.\n")
- return
- end
- gpu = component.gpu
- pcall(function()
- if gpu.getScreen and not gpu.getScreen() then
- gpu.bind(component.screen.address, true)
- end
- end)
- pcall(function()
- local maxW, maxH = gpu.maxResolution()
- gpu.setResolution(math.min(maxW, cfg.preferredWidth), math.min(maxH, cfg.preferredHeight))
- end)
- math.randomseed(os.time() + math.floor(computer.uptime() * 100000))
- for _ = 1, 5 do math.random() end
- data = loadData()
- local function main()
- redraw()
- while true do
- local ev, _, x, y, _, name = event.pull(1)
- if ev == "interrupted" then
- break
- elseif ev == "screen_resized" then
- redraw()
- elseif ev == "touch" then
- handleTouch(tonumber(x) or 1, tonumber(y) or 1, name)
- elseif player then
- if now() - player.lastActive >= cfg.idleTimeout then
- saveData()
- logout()
- else
- redraw()
- end
- end
- end
- end
- local ok, err = pcall(main)
- saveData()
- fill(1, 1, sw, sh, colors.black)
- setFg(colors.white)
- setBg(colors.black)
- if not ok and tostring(err) ~= "interrupted" then error(err) end
Add Comment
Please, Sign In to add comment