Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- leftMonitor = peripheral.wrap("left")
- topMonitor = peripheral.wrap("top")
- diskDrive = peripheral.wrap("right")
- ditoCreds = 30 -- Credits per diamond
- function findItemSlot(itemName)
- local chest = peripheral.wrap("minecraft:chest_0")
- for slot = 1, 27 do
- local item = chest.getItemDetail(slot)
- if item and item.name == itemName then
- return slot
- end
- end
- topMonitor.write("diamonds not found in chest.")
- return nil
- end
- function deposit(player)
- local chest2 = peripheral.wrap("minecraft:chest_2")
- local chest = peripheral.wrap("minecraft:chest_0")
- for slot, item in pairs(chest.list()) do
- print(("%d x %s in slot %s"):format(item.count, item.name, slot))
- end
- local desiredItemName = "minecraft:diamond" -- item to deposit
- local itemFound = false
- local totalDiamonds = 0
- for i = 1, chest.size() do
- local item = chest.getItemDetail(i)
- if item and item.name == desiredItemName then
- if not itemFound then
- topMonitor.setCursorPos(1, 4)
- end
- end
- end
- local desiredItemName = "minecraft:diamond" -- item to deposit
- local diamondSlots = {}
- -- First, find all diamond stacks and sum their counts
- for i = 1, chest.size() do
- local item = chest.getItemDetail(i)
- if item and item.name == desiredItemName then
- totalDiamonds = totalDiamonds + item.count
- table.insert(diamondSlots, {slot = i, count = item.count})
- end
- end
- if totalDiamonds > 0 then
- topMonitor.setCursorPos(1, 4)
- topMonitor.write("Depositing " .. totalDiamonds .. " of diamonds")
- topMonitor.setCursorPos(1, 5)
- topMonitor.write("Credits to be added: " .. (totalDiamonds * ditoCreds))
- -- Move all diamond stacks to chest2
- for _, entry in ipairs(diamondSlots) do
- chest.pushItems(peripheral.getName(chest2), entry.slot, entry.count)
- end
- -- Add credits for all deposited diamonds
- local newCredits = loadCreditsFromDisk() + (totalDiamonds * ditoCreds)
- saveCreditsToDisk(newCredits)
- savePlayerData(loadUsernameFromDisk(), newCredits)
- else
- topMonitor.setCursorPos(1, 6)
- topMonitor.write("No diamonds found in the chest.")
- end
- sleep(3)
- displayMain(loadUsernameFromDisk())
- end
- function withdraw()
- -- chest2 is the storage (source), chest is the withdrawal location (destination)
- local chest2 = peripheral.wrap("minecraft:chest_2")
- local chest = peripheral.wrap("minecraft:chest_0")
- drawWithdrawKeyboard()
- local amount = handleWithdrawalKeyboardInput()
- -- We assume the user is standing next to chest_0 (destination) and that it has space.
- -- The slot number returned here is usually where the item *already* is or where it *should go*.
- -- The push/pull functions handle finding an empty slot automatically if you don't specify the destination slot index.
- while true do
- if amount > loadCreditsFromDisk() then
- topMonitor.setCursorPos(1, 6)
- topMonitor.write("Insufficient credits. Please enter a smaller amount.")
- sleep(2)
- topMonitor.clearLine(1, 6)
- drawWithdrawKeyboard()
- amount = handleWithdrawalKeyboardInput()
- else
- break
- end
- end
- -- Calculate the exact diamonds required, then convert to an integer (floor)
- local diamondsToWithdraw = math.floor(amount / ditoCreds)
- topMonitor.clearLine(1, 3)
- topMonitor.clearLine(1, 5)
- topMonitor.clearLine(1,9)
- topMonitor.setCursorPos(1, 3)
- topMonitor.write("Withdrawing " .. diamondsToWithdraw .. " diamonds.")
- -- The primary logic should be a single push or pull operation, optimized for moving from source to destination.
- -- We call 'pushItems' on the source (chest2) to move items into the destination (chest)
- local actualMovedCount = 0
- -- Iterate over chest2 slots to find diamonds
- for slot = 1, chest2.size() do
- local item = chest2.getItemDetail(slot)
- if item and item.name == "minecraft:diamond" then
- local toMove = math.min(item.count, diamondsToWithdraw - actualMovedCount)
- -- Push from chest2 into chest
- -- Arguments: target name, source slot, limit
- local moved = chest2.pushItems(peripheral.getName(chest), slot, toMove)
- actualMovedCount = actualMovedCount + (moved or 0)
- if actualMovedCount >= diamondsToWithdraw then break end
- end
- end
- -- Update credits based on what was *actually* moved, not just requested
- if actualMovedCount > 0 then
- local newCredits = - (actualMovedCount * ditoCreds)
- saveCreditsToDisk(newCredits)
- savePlayerData(loadUsernameFromDisk(), newCredits)
- end
- sleep(3)
- displayMain(loadUsernameFromDisk())
- end
- function Log_Off()
- topMonitor.clear()
- topMonitor.setCursorPos(1, 1)
- topMonitor.write("Logging off... Please remove your disk.")
- sleep(5)
- topMonitor.clear()
- leftMonitor.clear()
- setUpMonitors()
- waitForDisk()
- end
- function setUpMonitors()
- leftMonitor.clear()
- leftMonitor.setTextScale(1)
- topMonitor.clear()
- topMonitor.setTextScale(1)
- topMonitor.setBackgroundColor(colors.black)
- topMonitor.setTextColor(colors.red)
- end
- function drawKeyboard()
- topMonitor.setBackgroundColor(colors.black)
- topMonitor.setTextColor(colors.red)
- -- Draw letters (3 rows of letters)
- local letters = {
- "qwertyuiop",
- "asdfghjkl",
- "zxcvbnm"
- }
- -- Draw the letters on the top monitor (adjust x, y positions for rows)
- for row = 1, #letters do
- local y = row + 4 -- Start the keyboard lower on the screen (row 5)
- for i = 1, #letters[row] do
- local letter = letters[row]:sub(i, i)
- local x = (i * 3) - 2 -- Space out letters and adjust for alignment
- topMonitor.setCursorPos(x, y)
- topMonitor.write("[" .. letter .. "]")
- end
- end
- -- Draw numbers
- local numbers = "1234567890"
- topMonitor.setCursorPos(1, 3) -- Numbers row above letters
- for i = 1, #numbers do
- local x = (i * 3) - 2 -- Space out numbers properly
- topMonitor.setCursorPos(x, 3)
- topMonitor.write("[" .. numbers:sub(i, i) .. "]")
- end
- -- Draw special controls (Backspace and Submit)
- topMonitor.setCursorPos(1, 9)
- topMonitor.write("[Backspace] [Submit]")
- -- Draw the spacebar (new addition)
- topMonitor.setCursorPos(1, 8) -- Place it one row above Backspace/Submit
- topMonitor.write("[ Space ]") -- Large button for space
- end
- function drawWithdrawKeyboard()
- topMonitor.setBackgroundColor(colors.black)
- topMonitor.setTextColor(colors.white)
- -- Draw numbers for withdrawal amounts
- local numbers = {
- "123",
- "456",
- "789",
- "0"
- }
- -- Draw each row of numbers at a different y-coordinate
- for row = 1, #numbers do
- local y = row + 4 -- Start at y=5 for the first row
- for i = 1, #numbers[row] do
- local x = (i * 3) - 2 -- Space out numbers properly
- topMonitor.setCursorPos(x, y)
- topMonitor.write("[" .. numbers[row]:sub(i, i) .. "]")
- end
- end
- -- Draw special controls (Backspace and Submit)
- topMonitor.setCursorPos(1, 9)
- topMonitor.write("[Backspace] [Submit]")
- end
- function handleWithdrawalKeyboardInput()
- topMonitor.setBackgroundColor(colors.black)
- topMonitor.setTextColor(colors.white)
- local amount = "" -- The entered amount (masked)
- while true do
- topMonitor.setBackgroundColor(colors.black)
- topMonitor.setTextColor(colors.red)
- -- Redraw the "Amount:" label before every key press update
- topMonitor.setCursorPos(1, 3)
- topMonitor.write("Amount:") -- Keep the label on the screen
- -- Wait for a touch event on the monitor
- local event, side, x, y = os.pullEvent("monitor_touch")
- -- Handle number inputs
- if y >= 5 and y <= 8 then
- local numbers = {
- "123",
- "456",
- "789",
- "0"
- }
- local row = y - 4
- local numberRow = numbers[row]
- if numberRow then
- local numberIndex = math.ceil(x / 3)
- local number = numberRow:sub(numberIndex, numberIndex)
- if number and number ~= "" then
- amount = amount .. number
- updateWithdrawDisplay(amount)
- end
- end
- -- Handle special buttons (Backspace and Submit)
- elseif y == 9 then
- if x >= 1 and x <= 11 then -- Backspace button
- amount = amount:sub(1, -2)
- updateWithdrawDisplay(amount)
- elseif x >= 13 and x <= 20 then -- Submit button
- local num = tonumber(amount)
- if num and num > 0 then
- return num -- Return the entered amount as a number
- else
- topMonitor.setCursorPos(1, 10)
- topMonitor.write("Enter a valid amount.")
- sleep(1)
- topMonitor.setCursorPos(1, 10)
- topMonitor.clearLine()
- end
- end
- end
- end
- end
- function updateWithdrawDisplay(amount)
- topMonitor.setCursorPos(8, 3)
- topMonitor.clearLine()
- topMonitor.write(amount)
- end
- function resetOnDiskRemove()
- while true do
- -- Wait for the "disk" or "disk_eject" event
- local event, side = os.pullEvent()
- -- If the event is related to the disk and it has been removed
- if event == "disk_eject" or event == "disk" then
- if not diskDrive.isDiskPresent() then
- -- Clear any stored global variables related to user info
- username = nil
- password = nil
- hint = nil
- credits = 0
- -- Clear monitors
- topMonitor.clear()
- leftMonitor.clear()
- topMonitor.setCursorPos(1, 1)
- topMonitor.write("Disk removed! Returning to main menu...")
- sleep(2)
- main() -- Reset to main menu or startup script
- end
- end
- end
- end
- function handleKeyboardInput(hint)
- topMonitor.setBackgroundColor(colors.black)
- topMonitor.setTextColor(colors.red)
- local password = "" -- The entered password (masked)
- while true do
- topMonitor.setBackgroundColor(colors.black)
- topMonitor.setTextColor(colors.red)
- -- Redraw the "Password:" label before every key press update
- topMonitor.setCursorPos(1, 1)
- topMonitor.write("Password: ") -- Keep the label on the screen
- -- Display the password hint below the keyboard if available
- if hint and hint ~= "" then
- topMonitor.setCursorPos(1, 11)
- topMonitor.write("Password Hint: " .. hint)
- end
- -- Wait for a touch event on the monitor
- local event, side, x, y = os.pullEvent("monitor_touch")
- -- DEBUG: Print the touch event to the console for debugging
- --print("Touch event: x = " .. x .. ", y = " .. y)
- -- Handle letter inputs
- if y >= 5 and y <= 7 then
- local row = y - 4
- local letters = {
- "qwertyuiop", -- First row of letters
- "asdfghjkl", -- Second row of letters
- "zxcvbnm" -- Third row of letters
- }
- local letterIndex = math.ceil(x / 3) -- Correct letter position based on new spacing
- local letter = letters[row]:sub(letterIndex, letterIndex)
- -- DEBUG: Print the selected letter
- --print("Selected letter: " .. letter)
- if letter and letter ~= "" then
- password = password .. letter
- updatePasswordDisplay(password)
- end
- -- Handle number inputs
- elseif y == 3 then
- local numbers = "1234567890"
- local numberIndex = math.ceil(x / 3)
- local number = numbers:sub(numberIndex, numberIndex)
- -- DEBUG: Print the selected number
- --print("Selected number: " .. number)
- if number and number ~= "" then
- password = password .. number
- updatePasswordDisplay(password)
- end
- -- Handle special buttons (Backspace and Submit)
- elseif y == 9 then
- if x >= 1 and x <= 11 then -- Backspace button
- password = password:sub(1, -2)
- updatePasswordDisplay(password)
- -- DEBUG: Print Backspace press
- --print("Pressed: [Backspace]")
- elseif x >= 13 and x <= 20 then -- Submit button
- -- DEBUG: Print Submit press and the entered password
- --print("Pressed: [Submit], Password: " .. password)
- return password -- Return the entered password
- end
- end
- end
- end
- -- Update the password display (masked with *)
- function updatePasswordDisplay(password)
- topMonitor.setCursorPos(11, 1) -- Password display starts at column 11
- topMonitor.clearLine()
- topMonitor.write(string.rep("*", #password))
- end
- -- Main menu buttons
- function drawMainMenu()
- leftMonitor.setBackgroundColor(colors.black)
- leftMonitor.setTextColor(colors.red)
- leftMonitor.clear()
- leftMonitor.setCursorPos(1, 1)
- leftMonitor.write("[deposit]") -- Button for Item Shop
- leftMonitor.setCursorPos(1, 2)
- leftMonitor.write("[withdraw]") -- Button for Slot Machine
- leftMonitor.setCursorPos(1, 3)
- leftMonitor.write("[Log_Off]") -- Button to exit to disk prompt
- end
- -- Handle Main Menu touch events
- function handleMainMenuTouch(player)
- while true do
- local event, side, x, y = os.pullEvent("monitor_touch")
- if side == "left" then
- if y == 1 then
- deposit(player) -- Launch Item Shop
- elseif y == 2 then
- withdraw(player) -- Launch Slot Machine
- elseif y == 3 then
- Log_Off(player) -- Exit to disk prompt
- end
- end
- end
- end
- -- Display welcome message and credit info on top monitor
- function displayMain(player)
- -- Load credits from the floppy disk (source of truth)
- local credits = loadCreditsFromDisk()
- -- Keep player data file in sync
- savePlayerData(player, credits)
- topMonitor.clear()
- topMonitor.setCursorPos(1, 1)
- topMonitor.write("Welcome, " .. player)
- topMonitor.setCursorPos(1, 2)
- topMonitor.write("Credits: " .. credits)
- end
- -- Deprecated: Save Player Data to Disk (use saveCreditsToDisk instead)
- function savePlayerData(player, credits)
- -- No longer used; kept for compatibility
- saveCreditsToDisk(credits)
- end
- -- Deprecated: Load Player Data from Disk (use loadCreditsFromDisk instead)
- function loadPlayerData(player)
- -- No longer used; kept for compatibility
- return loadCreditsFromDisk()
- end
- -- Save credentials (username, password, hint) to disk
- function saveCredentials(username, password, hint)
- local diskPath = diskDrive.getMountPath()
- if diskPath then
- local file = fs.open(diskPath .. "/" .. username .. "_credentials.txt", "w")
- file.writeLine(password) -- Ensure password is written as a string
- file.writeLine(hint)
- file.close()
- diskDrive.setDiskLabel(username)
- end
- end
- -- Load credentials from disk
- function loadCredentials(username)
- local diskPath = diskDrive.getMountPath()
- if diskPath and fs.exists(diskPath .. "/" .. username .. "_credentials.txt") then
- local file = fs.open(diskPath .. "/" .. username .. "_credentials.txt", "r")
- local password = file.readLine() -- Read password as string
- local hint = file.readLine()
- file.close()
- return password, hint
- else
- return nil, nil
- end
- end
- -- Function to save credits directly to the floppy disk in the drive
- function saveCreditsToDisk(credits)
- local diskPath = diskDrive.getMountPath() -- Get the disk's path
- if diskPath then
- local file = fs.open(diskPath .. "disk/credits.txt", "w") -- Open or create 'credits.txt'
- file.writeLine(tostring(credits)) -- Write the credits as a string
- file.close()
- else
- print("No disk found.")
- end
- end
- -- Function to load credits directly from the floppy disk
- function loadCreditsFromDisk()
- local diskPath = diskDrive.getMountPath()
- if diskPath and fs.exists(diskPath .. "disk/credits.txt") then
- local file = fs.open(diskPath .. "disk/credits.txt", "r")
- local credits = tonumber(file.readAll()) -- Read the credits and convert to a number
- file.close()
- return credits
- else
- return 0 -- Default to 0 credits if no file exists
- end
- end
- -- Function to load the username from the floppy disk label
- function loadUsernameFromDisk()
- local diskPath = diskDrive.getMountPath()
- if diskPath then
- return diskDrive.getDiskLabel() or "Unknown User"
- else
- return "Unknown User"
- end
- end
- -- Function to draw prompts and keyboard for setting up new player (no clearing topMonitor every time)
- function drawSetupScreen(prompt)
- topMonitor.setBackgroundColor(colors.black)
- topMonitor.setTextColor(colors.red)
- topMonitor.setCursorPos(1, 1)
- topMonitor.write(prompt) -- Display the correct prompt (username, password, or hint)
- -- Draw the on-screen keyboard (keep the same for all inputs)
- drawKeyboard()
- end
- -- Function to handle input via on-screen keyboard
- function handleKeyboardInputForSetup(prompt, isPassword, hint)
- topMonitor.setBackgroundColor(colors.black)
- topMonitor.setTextColor(colors.red)
- local input = "" -- To store the input masked if its password
- -- Initially display the prompt and input area
- updateSetupDisplay(input, prompt, isPassword)
- while true do
- -- Display the password hint below the keyboard if available (during password input)
- if hint and hint ~= "" then
- topMonitor.setCursorPos(1, 11)
- topMonitor.write("Password Hint: " .. hint)
- end
- -- Wait for a touch event on the monitor
- local event, side, x, y = os.pullEvent("monitor_touch")
- -- Handle letter inputs
- if y >= 5 and y <= 7 then
- local row = y - 4
- local letters = {
- "qwertyuiop",
- "asdfghjkl",
- "zxcvbnm"
- }
- local letterIndex = math.ceil(x / 3) -- Correct letter position based on new spacing
- local letter = letters[row]:sub(letterIndex, letterIndex)
- if letter and letter ~= "" then
- input = input .. letter
- updateSetupDisplay(input, prompt, isPassword) -- Refresh after adding input
- end
- -- Handle number inputs
- elseif y == 3 then
- local numbers = "1234567890"
- local numberIndex = math.ceil(x / 3)
- local number = numbers:sub(numberIndex, numberIndex)
- if number and number ~= "" then
- input = input .. number
- updateSetupDisplay(input, prompt, isPassword) -- Refresh after adding input
- end
- -- Handle spacebar input
- elseif y == 8 then
- if x >= 2 and x <= 21 then -- Spacebar spans this range
- input = input .. " " -- Add a space character to the input
- updateSetupDisplay(input, prompt, isPassword) -- Refresh after adding input
- end
- -- Handle special buttons (Backspace and Submit)
- elseif y == 9 then
- if x >= 1 and x <= 11 then -- Backspace button
- input = input:sub(1, -2)
- updateSetupDisplay(input, prompt, isPassword) -- Refresh after backspace
- elseif x >= 13 and x <= 20 then -- Submit button
- return input -- Return the entered input
- end
- end
- end
- end
- -- Update the display for setup input (Username, Password, or Hint)
- function updateSetupDisplay(input, prompt, isPassword)
- -- Clear the input area (start after the prompt text)
- topMonitor.setCursorPos(1, 1)
- topMonitor.clearLine() -- Clear the line before rewriting
- topMonitor.write(prompt) -- Re-write the prompt every time
- -- Set the cursor position for input (right after the prompt)
- topMonitor.setCursorPos(#prompt + 1, 1)
- -- Mask the password if its a password input, otherwise display normal input
- if isPassword then
- --topMonitor.write(string.rep("*", #input)) -- Mask the password with *
- topMonitor.write(input)
- print(input)
- else
- topMonitor.write(input) -- Display the actual input for username or hint
- end
- -- Redraw the keyboard so it stays visible
- drawKeyboard()
- end
- -- Setup new user on a new disk using the monitor and keyboard
- function setupNewPlayerDisk()
- -- Step 1: Clear the monitor first
- topMonitor.clear()
- -- Clear any stored data from previous operations
- username = nil
- password = nil
- hint = nil
- credits = 0
- -- Step 2: Get the Username
- local usernamePrompt = "Enter your Username: "
- local username = handleKeyboardInputForSetup(usernamePrompt, false, nil) -- No hint and not masked
- -- Step 3: Get the Password (masked input)
- local passwordPrompt = "Enter your Password: "
- local password = handleKeyboardInputForSetup(passwordPrompt, true, nil) -- Masked input for password
- -- Step 4: Get the Password Hint (optional)
- local hintPrompt = "Enter a Password Hint (optional): "
- local hint = handleKeyboardInputForSetup(hintPrompt, false, nil) -- No masking for the hint
- -- Step 5: Save the credentials and start with 0 credits
- saveCredentials(username, password, hint)
- saveCreditsToDisk(0)
- return username
- end
- -- Convert a string to its hexadecimal representation safely
- function toHex(str)
- -- Ensure the input is a string and not nil or a number
- if type(str) ~= "string" then
- return "(invalid input)"
- end
- return (str:gsub('.', function(c)
- return string.format('%02X', string.byte(c))
- end))
- end
- --print("Loaded password from disk: ", correctPassword)
- --print("Entered password: ", result)
- -- Validate user disk and password using on-screen keyboard
- function validateUserDisk(username)
- local correctPassword, hint = loadCredentials(username)
- local attempts = 0
- --print("Loaded correct password from disk: [" .. tostring(correctPassword) .. "]")
- while attempts < 3 do
- -- Draw the password keyboard and prompt
- drawKeyboard()
- local enteredPassword = nil
- -- Parallel process to monitor disk removal while the user inputs the password
- local success = parallel.waitForAny(
- function()
- enteredPassword = handleKeyboardInput(hint)
- end,
- resetOnDiskRemove -- Monitor disk removal
- )
- if success == 1 then -- This means the keyboard input was successful
- --print("Entered password: [" .. tostring(enteredPassword) .. "]")
- -- Compare entered and stored password after trimming extra characters
- enteredPassword = enteredPassword:match("^%s*(.-)%s*$") -- Trim any extra spaces or newlines
- correctPassword = correctPassword and correctPassword:match("^%s*(.-)%s*$")
- --print("Comparing passwords: [" .. enteredPassword .. "] vs [" .. correctPassword .. "]")
- if enteredPassword == correctPassword then
- return true -- Access granted
- else
- attempts = attempts + 1
- topMonitor.setCursorPos(1, 18)
- topMonitor.write("Wrong Password, Try Again.")
- sleep(2) -- Brief pause
- if attempts == 3 then
- topMonitor.setCursorPos(1, 19)
- topMonitor.write("Too many failed attempts.")
- sleep(2)
- topMonitor.clear()
- return false
- end
- end
- elseif success == 2 then
- -- Handle disk removal scenario
- return false
- end
- end
- end
- -- Wait for disk to be inserted
- function waitForDisk()
- while not diskDrive.isDiskPresent() do
- topMonitor.clear()
- topMonitor.setCursorPos(1, 1)
- topMonitor.write("Please insert your disk...")
- os.pullEvent("disk")
- end
- -- Disk has been inserted, clear any old data
- username = nil
- password = nil
- hint = nil
- credits = 0
- end
- -- Main Program
- function main()
- setUpMonitors()
- waitForDisk()
- -- Parallel process to monitor disk removal and user actions
- parallel.waitForAny(
- function()
- -- When a disk is inserted, get its label
- local diskLabel = diskDrive.getDiskLabel()
- -- If the disk is not labeled (i.e., its a new disk)
- if not diskLabel or diskLabel == "" then
- -- Clear any lingering data
- username = nil
- password = nil
- hint = nil
- credits = 0
- -- New disk detected, setup a new player
- local username = setupNewPlayerDisk() -- Prompt to enter username, password, and hint
- -- Initialize credits.txt to 0 for new user
- saveCreditsToDisk(0)
- topMonitor.clear()
- topMonitor.setCursorPos(1, 1)
- topMonitor.write("Welcome, " .. username)
- drawMainMenu()
- handleMainMenuTouch(username)
- else
- -- Existing disk detected, validate the user with a password
- local username = diskLabel
- if validateUserDisk(username) then
- -- Use loadCreditsFromDisk for consistency
- displayMain(username) -- Display the users info on the screen
- drawMainMenu() -- Draw the main menu
- handleMainMenuTouch(username) -- Handle user input for the menu
- else
- -- If validation fails, return to startup
- os.reboot()
- end
- end
- end,
- resetOnDiskRemove -- Monitor for disk ejection and handle accordingly
- )
- end
- main()
Add Comment
Please, Sign In to add comment