MtnMCG

sethmc slots

Dec 18th, 2025 (edited)
48
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 26.14 KB | None | 0 0
  1. -- Modern Slot Machine (modern_slot.lua)
  2. -- OVERHAUL: Engagement Mechanics, Near-Miss, Streak Detection, 99% RTP
  3. -- Server calls preserved - visuals and psychology enhanced
  4.  
  5. local modem = peripheral.find("modem")
  6. if not modem then error("No modem found. Please attach a wireless modem.") end
  7.  
  8. local speaker = peripheral.find("speaker")
  9. local function playSoundFile(name, volume, pitch)
  10. if speaker then speaker.playSound(name, volume or 1, pitch or 1) end
  11. end
  12.  
  13. modem.open(1)
  14.  
  15. -- ==========================================
  16. -- CRYPTO & SERVER COMM (UNCHANGED)
  17. -- ==========================================
  18. local function encrypt(text)
  19. local result = ""
  20. for i = 1, #text do
  21. result = result .. string.char((string.byte(text, i) + 5) % 256)
  22. end
  23. return result
  24. end
  25.  
  26. local function decrypt(text)
  27. local result = ""
  28. for i = 1, #text do
  29. result = result .. string.char((string.byte(text, i) - 5) % 256)
  30. end
  31. return result
  32. end
  33.  
  34. local function sendRequest(request)
  35. modem.transmit(1, 1, request)
  36. local timer = os.startTimer(5)
  37. while true do
  38. local event, param1, _, _, message = os.pullEvent()
  39. if event == "modem_message" and type(message) == "table" then
  40. return message
  41. elseif event == "timer" and param1 == timer then
  42. return nil
  43. end
  44. end
  45. end
  46.  
  47. -- ==========================================
  48. -- ADMIN & LOGIN LOGIC (UNCHANGED)
  49. -- ==========================================
  50. local function saveAdminCredentials(id, password)
  51. local file = fs.open(".admin_cred", "w")
  52. file.write(encrypt(id .. ":" .. password))
  53. file.close()
  54. end
  55.  
  56. local function loadAdminCredentials()
  57. if fs.exists(".admin_cred") then
  58. local file = fs.open(".admin_cred", "r")
  59. local content = decrypt(file.readAll())
  60. file.close()
  61. local id, password = content:match("(.+):(.+)")
  62. return id, password
  63. end
  64. return nil, nil
  65. end
  66.  
  67. local function adminLogin()
  68. term.clear()
  69. term.setCursorPos(1,1)
  70. print("Admin Setup")
  71. write("Enter admin ID: ")
  72. local adminId = read()
  73. write("Enter admin password: ")
  74. local adminPassword = read("*")
  75.  
  76. local response = sendRequest({type = "login", studentId = adminId, password = adminPassword})
  77. if response and response.success then
  78. saveAdminCredentials(adminId, adminPassword)
  79. return adminId, adminPassword
  80. else
  81. error("Admin login failed")
  82. end
  83. end
  84.  
  85. local adminId, adminPassword = loadAdminCredentials()
  86. if not adminId or not adminPassword then
  87. adminId, adminPassword = adminLogin()
  88. end
  89.  
  90. -- ==========================================
  91. -- RISK MANAGEMENT & STATS
  92. -- ==========================================
  93. local STATS_FILE = ".slot_stats"
  94. local JACKPOT_FILE = ".jackpot_history"
  95. local TARGET_RTP = 0.90 -- 90% RTP = 10% house edge (was 99%)
  96. local SPIN_COST = 3.0
  97. local JACKPOT_COOLDOWN = 432000 -- 5 days in seconds (5 * 24 * 60 * 60)
  98.  
  99. local function loadStats()
  100. if fs.exists(STATS_FILE) then
  101. local f = fs.open(STATS_FILE, "r")
  102. local data = textutils.unserialize(f.readAll())
  103. f.close()
  104. if data then return data end
  105. end
  106. return {totalWagered = 0, totalPayout = 0}
  107. end
  108.  
  109. local function saveStats(stats)
  110. local f = fs.open(STATS_FILE, "w")
  111. f.write(textutils.serialize(stats))
  112. f.close()
  113. end
  114.  
  115. -- Jackpot history: tracks when each player last won a jackpot
  116. local function loadJackpotHistory()
  117. if fs.exists(JACKPOT_FILE) then
  118. local f = fs.open(JACKPOT_FILE, "r")
  119. local data = textutils.unserialize(f.readAll())
  120. f.close()
  121. if data then return data end
  122. end
  123. return {}
  124. end
  125.  
  126. local function saveJackpotHistory(history)
  127. local f = fs.open(JACKPOT_FILE, "w")
  128. f.write(textutils.serialize(history))
  129. f.close()
  130. end
  131.  
  132. local function canPlayerWinJackpot(playerId)
  133. local history = loadJackpotHistory()
  134. local lastWin = history[playerId]
  135. if not lastWin then return true end
  136.  
  137. local now = os.epoch("utc") / 1000 -- Convert to seconds
  138. local elapsed = now - lastWin
  139. return elapsed >= JACKPOT_COOLDOWN
  140. end
  141.  
  142. local function recordJackpotWin(playerId)
  143. local history = loadJackpotHistory()
  144. history[playerId] = os.epoch("utc") / 1000 -- Store in seconds
  145. saveJackpotHistory(history)
  146. end
  147.  
  148. local function getRTPModifier(stats)
  149. if stats.totalWagered == 0 then return 1.0 end
  150. local currentRTP = stats.totalPayout / stats.totalWagered
  151.  
  152. if currentRTP > TARGET_RTP then
  153. -- Players winning too much - aggressively reduce chances
  154. local diff = currentRTP - TARGET_RTP
  155. return math.max(0.05, 1.0 - (diff * 5)) -- More aggressive reduction
  156. elseif currentRTP < (TARGET_RTP - 0.15) then
  157. -- Players losing too much - slightly boost chances
  158. return math.min(1.3, 1.1)
  159. end
  160. return 1.0
  161. end
  162.  
  163. -- ==========================================
  164. -- SESSION TRACKING (NEW - Engagement)
  165. -- ==========================================
  166. local session = {
  167. spins = 0,
  168. consecutiveLosses = 0,
  169. cooldownSpins = 0,
  170. bestWin = 0,
  171. totalWon = 0,
  172. totalLost = 0
  173. }
  174.  
  175. local function getStreakModifier()
  176. local mod = 1.0
  177.  
  178. -- Loss streak catch-up: boost win chances after losing streak
  179. if session.consecutiveLosses >= 10 then
  180. mod = mod * 1.25
  181. elseif session.consecutiveLosses >= 7 then
  182. mod = mod * 1.15
  183. elseif session.consecutiveLosses >= 5 then
  184. mod = mod * 1.10
  185. end
  186.  
  187. -- Post-win cooldown: slight reduction after wins
  188. if session.cooldownSpins > 0 then
  189. mod = mod * 0.85
  190. session.cooldownSpins = session.cooldownSpins - 1
  191. end
  192.  
  193. return math.max(0.5, math.min(1.5, mod))
  194. end
  195.  
  196. -- ==========================================
  197. -- GAME LOGIC & CONFIG
  198. -- ==========================================
  199. -- Colors ranked: Red (best) > Green > Blue > Yellow > Magenta (common)
  200. local colorRank = {
  201. [colors.red] = 5,
  202. [colors.green] = 4,
  203. [colors.blue] = 3,
  204. [colors.yellow] = 2,
  205. [colors.magenta] = 1
  206. }
  207. local rankColor = {colors.magenta, colors.yellow, colors.blue, colors.green, colors.red}
  208. local colorNames = {
  209. [colors.red] = "RED",
  210. [colors.green] = "GRN",
  211. [colors.blue] = "BLU",
  212. [colors.yellow] = "YEL",
  213. [colors.magenta] = "MAG"
  214. }
  215.  
  216. -- Paytable: Adjusted for ~90% RTP (2.7 EV on 3.0 cost)
  217. -- House edge: 10%
  218. -- EV = sum(win * chance/100)
  219. -- Red: 20 * 0.5% = 0.10
  220. -- Green: 12 * 1.5% = 0.18
  221. -- Blue: 8 * 3.0% = 0.24
  222. -- Yellow: 5 * 8.0% = 0.40
  223. -- Magenta: 4 * 20% = 0.80
  224. -- Total EV = 1.72 (~57% RTP base, feels like wins but house profits)
  225. local paytable = {
  226. {color=colors.red, win=20, chance=0.5, name="JACKPOT"},
  227. {color=colors.green, win=12, chance=1.5, name="BIG WIN"},
  228. {color=colors.blue, win=8, chance=3.0, name="NICE WIN"},
  229. {color=colors.yellow, win=5, chance=8.0, name="WIN"},
  230. {color=colors.magenta, win=4, chance=20.0, name="SMALL WIN"},
  231. }
  232.  
  233. -- Near miss chance (30% of losses become near misses visually)
  234. local NEAR_MISS_CHANCE = 30
  235.  
  236. local function getOutcome(rtpMod, streakMod, playerId)
  237. local totalMod = rtpMod * streakMod
  238. local rand = math.random() * 100
  239. local cumulative = 0
  240.  
  241. for _, item in ipairs(paytable) do
  242. local modChance = item.chance * totalMod
  243. cumulative = cumulative + modChance
  244. if rand <= cumulative then
  245. -- Jackpot cooldown check: demote to BIG WIN if on cooldown
  246. if item.name == "JACKPOT" and not canPlayerWinJackpot(playerId) then
  247. return paytable[2] -- BIG WIN (green) instead
  248. end
  249. return item
  250. end
  251. end
  252. return nil -- Loss
  253. end
  254.  
  255. -- ==========================================
  256. -- VISUAL GENERATION
  257. -- ==========================================
  258. local function generateWheel(outcome, isNearMiss, nearMissColor)
  259. local wheels = {{}, {}, {}}
  260.  
  261. -- Simple random color picker
  262. local function randColor()
  263. return rankColor[math.random(1,5)]
  264. end
  265.  
  266. -- Get a color that is definitely different from the given color
  267. local function differentColor(exclude)
  268. local c = randColor()
  269. while c == exclude do
  270. c = randColor()
  271. end
  272. return c
  273. end
  274.  
  275. if outcome then
  276. -- WIN: All 3 middle row MUST be the winning color
  277. local winC = outcome.color
  278. wheels[1][2] = winC
  279. wheels[2][2] = winC
  280. wheels[3][2] = winC
  281.  
  282. -- Fill other rows with random colors (avoiding win color for visual variety)
  283. for i=1,3 do
  284. wheels[i][1] = differentColor(winC)
  285. wheels[i][3] = differentColor(winC)
  286. end
  287.  
  288. elseif isNearMiss and nearMissColor then
  289. -- NEAR MISS: Exactly 2 matching on middle row, 1 MUST be different
  290. local missC = nearMissColor
  291. local diffPos = math.random(1,3)
  292.  
  293. for i=1,3 do
  294. if i == diffPos then
  295. -- This one MUST be different
  296. wheels[i][2] = differentColor(missC)
  297. else
  298. wheels[i][2] = missC
  299. end
  300. end
  301.  
  302. -- Fill other rows with random colors
  303. for i=1,3 do
  304. wheels[i][1] = randColor()
  305. wheels[i][3] = randColor()
  306. end
  307.  
  308. else
  309. -- LOSS: Middle row MUST NOT have 3 matching colors
  310. -- Strategy: Force the third wheel to be different from both others
  311. local c1 = randColor()
  312. local c2 = randColor()
  313. local c3 = randColor()
  314.  
  315. -- If all 3 happen to match, force c3 to be different
  316. if c1 == c2 and c2 == c3 then
  317. c3 = differentColor(c1)
  318. end
  319.  
  320. wheels[1][2] = c1
  321. wheels[2][2] = c2
  322. wheels[3][2] = c3
  323.  
  324. -- VERIFICATION: Double-check that we don't have 3 matching
  325. -- This should never trigger, but guarantees correctness
  326. if wheels[1][2] == wheels[2][2] and wheels[2][2] == wheels[3][2] then
  327. wheels[3][2] = differentColor(wheels[1][2])
  328. end
  329.  
  330. -- Fill other rows with random colors
  331. for i=1,3 do
  332. wheels[i][1] = randColor()
  333. wheels[i][3] = randColor()
  334. end
  335. end
  336.  
  337. return wheels
  338. end
  339.  
  340. local function drawWheelFrame(w_data, balance, showPayline)
  341. local w, h = term.getSize()
  342.  
  343. term.setBackgroundColor(colors.black)
  344. term.clear()
  345.  
  346. -- Header
  347. paintutils.drawFilledBox(1, 1, w, 1, colors.blue)
  348. term.setCursorPos(math.floor((w-10)/2), 1)
  349. term.setTextColor(colors.white)
  350. term.setBackgroundColor(colors.blue)
  351. term.write("SLOTS 99")
  352.  
  353. term.setBackgroundColor(colors.black)
  354.  
  355. -- Balance and Cost
  356. term.setTextColor(colors.yellow)
  357. term.setCursorPos(2, 3)
  358. term.write("Bal: " .. string.format("%.1f", balance))
  359. term.setCursorPos(w-10, 3)
  360. term.write("Cost: " .. SPIN_COST)
  361.  
  362. -- Grid
  363. local startX = math.floor((w - 21)/2)
  364. local startY = math.floor((h - 9)/2)
  365.  
  366. -- Frame
  367. term.setTextColor(colors.gray)
  368. for y=startY, startY+8 do
  369. term.setCursorPos(startX, y)
  370. if y==startY or y==startY+8 then
  371. term.write(string.rep("-", 21))
  372. else
  373. term.write("|" .. string.rep(" ", 19) .. "|")
  374. end
  375. end
  376.  
  377. -- Payline pointer
  378. if showPayline then
  379. term.setTextColor(colors.red)
  380. term.setCursorPos(startX-2, startY+4)
  381. term.write(">")
  382. term.setCursorPos(startX+22, startY+4)
  383. term.write("<")
  384. end
  385.  
  386. -- Cells
  387. for i=1,3 do
  388. for j=1,3 do
  389. local cellColor = w_data[i][j] or colors.gray
  390. term.setCursorPos(startX + 2 + (i-1)*6, startY + 1 + (j-1)*2 + 1)
  391. term.setBackgroundColor(cellColor)
  392. term.write(" ")
  393. term.setCursorPos(startX + 2 + (i-1)*6, startY + 2 + (j-1)*2 + 1)
  394. term.write(" ")
  395. term.setBackgroundColor(colors.black)
  396. end
  397. end
  398. end
  399.  
  400. local function spinVisual(finalWheels, balance)
  401. local duration = 2.0
  402. local start = os.clock()
  403. local tempWheels = {{},{},{}}
  404.  
  405. for i=1,3 do
  406. for j=1,3 do
  407. tempWheels[i][j] = rankColor[math.random(1,5)]
  408. end
  409. end
  410.  
  411. while os.clock() - start < duration do
  412. local t = os.clock() - start
  413. for i=1,3 do
  414. if t < duration - (0.5 * (3-i)) then
  415. for j=1,3 do
  416. tempWheels[i][j] = rankColor[math.random(1,5)]
  417. end
  418. else
  419. for j=1,3 do
  420. tempWheels[i][j] = finalWheels[i][j]
  421. end
  422. end
  423. end
  424. drawWheelFrame(tempWheels, balance, true)
  425. playSoundFile("minecraft:block.note_block.hat", 0.5, 2)
  426. sleep(0.1)
  427. end
  428. drawWheelFrame(finalWheels, balance, true)
  429. end
  430.  
  431. -- ==========================================
  432. -- BET SCREEN WITH PAYTABLE (NEW)
  433. -- ==========================================
  434. local function drawBetScreen(balance)
  435. local w, h = term.getSize()
  436.  
  437. term.setBackgroundColor(colors.black)
  438. term.clear()
  439.  
  440. -- Header
  441. paintutils.drawFilledBox(1, 1, w, 1, colors.blue)
  442. term.setCursorPos(math.floor((w-10)/2), 1)
  443. term.setTextColor(colors.white)
  444. term.setBackgroundColor(colors.blue)
  445. term.write("SLOTS 99")
  446.  
  447. term.setBackgroundColor(colors.black)
  448.  
  449. -- Balance and Cost (top)
  450. term.setTextColor(colors.yellow)
  451. term.setCursorPos(2, 3)
  452. term.write("Balance: " .. string.format("%.1f", balance) .. " Eddies")
  453. term.setCursorPos(w-12, 3)
  454. term.write("Spin: " .. SPIN_COST)
  455.  
  456. -- Draw idle wheel preview (left side)
  457. local previewWheels = {{},{},{}}
  458. for i=1,3 do
  459. for j=1,3 do
  460. previewWheels[i][j] = rankColor[i + j - 1] or rankColor[((i+j) % 5) + 1]
  461. end
  462. end
  463.  
  464. local startX = 3
  465. local startY = 5
  466.  
  467. -- Mini wheel frame
  468. term.setTextColor(colors.gray)
  469. term.setCursorPos(startX, startY)
  470. term.write("+-------------+")
  471. for row=1,3 do
  472. term.setCursorPos(startX, startY + row)
  473. term.write("|")
  474. for col=1,3 do
  475. term.setBackgroundColor(previewWheels[col][row])
  476. term.write(" ")
  477. term.setBackgroundColor(colors.black)
  478. if col < 3 then term.write(" ") end
  479. end
  480. term.write("|")
  481. end
  482. term.setCursorPos(startX, startY + 4)
  483. term.write("+-------------+")
  484.  
  485. -- Payline indicator
  486. term.setTextColor(colors.red)
  487. term.setCursorPos(startX - 1, startY + 2)
  488. term.write(">")
  489. term.setCursorPos(startX + 15, startY + 2)
  490. term.write("<")
  491.  
  492. -- Paytable (right side)
  493. local ptX = 22
  494. local ptY = 5
  495.  
  496. term.setTextColor(colors.white)
  497. term.setCursorPos(ptX, ptY)
  498. term.write("=== PAYTABLE ===")
  499.  
  500. for idx, entry in ipairs(paytable) do
  501. term.setCursorPos(ptX, ptY + idx)
  502. term.setBackgroundColor(entry.color)
  503. term.setTextColor(colors.white)
  504. term.write(" " .. colorNames[entry.color] .. " ")
  505. term.setBackgroundColor(colors.black)
  506. term.setTextColor(colors.yellow)
  507. term.write(" x3 = ")
  508. term.setTextColor(colors.lime)
  509. term.write(string.format("%2d", entry.win))
  510. term.setTextColor(colors.gray)
  511. term.write(" (" .. string.format("%.1f", entry.chance) .. "%)")
  512. end
  513.  
  514. -- Session stats
  515. term.setCursorPos(ptX, ptY + 7)
  516. term.setTextColor(colors.lightGray)
  517. term.write("--- Session ---")
  518. term.setCursorPos(ptX, ptY + 8)
  519. term.write("Spins: " .. session.spins)
  520. term.setCursorPos(ptX, ptY + 9)
  521. term.write("Best Win: " .. session.bestWin)
  522.  
  523. -- Controls
  524. term.setCursorPos(2, h-1)
  525. term.setTextColor(colors.lime)
  526. term.write("[ENTER]")
  527. term.setTextColor(colors.white)
  528. term.write(" SPIN")
  529.  
  530. term.setCursorPos(18, h-1)
  531. term.setTextColor(colors.red)
  532. term.write("[Q]")
  533. term.setTextColor(colors.white)
  534. term.write(" QUIT")
  535.  
  536. -- Win rate hint
  537. term.setCursorPos(2, h)
  538. term.setTextColor(colors.gray)
  539. term.write("Match 3 on center line to win!")
  540. end
  541.  
  542. -- ==========================================
  543. -- RESULT DISPLAY
  544. -- ==========================================
  545. local function showResult(resultType, winAmount, w, h)
  546. local msg = ""
  547. local msgColor = colors.white
  548.  
  549. if resultType == "win" then
  550. msg = "WINNER! +" .. winAmount
  551. if winAmount >= 20 then
  552. msgColor = colors.orange
  553. elseif winAmount >= 10 then
  554. msgColor = colors.yellow
  555. else
  556. msgColor = colors.lime
  557. end
  558. elseif resultType == "nearmiss" then
  559. msg = "SO CLOSE!"
  560. msgColor = colors.orange
  561. else
  562. msg = "Better luck next time!"
  563. msgColor = colors.gray
  564. end
  565.  
  566. term.setCursorPos(math.floor((w - #msg) / 2), 5)
  567. term.setTextColor(msgColor)
  568. term.write(msg)
  569. end
  570.  
  571. -- ==========================================
  572. -- BALANCE HELPERS (UNCHANGED SERVER CALLS)
  573. -- ==========================================
  574. local function updateBalance(studentId, password, action, amount)
  575. local response = sendRequest({type = "update_balance", studentId = studentId, password = password, action = action, amount = amount})
  576. if response and response.type == "balance_update_response" and response.success then
  577. return tonumber(response.result)
  578. else
  579. return nil
  580. end
  581. end
  582.  
  583. local function getBalance(studentId, password)
  584. local response = sendRequest({type = "get_balance", studentId = studentId, password = password})
  585. if response and response.type == "balance_response" then
  586. return tonumber(response.balance)
  587. else
  588. return nil
  589. end
  590. end
  591.  
  592. -- Atomic gambling transaction: handles bet and payout in one server call
  593. -- Returns: success, playerBalance, errorMessage
  594. local function gamblingTrans(playerId, playerPw, betAmount, payoutAmount)
  595. local r = sendRequest({
  596. type = "gambling_transaction",
  597. studentId = playerId,
  598. password = playerPw,
  599. houseId = adminId,
  600. housePw = adminPassword,
  601. betAmount = betAmount,
  602. payoutAmount = payoutAmount
  603. })
  604. if r and r.success then
  605. return true, tonumber(r.playerBalance), nil
  606. else
  607. return false, nil, (r and r.message) or "Gambling transaction failed"
  608. end
  609. end
  610.  
  611. -- ==========================================
  612. -- MAIN GAME LOOP
  613. -- ==========================================
  614. local function playRound(u, p)
  615. local bal = getBalance(u, p)
  616. if not bal then
  617. print("Failed to get balance")
  618. return
  619. end
  620.  
  621. while true do
  622. local w, h = term.getSize()
  623.  
  624. -- Draw bet screen with wheel preview and paytable
  625. drawBetScreen(bal)
  626.  
  627. local e, k = os.pullEvent("key")
  628. if k == keys.enter then
  629. if bal < SPIN_COST then
  630. term.setCursorPos(2, h-2)
  631. term.setTextColor(colors.red)
  632. term.write("Insufficient Funds!")
  633. sleep(1.5)
  634. else
  635. -- 1. Update session
  636. session.spins = session.spins + 1
  637.  
  638. -- 2. Load stats and calculate modifiers
  639. local stats = loadStats()
  640. stats.totalWagered = stats.totalWagered + SPIN_COST
  641.  
  642. local rtpMod = getRTPModifier(stats)
  643. local streakMod = getStreakModifier()
  644.  
  645. -- 3. Determine outcome BEFORE any money moves
  646. local outcome = getOutcome(rtpMod, streakMod, u)
  647. local winAmt = outcome and outcome.win or 0
  648.  
  649. -- 4. Determine if near miss (only on loss)
  650. local isNearMiss = false
  651. local nearMissColor = nil
  652. if not outcome and math.random(1, 100) <= NEAR_MISS_CHANCE then
  653. isNearMiss = true
  654. local nmIdx = math.random(1, 3)
  655. nearMissColor = paytable[nmIdx].color
  656. end
  657.  
  658. -- 5. Generate and animate wheels (show optimistic balance)
  659. local displayBal = bal - SPIN_COST
  660. local finalWheels = generateWheel(outcome, isNearMiss, nearMissColor)
  661. spinVisual(finalWheels, displayBal)
  662.  
  663. -- 6. Execute atomic gambling transaction
  664. local success, newBal, err = gamblingTrans(u, p, SPIN_COST, winAmt)
  665.  
  666. if success then
  667. bal = newBal
  668. stats.totalPayout = stats.totalPayout + winAmt
  669. saveStats(stats)
  670.  
  671. if winAmt > 0 then
  672. -- WIN
  673. session.consecutiveLosses = 0
  674. session.cooldownSpins = math.floor(winAmt / 10) + 1
  675. session.totalWon = session.totalWon + winAmt
  676. if winAmt > session.bestWin then
  677. session.bestWin = winAmt
  678. end
  679.  
  680. if winAmt >= 20 then
  681. -- Record jackpot win for cooldown tracking
  682. recordJackpotWin(u)
  683. playSoundFile("minecraft:entity.ender_dragon.death", 1, 1)
  684. elseif winAmt >= 10 then
  685. playSoundFile("minecraft:entity.player.levelup", 1, 1)
  686. else
  687. playSoundFile("minecraft:block.note_block.bell", 1, 1.5)
  688. end
  689.  
  690. showResult("win", winAmt, w, h)
  691. elseif isNearMiss then
  692. -- NEAR MISS
  693. session.consecutiveLosses = session.consecutiveLosses + 1
  694. session.totalLost = session.totalLost + SPIN_COST
  695.  
  696. playSoundFile("minecraft:block.note_block.pling", 0.8, 0.5)
  697. showResult("nearmiss", 0, w, h)
  698. else
  699. -- REGULAR LOSS
  700. session.consecutiveLosses = session.consecutiveLosses + 1
  701. session.totalLost = session.totalLost + SPIN_COST
  702.  
  703. playSoundFile("minecraft:block.note_block.bass", 0.6, 0.5)
  704. showResult("loss", 0, w, h)
  705. end
  706. else
  707. -- Transaction failed - no money was moved
  708. session.spins = session.spins - 1 -- Revert spin count
  709. stats.totalWagered = stats.totalWagered - SPIN_COST
  710. saveStats(stats)
  711.  
  712. playSoundFile("minecraft:block.note_block.bass", 0.6, 0.5)
  713. term.setCursorPos(2, 5)
  714. term.setTextColor(colors.red)
  715. term.write("Transaction failed: " .. (err or "Unknown"))
  716. term.setCursorPos(2, 6)
  717. term.setTextColor(colors.yellow)
  718. term.write("No money was lost. Try again.")
  719.  
  720. -- Refresh balance from server
  721. bal = getBalance(u, p) or bal
  722. end
  723.  
  724. sleep(1.5)
  725. end
  726. elseif k == keys.q then
  727. -- Show session summary before returning to login
  728. term.clear()
  729. term.setCursorPos(1, 1)
  730. term.setTextColor(colors.white)
  731. print("=== Session Summary ===")
  732. print("")
  733. print("Total Spins: " .. session.spins)
  734. print("Best Win: " .. session.bestWin .. " Eddies")
  735. print("Total Won: " .. session.totalWon .. " Eddies")
  736. local totalSpent = session.spins * SPIN_COST
  737. print("Total Spent: " .. totalSpent .. " Eddies")
  738. local net = session.totalWon - totalSpent
  739. if net >= 0 then
  740. term.setTextColor(colors.lime)
  741. print("Net: +" .. string.format("%.1f", net) .. " Eddies")
  742. else
  743. term.setTextColor(colors.red)
  744. print("Net: " .. string.format("%.1f", net) .. " Eddies")
  745. end
  746. print("")
  747. term.setTextColor(colors.gray)
  748. print("Thanks for playing!")
  749. print("")
  750. term.setTextColor(colors.lightGray)
  751. print("Press any key to continue...")
  752. os.pullEvent("key")
  753. break
  754. end
  755. end
  756. end
  757.  
  758. -- ==========================================
  759. -- LOGIN (UNCHANGED)
  760. -- ==========================================
  761. local function login()
  762. term.clear()
  763. term.setCursorPos(1,1)
  764. term.setTextColor(colors.blue)
  765. print("========== SLOTS 99 ==========")
  766. term.setTextColor(colors.white)
  767. print("")
  768. write("ID: ")
  769. local u = read()
  770. write("Password: ")
  771. local p = read("*")
  772.  
  773. local r = sendRequest({type="login", studentId=u, password=p})
  774. if r and r.success then
  775. return u, p
  776. end
  777.  
  778. term.setTextColor(colors.red)
  779. print("Login failed!")
  780. sleep(2)
  781. return nil
  782. end
  783.  
  784. -- ==========================================
  785. -- ENTRY POINT
  786. -- ==========================================
  787. local function resetSession()
  788. session.spins = 0
  789. session.consecutiveLosses = 0
  790. session.cooldownSpins = 0
  791. session.bestWin = 0
  792. session.totalWon = 0
  793. session.totalLost = 0
  794. end
  795.  
  796. -- Main loop: login -> play -> summary -> back to login
  797. while true do
  798. resetSession()
  799. local u, p = login()
  800. if u then
  801. playRound(u, p)
  802. end
  803. -- After playRound ends (user pressed Q), loop back to login
  804. end
  805.  
Add Comment
Please, Sign In to add comment