local player = require("player") local Stage = { NONE = "none", SHOP = "shop", SHOP_GEAR = "shop_gear", SHOP_TOTEM = "shop_totem", SHOP_CONFIRM = "shop_confirm", SHOP_CHECK_FULL = "shop_check_full", SHOP_EXIT = "shop_exit", WAIT = "wait", ORDERS = "orders", ORDERS_SELECT = "orders_select", ORDERS_EXIT = "orders_exit", ORDERS_CONFIRM = "orders_confirm", ORDERS_FINAL_EXIT = "orders_final_exit", CYCLE_PAUSE = "cycle_pause", TARGET_ORDERS = "target_orders" } local stage = Stage.NONE local stageStart = 0 local WAIT_TIME_MS = 50 local shulkerMoveIndex = 0 local lastShulkerMoveTime = 0 local exitCount = 0 local finalExitCount = 0 local finalExitStart = 0 local bulkBuyCount = 0 local targetPlayer = "" local isTargetingActive = false local isEnabled = false local settings = { minPrice = "1500", notifications = true, speedMode = true, enableTargeting = false, targetPlayerName = "", targetOnlyMode = false, blacklistedPlayers = {} } local function log(message, ...) if settings.notifications then local formatted = string.format(message, ...) player.addMessage(formatted) print(formatted) end end local function parsePrice(priceStr) if not priceStr or priceStr == "" then return -1.0 end local cleaned = string.lower(string.gsub(priceStr, ",", "")) local multiplier = 1.0 if string.find(cleaned, "b$") then multiplier = 1000000000.0 cleaned = string.sub(cleaned, 1, -2) elseif string.find(cleaned, "m$") then multiplier = 1000000.0 cleaned = string.sub(cleaned, 1, -2) elseif string.find(cleaned, "k$") then multiplier = 1000.0 cleaned = string.sub(cleaned, 1, -2) end local num = tonumber(cleaned) if num then return num * multiplier end return -1.0 end local function formatPrice(price) if price >= 1000000000 then return string.format("$%.1fB", price / 1000000000.0) elseif price >= 1000000 then return string.format("$%.1fM", price / 1000000.0) elseif price >= 1000 then return string.format("$%.1fK", price / 1000.0) else return string.format("$%.0f", price) end end local function isTotemOfUndying(slotIndex) local stack = player.inventory.getStack(slotIndex) if stack then return stack.id == 1303 end return false end local function isInventoryFull() for i = 9, 35 do local stack = player.inventory.getStack(i) if not stack or stack.id == "air" then return false end end return true end local function parseOrderPrice(stack) if not stack or not stack.lore then return -1.0 end for _, line in ipairs(stack.lore) do local text = line if type(line) == "table" and line.getString then text = line:getString() end text = tostring(text) local priceStr, suffix = string.match(text, "%$([%d,]+%.?%d*)([kmbKMB]?)") if priceStr then priceStr = string.gsub(priceStr, ",", "") local multiplier = 1.0 suffix = string.lower(suffix or "") if suffix == "k" then multiplier = 1000.0 elseif suffix == "m" then multiplier = 1000000.0 elseif suffix == "b" then multiplier = 1000000000.0 end local price = tonumber(priceStr) if price then return price * multiplier end end local payPrice = string.match(string.lower(text), "pay%s*:%s*$([%d,]+)") if payPrice then payPrice = string.gsub(payPrice, ",", "") return tonumber(payPrice) or -1.0 end end return -1.0 end local function getOrderPlayerName(stack) if not stack or not stack.lore then return nil end for _, line in ipairs(stack.lore) do local text = line if type(line) == "table" and line.getString then text = line:getString() end text = tostring(text) local playerName = string.match(text, "Player%s*:%s*([A-Za-z0-9_]+)") if playerName then return playerName end local fromName = string.match(text, "From%s*:%s*([A-Za-z0-9_]+)") if fromName then return fromName end local byName = string.match(text, "By%s*:%s*([A-Za-z0-9_]+)") if byName then return byName end end return nil end local function isBlacklisted(playerName) if not playerName or #settings.blacklistedPlayers == 0 then return false end for _, p in ipairs(settings.blacklistedPlayers) do if string.lower(p) == string.lower(playerName) then return true end end return false end local function updateTargetPlayer() targetPlayer = "" isTargetingActive = false if settings.enableTargeting and settings.targetPlayerName and string.match(settings.targetPlayerName, "%S") then targetPlayer = settings.targetPlayerName isTargetingActive = true log("Targeting enabled for player: %s", targetPlayer) elseif settings.enableTargeting then log("Targeting disabled - no player name provided") end end local function onTick() if not isEnabled or not player.entity then return end local now = os.clock() * 1000 if stage == Stage.NONE then return elseif stage == Stage.TARGET_ORDERS then player.sendCommand("/orders " .. targetPlayer) stage = Stage.ORDERS stageStart = now log("Checking orders for: %s", targetPlayer) elseif stage == Stage.SHOP then player.sendCommand("/shop") stage = Stage.SHOP_GEAR stageStart = now elseif stage == Stage.SHOP_GEAR then if player.inventory.isAnyScreenOpened() and player.inventory.getContainerSlots() > 0 then local stack = player.inventory.getStackFromContainer(13) if stack and stack.id == 1303 then player.inventory.leftClick(13) stage = Stage.SHOP_TOTEM stageStart = now bulkBuyCount = 0 --return end --end local waitTime = settings.speedMode and 1000 or 3000 if now - stageStart > waitTime then player.inventory.closeScreen() stage = Stage.SHOP stageStart = now end end elseif stage == Stage.SHOP_TOTEM then if player.inventory.isAnyScreenOpened() and player.inventory.getContainerSlots() > 0 then local slotCount = player.inventory.getContainerSlots() local foundTotem = false for slot = 0, slotCount - 1 do local stack = player.inventory.getStackFromContainer(slot) if stack and stack.id == 1303 then local clickCount = settings.speedMode and 10 or 5 for i = 1, clickCount do player.inventory.leftClick(slot) end foundTotem = true bulkBuyCount = bulkBuyCount + 1 break end end if foundTotem then stage = Stage.SHOP_CONFIRM stageStart = now return end local waitTime = settings.speedMode and 500 or 1500 if now - stageStart > waitTime then player.inventory.closeScreen() stage = Stage.SHOP stageStart = now end end elseif stage == Stage.SHOP_CONFIRM then if player.inventory.isAnyScreenOpened() and player.inventory.getContainerSlots() > 0 then local foundGreen = false local clickCount = settings.speedMode and 46 or 2 for i = 1, clickCount do player.inventory.leftClick(23) end foundGreen = true if foundGreen then stage = Stage.SHOP_CHECK_FULL stageStart = now return end local waitTime = settings.speedMode and 200 or 800 if now - stageStart > waitTime then stage = Stage.SHOP_TOTEM stageStart = now end end elseif stage == Stage.SHOP_CHECK_FULL then local waitTime = settings.speedMode and 5 or 200 if now - stageStart > waitTime then if isInventoryFull() then player.inventory.closeScreen() stage = Stage.SHOP_EXIT stageStart = now else local checkTime = settings.speedMode and 200 or 400 if now - stageStart > checkTime then stage = Stage.SHOP_TOTEM stageStart = now end end end elseif stage == Stage.SHOP_EXIT then if player.inventory.isAnyScreenOpened() and player.inventory.getContainerSlots() > 0 then stage = Stage.WAIT stageStart = now end local waitTime = settings.speedMode and 1000 or 5000 if now - stageStart > waitTime then player.inventory.closeScreen() stage = Stage.SHOP stageStart = now end elseif stage == Stage.WAIT then local waitTime = settings.speedMode and 5 or WAIT_TIME_MS if now - stageStart >= waitTime then if isTargetingActive and targetPlayer ~= "" then stage = Stage.TARGET_ORDERS else player.sendCommand("/orders totem") stage = Stage.ORDERS end stageStart = now end elseif stage == Stage.ORDERS then if player.inventory.isAnyScreenOpened() and player.inventory.getContainerSlots() > 0 then if settings.speedMode and now - stageStart < 15 then return end local slotCount = player.inventory.getContainerSlots() local foundOrder = false for slot = 0, slotCount - 1 do local stack = player.inventory.getStackFromContainer(slot) if stack and stack.id == 1303 then local orderPrice = parseOrderPrice(stack) local orderPlayer = getOrderPlayerName(stack) if isBlacklisted(orderPlayer) then goto continue_order end local shouldTakeOrder = false local isTargetedOrder = isTargetingActive and targetPlayer ~= "" and orderPlayer and string.lower(orderPlayer) == string.lower(targetPlayer) if isTargetedOrder then shouldTakeOrder = true if settings.notifications then log("Found TARGET order from %s: %s", orderPlayer or "Unknown", formatPrice(orderPrice)) end elseif not settings.targetOnlyMode and orderPrice > 0 then local minPriceValue = parsePrice(settings.minPrice) if orderPrice >= minPriceValue then shouldTakeOrder = true if settings.notifications then log("Found order: %s", formatPrice(orderPrice)) end end end if shouldTakeOrder then player.inventory.leftClick(slot) stage = Stage.ORDERS_SELECT stageStart = now + (settings.speedMode and 100 or 50) shulkerMoveIndex = 0 lastShulkerMoveTime = 0 foundOrder = true if settings.notifications then log("Selected order, preparing to transfer items...") end return end ::continue_order:: end end local waitTime = settings.speedMode and 3000 or 5000 if not foundOrder and now - stageStart > waitTime then player.inventory.closeScreen() stage = Stage.SHOP stageStart = now end end elseif stage == Stage.ORDERS_SELECT then if player.inventory.isAnyScreenOpened() and player.inventory.getContainerSlots() > 0 then if shulkerMoveIndex >= 36 then player.inventory.closeScreen() stage = Stage.ORDERS_CONFIRM stageStart = now shulkerMoveIndex = 0 log("ORDERS_CONFIRM: startIndex=" .. shulkerMoveIndex) return end local moveDelay = settings.speedMode and 2 or 50 if now - lastShulkerMoveTime >= moveDelay then local batchSize = settings.speedMode and 3 or 1 print("ORDERS_SELECT: batchSize=" .. batchSize .. ", startIndex=" .. shulkerMoveIndex) for batch = 1, batchSize do if shulkerMoveIndex >= 36 then break end local stack = player.inventory.getStack(shulkerMoveIndex) if stack then print("Slot " .. shulkerMoveIndex .. ": " .. tostring(stack.name)) end if isTotemOfUndying(shulkerMoveIndex) then log("Found totem in slot " .. shulkerMoveIndex .. ", shift clicking!") player.inventory.shiftLeftClick(shulkerMoveIndex + 27) end shulkerMoveIndex = shulkerMoveIndex + 1 end lastShulkerMoveTime = now end else print("ORDERS_SELECT: No screen opened!") end elseif stage == Stage.ORDERS_EXIT then if player.inventory.isAnyScreenOpened() and player.inventory.getContainerSlots() > 0 then exitCount = exitCount + 1 if exitCount < 2 then player.inventory.closeScreen() stageStart = now else exitCount = 0 stage = Stage.ORDERS_CONFIRM stageStart = now end end elseif stage == Stage.ORDERS_CONFIRM then if player.inventory.isAnyScreenOpened() and player.inventory.getContainerSlots() > 0 then if player.inventory.getStackFromContainer(15) then local clickCount = settings.speedMode and 15 or 5 for i = 1, clickCount do player.inventory.leftClick(15) end stage = Stage.ORDERS_FINAL_EXIT stageStart = now finalExitCount = 0 finalExitStart = now log("ORDERS_CONFIRM: clickCount=" .. clickCount .. ", stageStart=" .. stageStart) end local waitTime = settings.speedMode and 2000 or 5000 if now - stageStart > waitTime then player.inventory.closeScreen() stage = Stage.SHOP stageStart = now end end elseif stage == Stage.ORDERS_FINAL_EXIT then local exitDelay = settings.speedMode and 5 or 200 if finalExitCount == 0 then if now - finalExitStart >= exitDelay then player.inventory.closeScreen() finalExitCount = finalExitCount + 1 finalExitStart = now end elseif finalExitCount == 1 then if now - finalExitStart >= exitDelay then player.inventory.closeScreen() finalExitCount = finalExitCount + 1 finalExitStart = now end else finalExitCount = 0 stage = Stage.CYCLE_PAUSE stageStart = now end elseif stage == Stage.CYCLE_PAUSE then local cycleWait = settings.speedMode and 10 or 25 if now - stageStart >= cycleWait then updateTargetPlayer() stage = Stage.SHOP stageStart = now end end end local function enable() local parsedPrice = parsePrice(settings.minPrice) if parsedPrice == -1.0 and not settings.enableTargeting then log("Invalid minimum price format!") return end updateTargetPlayer() stage = Stage.SHOP stageStart = os.clock() * 1000 shulkerMoveIndex = 0 lastShulkerMoveTime = 0 exitCount = 0 finalExitCount = 0 bulkBuyCount = 0 isEnabled = true local modeInfo = isTargetingActive and string.format(" | Targeting: %s", targetPlayer) or "" log("AutoTotemOrder activated! Minimum: %s%s", settings.minPrice, modeInfo) end local function disable() isEnabled = false stage = Stage.NONE log("AutoTotemOrder disabled") end local function toggle() if isEnabled then disable() else enable() end end registerClientTick(onTick) player.addMessage("[AutoTotemOrder] Script loaded and enabled!") enable()