if getgenv().AutoPoopCleanup then pcall(getgenv().AutoPoopCleanup) getgenv().AutoPoopCleanup = nil end if getgenv().AutoPoopLibrary then pcall(function() getgenv().AutoPoopLibrary:Unload() end) getgenv().AutoPoopLibrary = nil end local libraryUrl = "https://raw.githubusercontent.com/croksyscript/CrypticLoader/refs/heads/main/Obsidian/Obsidian_UI/Obsidian_UI.lua" local success, Library = pcall(function() return loadstring(game:HttpGet(libraryUrl))() end) if not success or not Library then return end getgenv().AutoPoopLibrary = Library local ReplicatedStorage = game:GetService("ReplicatedStorage") local Players = game:GetService("Players") local GuiService = game:GetService("GuiService") local RunService = game:GetService("RunService") local VirtualInputManager = game:GetService("VirtualInputManager") local LocalPlayer = Players.LocalPlayer local PlayerScripts = LocalPlayer:WaitForChild("PlayerScripts") local PlayerGui = LocalPlayer:WaitForChild("PlayerGui") local Packets = require(ReplicatedStorage:WaitForChild("Packets")) local minigameActive = PlayerScripts:WaitForChild("IS_MINIGAME_ACTIVE") local okFood, FoodData = pcall(require, ReplicatedStorage:WaitForChild("FoodData")) local okMoney, FormatMoney = pcall(require, ReplicatedStorage:WaitForChild("FormatMoney")) local PoopUI = PlayerGui:WaitForChild("PoopUI") local lineTrigger = PoopUI:WaitForChild("CatchBar"):WaitForChild("LineTrigger") local slider = PoopUI.CatchBar:WaitForChild("BarLine"):WaitForChild("Slider") local target = slider:WaitForChild("Target") local BACKPACK_LIMIT = 300 local state = { running = true, notifications = {}, remotePoop = false, legitPoop = false, instantLine = false, remoteDelay = 1, pause = 2, cycles = 0, autoSell = false, sellMode = "Poop Count", sellValue = 100, sellAction = "Sell All", sellOneMode = "First", lastSell = os.clock(), autoBuy = false, buyInterval = 10, selectedFoods = {}, stock = {}, } local connections = {} local function jitter(minValue, maxValue) return minValue + math.random() * (maxValue - minValue) end local function formatMoney(value) if okMoney and typeof(value) == "number" then local ok, text = pcall(FormatMoney, value, true) if ok then return text end end return tostring(value) end local function isPoop(tool) return tool:IsA("Tool") and tool.Name:sub(1, 4) == "Poop" end local function getPoops() local list = {} for _, tool in ipairs(LocalPlayer.Backpack:GetChildren()) do if isPoop(tool) then table.insert(list, tool) end end local character = LocalPlayer.Character local held = character and character:FindFirstChildOfClass("Tool") if held and isPoop(held) then table.insert(list, held) end return list end local function getBackpackSize() return #LocalPlayer.Backpack:GetChildren() end local function getWeight(tool) return tonumber(tool.Name:match("([%d%.]+)%s*lb")) or 0 end local CLICK_LATENCY = 0.05 local lastCenter = 0 local lastTime = os.clock() local function isLineInTarget() local now = os.clock() local center = lineTrigger.AbsolutePosition.X + lineTrigger.AbsoluteSize.X / 2 local elapsed = math.max(now - lastTime, 1 / 240) local velocity = (center - lastCenter) / elapsed lastCenter = center lastTime = now local predicted = center + velocity * CLICK_LATENCY local margin = lineTrigger.AbsoluteSize.X * 0.065 local left = target.AbsolutePosition.X return left - margin < predicted and predicted < left + target.AbsoluteSize.X + margin end local function sendMouse(position, down) VirtualInputManager:SendMouseButtonEvent(position.X, position.Y, 0, down, game, 0) end local CLICK_SPOTS = {} for _, x in ipairs({ 0.04, 0.96, 0.08, 0.92 }) do for _, y in ipairs({ 0.35, 0.5, 0.25, 0.65, 0.75 }) do table.insert(CLICK_SPOTS, Vector2.new(x, y)) end end local function isSpotFree(point) local inset = GuiService:GetGuiInset() for _, gui in ipairs(PlayerGui:GetGuiObjectsAtPosition(point.X - inset.X, point.Y - inset.Y)) do if not gui:IsDescendantOf(PoopUI) and (gui:IsA("GuiButton") or gui.Active) then return false end end return true end local function getFreePoint() local size = workspace.CurrentCamera.ViewportSize for _, spot in ipairs(CLICK_SPOTS) do local point = size * spot if isSpotFree(point) then return point end end return nil end local function clickTrigger() local position = getFreePoint() if not position then return end sendMouse(position, true) task.wait(0.03) sendMouse(position, false) end local function getChargeFill() local chargeUi = workspace:FindFirstChild("ChargeUI") local bar = chargeUi and chargeUi:FindFirstChild("BarProgress", true) return bar and bar:FindFirstChild("Fill") end local function chargeBar() local position = getFreePoint() if not position then task.wait(1) return end sendMouse(position, true) local startTime = os.clock() local fill repeat task.wait() fill = getChargeFill() until fill or os.clock() - startTime > 0.5 while os.clock() - startTime < 2 do if fill and fill.Parent and fill.Size.Y.Scale >= 0.97 then break end if not fill and os.clock() - startTime >= 0.7 then break end task.wait() end sendMouse(position, false) end local targetAspect = target:FindFirstChildOfClass("UIAspectRatioConstraint") local defaultAspectRatio = targetAspect and targetAspect.AspectRatio local WIDE_ASPECT_RATIO = 4 local function moveTargetToLine() local lineCenter = lineTrigger.AbsolutePosition.X + lineTrigger.AbsoluteSize.X / 2 local targetCenter = target.AbsolutePosition.X + target.AbsoluteSize.X / 2 local position = slider.Position slider.Position = UDim2.new(position.X.Scale, position.X.Offset + lineCenter - targetCenter, position.Y.Scale, position.Y.Offset) end local function widenTarget() if targetAspect then targetAspect.AspectRatio = WIDE_ASPECT_RATIO end end local function restoreTarget() if targetAspect then targetAspect.AspectRatio = defaultAspectRatio end end local function runLegitCycle() if not minigameActive.Value then chargeBar() task.wait(0.3) end local deadline = os.clock() + 30 while state.running and minigameActive.Value and os.clock() < deadline do if state.instantLine then widenTarget() moveTargetToLine() RunService.Heartbeat:Wait() moveTargetToLine() clickTrigger() task.wait(jitter(0.2, 0.3)) elseif isLineInTarget() then clickTrigger() task.wait(jitter(0.4, 0.55)) end task.wait() end restoreTarget() state.cycles += 1 task.wait(state.pause * jitter(0.85, 1.25)) end local function runRemoteCycle() Packets.RequestPoop:Fire() state.cycles += 1 local delay = state.remoteDelay * jitter(0.8, 1.35) if math.random() < 0.05 then delay += jitter(2, 4) end task.wait(delay) end local function pickPoop(mode) local poops = getPoops() if #poops == 0 then return nil end if mode == "Held" then local character = LocalPlayer.Character local held = character and character:FindFirstChildOfClass("Tool") return held and isPoop(held) and held or nil end if mode == "Smallest" or mode == "Biggest" then table.sort(poops, function(a, b) if mode == "Smallest" then return getWeight(a) < getWeight(b) end return getWeight(a) > getWeight(b) end) end return poops[1] end local function sellOne(mode) local tool = pickPoop(mode) local uuid = tool and tool:GetAttribute("uniquePoopID") if not uuid then return false end local ok, result = pcall(function() return Packets.SellCurrentlyHeldPoop:Fire(uuid) end) if ok then Library:Notify("Sold poop for " .. formatMoney(result), 3) end return ok end local function sellAll() local ok, result = pcall(function() return Packets.SellInventory:Fire() end) if ok then state.lastSell = os.clock() Library:Notify("Sold inventory for " .. formatMoney(result), 3) end return ok end local function runSell() if state.sellAction == "Sell One" then return sellOne(state.sellOneMode == "Held" and "First" or state.sellOneMode) end return sellAll() end local function refreshStock() pcall(function() Packets.FoodStock:Fire() end) end local function buyFood(name) pcall(function() Packets.PurchaseFood:Fire(name) end) end local function getFoodNames() local names = {} if okFood and typeof(FoodData) == "table" then for name in pairs(FoodData) do table.insert(names, name) end table.sort(names, function(a, b) return FoodData[a].Price < FoodData[b].Price end) end return names end local function getStockText() local names = {} for name in pairs(state.stock) do table.insert(names, name) end table.sort(names) return #names > 0 and table.concat(names, ", ") or "Nothing" end table.insert(connections, Packets.FoodStock.OnClientEvent:Connect(function(list) state.stock = {} if typeof(list) == "table" then for _, name in ipairs(list) do state.stock[name] = true end end end)) table.insert(connections, Packets.PurchaseFood.OnClientEvent:Connect(function(purchased, name) if purchased then state.stock[name] = nil table.insert(state.notifications, "Bought " .. tostring(name)) end end)) task.spawn(function() while state.running do local hasSpace = getBackpackSize() < BACKPACK_LIMIT local cycle = nil if hasSpace and state.remotePoop then cycle = runRemoteCycle elseif state.legitPoop and (hasSpace or minigameActive.Value) then cycle = runLegitCycle end if cycle then local ok = pcall(cycle) if not ok then task.wait(1) end else task.wait(0.3) end end end) task.spawn(function() while state.running do if state.autoSell then if state.sellMode == "Seconds" then if os.clock() - state.lastSell >= state.sellValue then state.lastSell = os.clock() runSell() end elseif #getPoops() >= state.sellValue then runSell() task.wait(2) end end task.wait(0.5) end end) task.spawn(function() while state.running do local text = table.remove(state.notifications, 1) if text then Library:Notify(text, 3) end task.wait(0.3) end end) local lastBuyCheck = 0 task.spawn(function() while state.running do local due = os.clock() - lastBuyCheck >= state.buyInterval if state.autoBuy and due and next(state.selectedFoods) then lastBuyCheck = os.clock() refreshStock() task.wait(1) for name in pairs(state.selectedFoods) do if state.stock[name] then buyFood(name) task.wait(jitter(0.5, 0.9)) end end end task.wait(0.5) end end) local Window = Library:CreateWindow({ Title = "Open Source - Poop a Big Poop", Footer = ".gg/XDpsSW7Ybs", Icon = 120217226033572, NotifySide = "Right", Center = true, AutoShow = true, ToggleKeybind = Enum.KeyCode.RightControl, Folder = "Poop", }) local function buildPoopTab(Tab) local Box = Tab:AddGroupbox({ Side = "Left", Name = "Auto Poop", IconName = "zap", }) Box:AddToggle("RemotePoopToggle", { Text = "Auto Poop (Remote)", Default = false, Tooltip = "Fast, sends the poop request directly", Callback = function(value) state.remotePoop = value if value and Library.Toggles.LegitPoopToggle.Value then Library.Toggles.LegitPoopToggle:SetValue(false) end end, }) Box:AddSlider("RemoteDelay", { Text = "Remote Delay", Default = 1, Min = 0.3, Max = 5, Rounding = 1, Suffix = "s", Tooltip = "Lower values are faster but riskier", Callback = function(value) state.remoteDelay = value end, }) Box:AddDivider("Legit") Box:AddToggle("LegitPoopToggle", { Text = "Auto Poop (Legit)", Default = false, Tooltip = "Charges the bar and clicks the minigame like a player", Callback = function(value) state.legitPoop = value if value and Library.Toggles.RemotePoopToggle.Value then Library.Toggles.RemotePoopToggle:SetValue(false) end end, }) Box:AddToggle("InstantLineToggle", { Text = "Instant Line", Default = false, Tooltip = "Moves the target under the line before every click", Callback = function(value) state.instantLine = value end, }) Box:AddSlider("PoopPause", { Text = "Legit Pause", Default = 2, Min = 0.1, Max = 10, Rounding = 1, Suffix = "s", Callback = function(value) state.pause = value end, }) local Info = Tab:AddGroupbox({ Side = "Right", Name = "Info", IconName = "info", }) local statusLabel = Info:AddLabel("Poops: 0") local cycleLabel = Info:AddLabel("Poops Made: 0") local sizeLabel = Info:AddLabel("Inventory: 0 / " .. BACKPACK_LIMIT) task.spawn(function() while state.running do statusLabel:SetText("Poops: " .. #getPoops()) cycleLabel:SetText("Poops Made: " .. state.cycles) sizeLabel:SetText(("Inventory: %d / %d"):format(getBackpackSize(), BACKPACK_LIMIT)) task.wait(0.5) end end) end local function buildSellTab(Tab) local Manual = Tab:AddGroupbox({ Side = "Left", Name = "Manual Sell", IconName = "hand-coins", }) Manual:AddButton({ Text = "Sell All Poops", Func = sellAll, }) Manual:AddDropdown("SellOneMode", { Text = "Sell One Mode", Values = { "First", "Smallest", "Biggest", "Held" }, Default = 1, Callback = function(value) state.sellOneMode = value end, }) Manual:AddButton({ Text = "Sell One Poop", Func = function() if not sellOne(state.sellOneMode) then Library:Notify("No matching poop found", 3) end end, }) local Auto = Tab:AddGroupbox({ Side = "Right", Name = "Auto Sell", IconName = "repeat", }) Auto:AddToggle("AutoSellToggle", { Text = "Auto Sell", Default = false, Callback = function(value) state.autoSell = value state.lastSell = os.clock() end, }) Auto:AddDropdown("AutoSellMode", { Text = "Sell Trigger", Values = { "Poop Count", "Seconds" }, Default = 1, Callback = function(value) state.sellMode = value end, }) Auto:AddInput("AutoSellValue", { Text = "Poops or Seconds", Default = "100", Numeric = true, Finished = true, Placeholder = "100", Callback = function(value) state.sellValue = math.max(tonumber(value) or 1, 1) end, }) Auto:AddDropdown("AutoSellAction", { Text = "Sell Action", Values = { "Sell All", "Sell One" }, Default = 1, Callback = function(value) state.sellAction = value end, }) Auto:AddLabel({ Text = "Stand near the sell NPC", DoesWrap = true, }) end local function buildFoodTab(Tab) local Buy = Tab:AddGroupbox({ Side = "Left", Name = "Buy Food", IconName = "shopping-cart", }) Buy:AddDropdown("FoodSelect", { Text = "Foods", Values = getFoodNames(), Default = {}, Multi = true, AllowNull = true, SelectAllButtons = true, Callback = function(values) state.selectedFoods = {} for name, enabled in pairs(values) do if enabled then state.selectedFoods[name] = true end end end, }) Buy:AddButton({ Text = "Buy Selected Once", Func = function() refreshStock() task.wait(1) for name in pairs(state.selectedFoods) do if state.stock[name] then buyFood(name) task.wait(jitter(0.5, 0.9)) else Library:Notify(name .. " is out of stock", 3) end end end, }) Buy:AddButton({ Text = "Buy Everything In Stock", Func = function() refreshStock() task.wait(1) for name in pairs(state.stock) do buyFood(name) task.wait(jitter(0.5, 0.9)) end end, }) local Auto = Tab:AddGroupbox({ Side = "Right", Name = "Auto Buy", IconName = "repeat", }) Auto:AddToggle("AutoBuyToggle", { Text = "Auto Buy Selected", Default = false, Callback = function(value) state.autoBuy = value end, }) Auto:AddSlider("BuyInterval", { Text = "Check Stock Every", Default = 10, Min = 5, Max = 60, Rounding = 0, Suffix = "s", Callback = function(value) state.buyInterval = value end, }) local stockLabel = Auto:AddLabel({ Text = "In Stock: Nothing", DoesWrap = true, }) task.spawn(function() while state.running do stockLabel:SetText("In Stock: " .. getStockText()) task.wait(1) end end) refreshStock() end local Tabs = { Poop = Window:AddTab("Poop", "zap"), Sell = Window:AddTab("Sell", "hand-coins"), Food = Window:AddTab("Food", "shopping-cart"), } buildPoopTab(Tabs.Poop) buildSellTab(Tabs.Sell) buildFoodTab(Tabs.Food) local function cleanup() state.running = false state.remotePoop = false state.legitPoop = false state.autoSell = false state.autoBuy = false restoreTarget() for _, connection in ipairs(connections) do connection:Disconnect() end connections = {} end getgenv().AutoPoopCleanup = cleanup Library:OnUnload(cleanup)