Auto farm | Dig And Clean | XENO SUPPORTED
Key Required

Auto farm | Dig And Clean | XENO SUPPORTED

[🎃] Dig & Clean 🧼ToastmanToastman 1.5K views 13.5K executesUpdated Sep 3, 2026
5.0/ 5(1 votes)
10100% Positive
Lua Script•4474 lines (80830 chars)
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local VirtualInputManager = game:GetService("VirtualInputManager")
local UserInputService = game:GetService("UserInputService")
local HttpService = game:GetService("HttpService")

local Player = Players.LocalPlayer
local PlayerGui = Player:WaitForChild("PlayerGui")
local Backpack = Player:WaitForChild("Backpack")
local RNG = Random.new()

local Env = getgenv and getgenv() or _G

local InstanceId =
    tostring(os.clock())
    .. "_"
    .. tostring(math.random(100000,999999))

Env.DigOPInstanceId = InstanceId

local function alive()
    return Env.DigOPInstanceId == InstanceId
end

local ExecutorName = "Unknown"

pcall(function()
    if identifyexecutor then
        ExecutorName = tostring(identifyexecutor())
    elseif getexecutorname then
        ExecutorName = tostring(getexecutorname())
    end
end)

local Caps = {
    Mouse1Click = typeof(mouse1click) == "function",
    FirePrompt = typeof(fireproximityprompt) == "function",
    ReadFile = typeof(readfile) == "function",
    WriteFile = typeof(writefile) == "function",
    IsFile = typeof(isfile) == "function",
    MakeFolder = typeof(makefolder) == "function",
    IsFolder = typeof(isfolder) == "function"
}

local SAVE_FOLDER = "DigOP"
local SAVE_FILE = SAVE_FOLDER .. "/settings.json"

local function ensureFolder()
    if not Caps.MakeFolder then
        return
    end

    pcall(function()
        if Caps.IsFolder then
            if not isfolder(SAVE_FOLDER) then
                makefolder(SAVE_FOLDER)
            end
        else
            makefolder(SAVE_FOLDER)
        end
    end)
end

ensureFolder()

local function loadSettings()
    if not Caps.ReadFile or not Caps.IsFile then
        return {}
    end

    local success,data = pcall(function()
        if not isfile(SAVE_FILE) then
            return nil
        end

        return HttpService:JSONDecode(
            readfile(SAVE_FILE)
        )
    end)

    if success and type(data) == "table" then
        return data
    end

    return {}
end

local Saved = loadSettings()

local function saveSettings(data)
    if not Caps.WriteFile then
        return
    end

    ensureFolder()

    pcall(function()
        writefile(
            SAVE_FILE,
            HttpService:JSONEncode(data)
        )
    end)
end

local function findRecursive(rootObject,name,timeout)
    local started = os.clock()
    local found = rootObject:FindFirstChild(name,true)

    while not found
        and alive()
        and os.clock() - started < (timeout or 15) do

        task.wait(0.05)

        found =
            rootObject:FindFirstChild(
                name,
                true
            )
    end

    return found
end

local function requireRecursive(name)
    local found =
        findRecursive(
            ReplicatedStorage,
            name,
            15
        )

    if not found then
        error("Missing: "..name)
    end

    return found
end

local ShopFunctions =
    requireRecursive(
        "shared/network/ShopNetwork@GlobalShopFunctions"
    )

local ItemsFunctions =
    requireRecursive(
        "shared/network/ItemsNetwork@GlobalItemsFunctions"
    )

local ItemsEvents =
    requireRecursive(
        "shared/network/ItemsNetwork@GlobalItemsEvents"
    )

local DataFunctions =
    requireRecursive(
        "shared/network/DataNetwork@GlobalDataFunctions"
    )

local MiscFunctions =
    requireRecursive(
        "shared/network/MiscNetwork@GlobalMiscFunctions"
    )

local PedestalFunctions =
    requireRecursive(
        "shared/network/PedestalNetwork@GlobalPedestalFunctions"
    )

local SellFunctions =
    requireRecursive(
        "shared/network/SellNetwork@GlobalSellFunctions"
    )

local ShovelEvents =
    requireRecursive(
        "shared/network/ShovelNetwork@GlobalShovelEvents"
    )

local DetectorEvents =
    requireRecursive(
        "shared/network/DetectorNetwork@GlobalDetectorEvents"
    )

local BuyGearRemote =
    ShopFunctions:WaitForChild("buyGear")

local EquipGearRemote =
    ShopFunctions:WaitForChild("equipGear")

local BeginCleaningRemote =
    ItemsFunctions:WaitForChild("beginCleaning")

local SaveCleanProgressRemote =
    ItemsEvents:WaitForChild("saveCleanProgress")

local FinishCleaningRemote =
    ItemsEvents:WaitForChild("finishCleaning")

local RequestDataRemote =
    DataFunctions:WaitForChild("requestDataUpdate")

local RequestPlotRemote =
    MiscFunctions:WaitForChild("RequestPlotNumber")

local PlaceItemRemote =
    PedestalFunctions:WaitForChild("placeItem")

local PickupItemRemote =
    PedestalFunctions:WaitForChild("pickupItem")

local SellHeldRemote =
    SellFunctions:WaitForChild("sellHeldItem")

local DigSceneStartRemote =
    ShovelEvents:WaitForChild("DigSceneStart")

local DigSceneEndRemote =
    ShovelEvents:WaitForChild("DigSceneEnd")

local BuriedNodesRemote =
    DetectorEvents:WaitForChild("BuriedNodes")

local SetDetectorHeldRemote =
    DetectorEvents:WaitForChild("SetDetectorHeld")

local RequestId = -1000000

local function invoke(remote,timeout,...)
    if not alive() then
        return false,nil
    end

    RequestId -= 1

    local id = RequestId
    local finished = false
    local status
    local result
    local connection

    connection =
        remote.OnClientEvent:Connect(function(
            responseId,
            responseStatus,
            responseResult
        )
            if not alive() then
                return
            end

            if responseId ~= id then
                return
            end

            status = responseStatus
            result = responseResult
            finished = true
        end)

    local args = table.pack(...)

    local fired = pcall(function()
        remote:FireServer(
            id,
            table.unpack(args,1,args.n)
        )
    end)

    if not fired then
        connection:Disconnect()
        return false,nil
    end

    local started = os.clock()

    while not finished
        and alive()
        and os.clock() - started < (timeout or 5) do

        task.wait()
    end

    connection:Disconnect()

    if not alive() or not finished then
        return false,nil
    end

    if status ~= true then
        return false,result
    end

    return true,result
end

local Shovels = {
    {"plasticShovel","Plastic Shovel",0,1,"starterIsland"},
    {"woodShovel","Wood Shovel",250,2,"starterIsland"},
    {"stoneShovel","Stone Shovel",1600,3,"starterIsland"},
    {"metalShovel","Metal Shovel",9000,5,"starterIsland"},
    {"goldShovel","Gold Shovel",45000,7,"starterIsland"},
    {"amethystShovel","Amethyst Shovel",190000,9,"starterIsland"},

    {"titaniumShovel","Titanium Shovel",390000,14,"island2"},
    {"cobaltShovel","Cobalt Shovel",650000,17,"island2"},
    {"carbonShovel","Carbon Shovel",1300000,21,"island2"},
    {"rubyShovel","Ruby Shovel",2900000,26,"island2"},
    {"diamondShovel","Diamond Shovel",9200000,40,"island2"},

    {"sandstoneShovel","Sandstone Shovel",20000000,50,"island3"},
    {"amberShovel","Amber Shovel",25000000,62,"island3"},
    {"scarabShovel","Scarab Shovel",40000000,78,"island3"},
    {"malachiteShovel","Malachite Shovel",65000000,100,"island3"},
    {"aquamarineShovel","Aquamarine Shovel",120000000,160,"island3"},

    {"cryoliteShovel","Cryolite Shovel",225000000,200,"island4"},
    {"peridotShovel","Peridot Shovel",450000000,250,"island4"},
    {"sunstoneShovel","Sunstone Shovel",1000000000,320,"island4"},
    {"sugiliteShovel","Sugilite Shovel",2250000000,420,"island4"},
    {"eclipseShovel","Eclipse Shovel",5500000000,640,"island4"},

    {"netherShovel","Nether Shovel",4000000000,900,"island5"},
    {"specterShovel","Specter Shovel",10000000000,1300,"island5"},
    {"infernoShovel","Inferno Shovel",20000000000,1900,"island5"},
    {"stormShovel","Storm Shovel",45000000000,2800,"island5"},
    {"magmaShovel","Magma Shovel",100000000000,4200,"island5"},

    {"seraphShovel","Seraph Shovel",200000000000,6000,"island6"},
    {"dominionShovel","Dominion Shovel",450000000000,9000,"island6"},
    {"virtueShovel","Virtue Shovel",1000000000000,13500,"island6"},
    {"throneShovel","Throne Shovel",1800000000000,20000,"island6"},
    {"archangelShovel","Archangel Shovel",3300000000000,30000,"island6"}
}

local Detectors = {
    {"rusty","Rusty Detector",0,1,"starterIsland"},
    {"copper","Copper Detector",400,2,"starterIsland"},
    {"silver","Silver Detector",4000,3,"starterIsland"},
    {"gold","Gold Detector",25000,4,"starterIsland"},
    {"platinum","Platinum Detector",150000,5,"starterIsland"},

    {"onyx","Onyx Detector",400000,6,"island2"},
    {"sapphire","Sapphire Detector",700000,7,"island2"},
    {"jade","Jade Detector",1500000,8,"island2"},
    {"topaz","Topaz Detector",3000000,9,"island2"},
    {"crystal","Crystal Detector",8000000,10,"island2"},

    {"sandstone","Sandstone Detector",22500000,11,"island3"},
    {"amber","Amber Detector",35000000,12,"island3"},
    {"scarab","Scarab Detector",50000000,13,"island3"},
    {"malachite","Malachite Detector",80000000,14,"island3"},
    {"aquamarine","Aquamarine Detector",125000000,15,"island3"},

    {"cryolite","Cryolite Detector",250000000,16,"island4"},
    {"peridot","Peridot Detector",450000000,17,"island4"},
    {"sunstone","Sunstone Detector",1000000000,18,"island4"},
    {"sugilite","Sugilite Detector",2250000000,19,"island4"},
    {"eclipse","Eclipse Detector",5500000000,20,"island4"},

    {"nether","Nether Detector",4000000000,21,"island5"},
    {"specter","Specter Detector",9000000000,22,"island5"},
    {"inferno","Inferno Detector",20000000000,23,"island5"},
    {"storm","Storm Detector",45000000000,24,"island5"},
    {"magma","Magma Detector",100000000000,25,"island5"},

    {"seraph","Seraph Detector",200000000000,26,"island6"},
    {"dominion","Dominion Detector",450000000000,27,"island6"},
    {"virtue","Virtue Detector",1000000000000,28,"island6"},
    {"throne","Throne Detector",1800000000000,29,"island6"},
    {"archangel","Archangel Detector",3300000000000,30,"island6"}
}

local Settings = {
    AutoFarm = Saved.AutoFarm == true,

    ActiveIsland =
        type(Saved.ActiveIsland) == "string"
        and Saved.ActiveIsland
        or nil,

    AutoDig =
        Saved.AutoDig ~= false,

    AutoFastClick =
        Saved.AutoFastClick ~= false,

    AutoClean =
        Saved.AutoClean ~= false,

    AutoPlace =
        Saved.AutoPlace ~= false,

    AutoSell =
        Saved.AutoSell ~= false,

    AutoBuyShovel =
        Saved.AutoBuyShovel ~= false,

    AutoBuyDetector =
        Saved.AutoBuyDetector ~= false
}

local ActiveIsland = nil
local IslandToggles = {}
local IslandStates = {}
local IslandProfiles = {}

local ChangingToggle = false
local ManualFastClick = false
local TestDigActive = false

local Busy = false
local Searching = false
local Digging = false

local CandidateLock = 0
local NextSearch = 0
local NextGearCheck = 0

local CachedData
local DataTime = 0

local PlotNumber
local CachedPlot
local CachedSeller

local CachedPedestals
local PedestalCacheTime = 0

local BuriedNodes = {}
local FailedNodes = {}
local KnownValues = {}

local FULL_CLEAN_MASK =
    string.rep("/",64)

local Rayfield

local function persist()
    saveSettings({
        AutoFarm = Settings.AutoFarm,

        ActiveIsland =
            ActiveIsland
            and ActiveIsland.Name
            or Settings.ActiveIsland,

        AutoDig = Settings.AutoDig,

        AutoFastClick =
            Settings.AutoFastClick,

        AutoClean =
            Settings.AutoClean,

        AutoPlace =
            Settings.AutoPlace,

        AutoSell =
            Settings.AutoSell,

        AutoBuyShovel =
            Settings.AutoBuyShovel,

        AutoBuyDetector =
            Settings.AutoBuyDetector
    })
end

local function character()
    return Player.Character
end

local function root()
    local char = character()

    return char
        and char:FindFirstChild(
            "HumanoidRootPart"
        )
end

local function humanoid()
    local char = character()

    return char
        and char:FindFirstChildOfClass(
            "Humanoid"
        )
end

local function notify(title,content)
    if Rayfield and alive() then
        pcall(function()
            Rayfield:Notify({
                Title = title,
                Content = content,
                Duration = 3
            })
        end)
    end
end

local function savePosition()
    local hrp = root()

    return
        hrp
        and hrp.CFrame
        or nil
end

local function restorePosition(cf)
    local hrp = root()

    if not hrp or not cf then
        return
    end

    hrp.CFrame = cf
    hrp.AssemblyLinearVelocity = Vector3.zero
    hrp.AssemblyAngularVelocity = Vector3.zero
end

local function getPart(object)
    if not object then
        return nil
    end

    if object:IsA("BasePart") then
        return object
    end

    if object:IsA("Model") then
        return
            object.PrimaryPart
            or object:FindFirstChild(
                "HumanoidRootPart",
                true
            )
            or object:FindFirstChildWhichIsA(
                "BasePart",
                true
            )
    end

    return object:FindFirstChildWhichIsA(
        "BasePart",
        true
    )
end

local function teleportTo(object)
    local hrp = root()
    local part = getPart(object)

    if not hrp or not part then
        return false
    end

    hrp.CFrame =
        part.CFrame
        * CFrame.new(
            0,
            part.Size.Y / 2 + 3,
            0
        )

    hrp.AssemblyLinearVelocity = Vector3.zero
    hrp.AssemblyAngularVelocity = Vector3.zero

    return true
end

local function teleportNear(object,distance)
    local hrp = root()
    local part = getPart(object)

    if not hrp or not part then
        return false
    end

    hrp.CFrame =
        part.CFrame
        * CFrame.new(
            0,
            0,
            -(distance or 3)
        )

    hrp.AssemblyLinearVelocity = Vector3.zero
    hrp.AssemblyAngularVelocity = Vector3.zero

    return true
end

local function getData(force)
    if not force
        and CachedData
        and os.clock() - DataTime < 0.2 then

        return CachedData
    end

    local success,data =
        invoke(
            RequestDataRemote,
            4
        )

    if success
        and type(data) == "table" then

        CachedData = data
        DataTime = os.clock()
    end

    return CachedData
end

local function refreshData()
    DataTime = 0

    return getData(true)
end

local function findInventory(uid,data)
    data = data or getData()

    if not data
        or type(data.Inventory) ~= "table" then

        return nil
    end

    for _,item in pairs(
        data.Inventory
    ) do
        if type(item) == "table"
            and item.uid == uid then

            return item
        end
    end

    return nil
end

local function isCollectible(item)
    return
        type(item) == "table"
        and type(item.uid) == "string"
        and (
            item.dirty ~= nil
            or item.condition ~= nil
            or item.pedestalSlot ~= nil
        )
end

local function inventoryEntries()
    local result = {}
    local data = refreshData()

    if not data
        or type(data.Inventory) ~= "table" then

        return result
    end

    for _,item in pairs(
        data.Inventory
    ) do
        if isCollectible(item) then
            table.insert(
                result,
                item
            )
        end
    end

    return result
end

local function getDirtyItem()
    local data = getData()

    if not data
        or type(data.Inventory) ~= "table" then

        return nil
    end

    for _,item in pairs(
        data.Inventory
    ) do
        if isCollectible(item)
            and item.dirty == true
            and item.pedestalSlot == nil then

            return item
        end
    end

    return nil
end

local function getToolByUid(uid)
    local containers = {
        character(),
        Backpack
    }

    for _,container in ipairs(
        containers
    ) do
        if container then
            for _,object in ipairs(
                container:GetChildren()
            ) do
                if object:IsA("Tool")
                    and object:GetAttribute(
                        "inventoryId"
                    ) == uid then

                    return object
                end
            end
        end
    end

    return nil
end

local function waitForTool(uid,timeout)
    local started = os.clock()

    repeat
        local tool =
            getToolByUid(uid)

        if tool then
            return tool
        end

        task.wait(0.035)

    until not alive()
        or os.clock() - started
        >= (timeout or 2.5)

    return nil
end

local function unequipTools()
    local hum = humanoid()

    if hum then
        pcall(function()
            hum:UnequipTools()
        end)
    end

    task.wait(0.025)
end

local function equipUid(uid)
    local tool =
        waitForTool(
            uid,
            2.5
        )

    if not tool then
        return false
    end

    local hum = humanoid()

    if not hum then
        return false
    end

    pcall(function()
        hum:UnequipTools()
    end)

    task.wait(0.025)

    pcall(function()
        hum:EquipTool(tool)
    end)

    local started = os.clock()

    repeat
        local char = character()

        local held =
            char
            and char:FindFirstChildOfClass(
                "Tool"
            )

        if held
            and held:GetAttribute(
                "inventoryId"
            ) == uid then

            return true
        end

        task.wait(0.015)

    until not alive()
        or os.clock() - started > 0.8

    return false
end

local function equipShovelToolIfPresent()
    local hum = humanoid()

    if not hum then
        return
    end

    for _,tool in ipairs(
        Backpack:GetChildren()
    ) do
        if tool:IsA("Tool") then
            local name =
                string.lower(
                    tool.Name
                )

            if string.find(
                name,
                "shovel",
                1,
                true
            ) then

                pcall(function()
                    hum:EquipTool(tool)
                end)

                return
            end
        end
    end
end

local function setDetectorHeld(value)
    pcall(function()
        SetDetectorHeldRemote:FireServer(
            value
        )
    end)
end

local function resetDigEquipment()
    setDetectorHeld(false)

    unequipTools()

    local data =
        refreshData()

    if data
        and data.EquippedShovel then

        pcall(function()
            invoke(
                EquipGearRemote,
                2,
                "shovel",
                data.EquippedShovel
            )
        end)
    end

    task.wait(0.025)

    unequipTools()

    equipShovelToolIfPresent()

    task.wait(0.025)
end

local function getPlot()
    if CachedPlot
        and CachedPlot.Parent then

        return CachedPlot
    end

    local plots =
        workspace:FindFirstChild(
            "Plots"
        )

    if not plots then
        return nil
    end

    for _,plot in ipairs(
        plots:GetChildren()
    ) do
        if plot:GetAttribute(
            "OwnerUserId"
        ) == Player.UserId then

            CachedPlot = plot

            return plot
        end
    end

    if not PlotNumber then
        local success,number =
            invoke(
                RequestPlotRemote,
                4
            )

        if success
            and type(number) == "number" then

            PlotNumber = number
        end
    end

    if PlotNumber then
        CachedPlot =
            plots:FindFirstChild(
                "Plot_"
                .. tostring(PlotNumber)
            )
    end

    return CachedPlot
end

local function getWorkbench()
    local plot = getPlot()

    if not plot then
        return nil
    end

    local bench =
        plot:FindFirstChild(
            "Workbench",
            true
        )

    if not bench then
        return nil
    end

    return
        bench:FindFirstChild(
            "TableTop",
            true
        )
        or bench
end

local function getPedestals()
    if CachedPedestals
        and os.clock() - PedestalCacheTime < 1 then

        local valid = true

        for _,pedestal in ipairs(
            CachedPedestals
        ) do
            if not pedestal.Parent then
                valid = false
                break
            end
        end

        if valid then
            return CachedPedestals
        end
    end

    local result = {}
    local plot = getPlot()

    if not plot then
        return result
    end

    for _,object in ipairs(
        plot:GetDescendants()
    ) do
        if object:IsA("Model")
            and type(
                object:GetAttribute(
                    "Slot"
                )
            ) == "number"
            and object:GetAttribute(
                "Owned"
            ) == true then

            table.insert(
                result,
                object
            )
        end
    end

    table.sort(
        result,
        function(a,b)
            return
                a:GetAttribute("Slot")
                <
                b:GetAttribute("Slot")
        end
    )

    CachedPedestals = result
    PedestalCacheTime = os.clock()

    return result
end

local function findPedestalBySlot(slot)
    for _,pedestal in ipairs(
        getPedestals()
    ) do
        if pedestal:GetAttribute(
            "Slot"
        ) == slot then

            return pedestal
        end
    end

    return nil
end

local function pedestalEmpty(pedestal)
    local uid =
        pedestal:GetAttribute(
            "ItemUid"
        )

    return
        type(uid) ~= "string"
        or uid == ""
end

local function getOpenPedestal()
    for _,pedestal in ipairs(
        getPedestals()
    ) do
        if pedestalEmpty(
            pedestal
        ) then

            return pedestal
        end
    end

    return nil
end

local function parseMoney(text)
    if type(text) ~= "string" then
        return nil
    end

    local clean =
        text:gsub(",","")
        :gsub("¢","")
        :gsub("%$","")
        :gsub("%s+","")

    local number,suffix =
        clean:match(
            "([%d%.]+)([%a]*)"
        )

    number = tonumber(number)

    if not number then
        return nil
    end

    suffix =
        string.lower(
            suffix or ""
        )

    local mult = {
        [""] = 1,
        k = 1e3,
        m = 1e6,
        b = 1e9,
        t = 1e12,
        qa = 1e15,
        qi = 1e18,
        sx = 1e21,
        sp = 1e24,
        oc = 1e27,
        no = 1e30,
        dc = 1e33
    }

    return
        number
        * (
            mult[suffix]
            or 1
        )
end

local function valueFromEntry(item)
    if not item then
        return 0
    end

    if KnownValues[item.uid] then
        return KnownValues[item.uid]
    end

    for _,key in ipairs({
        "value",
        "Value",
        "sellValue",
        "SellValue",
        "worth",
        "Worth",
        "price",
        "Price"
    }) do
        if type(item[key]) == "number" then
            return item[key]
        end
    end

    local tool =
        getToolByUid(
            item.uid
        )

    if tool then
        for _,key in ipairs({
            "value",
            "Value",
            "sellValue",
            "SellValue",
            "worth",
            "Worth",
            "price",
            "Price"
        }) do
            local value =
                tool:GetAttribute(
                    key
                )

            if type(value) == "number" then
                return value
            end
        end
    end

    return 0
end

local function getPedestalValue(pedestal)
    for _,object in ipairs(
        pedestal:GetDescendants()
    ) do
        if object:IsA("TextLabel") then
            local lower =
                string.lower(
                    object.Name
                )

            if lower == "money"
                or string.find(
                    lower,
                    "value",
                    1,
                    true
                ) then

                local value =
                    parseMoney(
                        object.Text
                    )

                if value then
                    return value
                end
            end
        end
    end

    local uid =
        pedestal:GetAttribute(
            "ItemUid"
        )

    if type(uid) == "string"
        and uid ~= "" then

        return valueFromEntry(
            findInventory(
                uid,
                getData()
            )
        )
    end

    return 0
end

local function pedestalFavorited(pedestal)
    local uid =
        pedestal:GetAttribute(
            "ItemUid"
        )

    if type(uid) ~= "string"
        or uid == "" then

        return false
    end

    local item =
        findInventory(
            uid,
            getData()
        )

    return
        item
        and item.favorited == true
        or false
end

local function weakestPedestal()
    local weakest
    local weakestValue = math.huge

    for _,pedestal in ipairs(
        getPedestals()
    ) do
        if not pedestalEmpty(pedestal)
            and not pedestalFavorited(
                pedestal
            ) then

            local value =
                getPedestalValue(
                    pedestal
                )

            if value
                and value < weakestValue then

                weakest = pedestal
                weakestValue = value
            end
        end
    end

    return weakest,weakestValue
end

local function findPrompt(object,words)
    if not object then
        return nil
    end

    local fallback

    for _,descendant in ipairs(
        object:GetDescendants()
    ) do
        if descendant:IsA(
            "ProximityPrompt"
        ) then

            fallback =
                fallback or descendant

            local text =
                string.lower(
                    tostring(
                        descendant.ActionText
                    )
                    .. " "
                    .. tostring(
                        descendant.ObjectText
                    )
                    .. " "
                    .. descendant.Name
                )

            for _,word in ipairs(
                words or {}
            ) do
                if string.find(
                    text,
                    string.lower(word),
                    1,
                    true
                ) then

                    return descendant
                end
            end
        end
    end

    return fallback
end

local function triggerPrompt(prompt)
    if not prompt then
        return false
    end

    if Caps.FirePrompt then
        local success =
            pcall(function()
                fireproximityprompt(
                    prompt
                )
            end)

        if success then
            task.wait(0.1)

            return true
        end
    end

    local duration =
        math.max(
            prompt.HoldDuration,
            0.05
        )

    local success =
        pcall(function()
            prompt:InputHoldBegin()

            task.wait(
                duration + 0.08
            )

            prompt:InputHoldEnd()
        end)

    if success then
        task.wait(0.1)

        return true
    end

    local key =
        prompt.KeyboardKeyCode

    if key == Enum.KeyCode.Unknown then
        key = Enum.KeyCode.E
    end

    success =
        pcall(function()
            VirtualInputManager:SendKeyEvent(
                true,
                key,
                false,
                game
            )

            task.wait(
                duration + 0.08
            )

            VirtualInputManager:SendKeyEvent(
                false,
                key,
                false,
                game
            )
        end)

    task.wait(0.1)

    return success
end

local function verifyPickedUp(
    pedestal,
    uid,
    timeout
)
    local started = os.clock()

    repeat
        local data =
            refreshData()

        local item =
            findInventory(
                uid,
                data
            )

        local pedestalUid =
            pedestal:GetAttribute(
                "ItemUid"
            )

        if (
            pedestalUid == nil
            or pedestalUid == ""
        )
            and item
            and item.pedestalSlot == nil then

            return true
        end

        task.wait(0.035)

    until not alive()
        or os.clock() - started
        > (timeout or 2)

    return false
end

local function pickupPedestal(pedestal)
    if not pedestal then
        return nil
    end

    local slot =
        pedestal:GetAttribute(
            "Slot"
        )

    local uid =
        pedestal:GetAttribute(
            "ItemUid"
        )

    if type(slot) ~= "number"
        or type(uid) ~= "string"
        or uid == "" then

        return nil
    end

    unequipTools()

    teleportNear(
        pedestal,
        2
    )

    task.wait(0.08)

    local prompt =
        findPrompt(
            pedestal,
            {
                "pick up",
                "pickup",
                "pick"
            }
        )

    if prompt then
        triggerPrompt(prompt)

        if verifyPickedUp(
            pedestal,
            uid,
            1.5
        ) then

            CachedPedestals = nil

            return uid
        end
    end

    local success =
        invoke(
            PickupItemRemote,
            3,
            slot
        )

    if success
        and verifyPickedUp(
            pedestal,
            uid,
            1.5
        ) then

        CachedPedestals = nil

        return uid
    end

    return nil
end

local function getSeller()
    if CachedSeller
        and CachedSeller.Parent then

        return CachedSeller
    end

    local islands =
        workspace:FindFirstChild(
            "Islands"
        )

    if not islands then
        return nil
    end

    local home =
        islands:FindFirstChild(
            "Home Beach"
        )

    if home then
        CachedSeller =
            home:FindFirstChild(
                "SellerNPC",
                true
            )

        if CachedSeller then
            return CachedSeller
        end
    end

    CachedSeller =
        islands:FindFirstChild(
            "SellerNPC",
            true
        )

    return CachedSeller
end

local function itemStillExists(uid)
    return
        findInventory(
            uid,
            refreshData()
        ) ~= nil
end

local function sellUid(uid)
    if not uid then
        return false,"missing uid"
    end

    local item =
        findInventory(
            uid,
            refreshData()
        )

    if not item then
        return true,"gone"
    end

    if item.favorited == true then
        return false,"favorited"
    end

    if item.pedestalSlot ~= nil then
        return false,"displayed"
    end

    unequipTools()

    if not equipUid(uid) then
        return false,"equip failed"
    end

    local seller =
        getSeller()

    if not seller then
        return false,"seller missing"
    end

    teleportNear(
        seller,
        3
    )

    task.wait(0.09)

    for attempt = 1,3 do
        if not equipUid(uid) then
            task.wait(0.1)
            continue
        end

        local success,result =
            invoke(
                SellHeldRemote,
                3
            )

        task.wait(0.1)

        if not itemStillExists(uid) then
            resetDigEquipment()

            return true,"sold"
        end

        if result == "favorited" then
            resetDigEquipment()

            return false,"favorited"
        end

        if attempt < 3 then
            teleportNear(
                seller,
                3
            )

            task.wait(0.1)
        end
    end

    resetDigEquipment()

    return false,"sell failed"
end

local function verifyPlaced(
    pedestal,
    uid,
    timeout
)
    local started = os.clock()

    repeat
        if pedestal:GetAttribute(
            "ItemUid"
        ) == uid then

            refreshData()

            CachedPedestals = nil

            return true
        end

        task.wait(0.03)

    until not alive()
        or os.clock() - started
        > (timeout or 1.3)

    return false
end

local function placeUid(pedestal,uid)
    if not pedestal or not uid then
        return false
    end

    local slot =
        pedestal:GetAttribute(
            "Slot"
        )

    if type(slot) ~= "number" then
        return false
    end

    unequipTools()

    if not equipUid(uid) then
        return false
    end

    teleportNear(
        pedestal,
        2
    )

    task.wait(0.07)

    local success,result =
        invoke(
            PlaceItemRemote,
            3,
            slot,
            uid
        )

    if success
        and result == true
        and verifyPlaced(
            pedestal,
            uid,
            1.2
        ) then

        resetDigEquipment()

        return true
    end

    local prompt =
        findPrompt(
            pedestal,
            {"place"}
        )

    if prompt then
        equipUid(uid)

        triggerPrompt(prompt)

        if verifyPlaced(
            pedestal,
            uid,
            1.2
        ) then

            resetDigEquipment()

            return true
        end
    end

    resetDigEquipment()

    return false
end

local function manageItem(uid,force)
    local allowPlace =
        force
        or Settings.AutoPlace

    local allowSell =
        force
        or Settings.AutoSell

    local returnPosition =
        savePosition()

    local item =
        findInventory(
            uid,
            refreshData()
        )

    if not item
        or item.dirty == true then

        restorePosition(
            returnPosition
        )

        return false
    end

    local newValue =
        valueFromEntry(item)

    local open =
        getOpenPedestal()

    if open then
        if allowPlace then
            local success =
                placeUid(
                    open,
                    uid
                )

            restorePosition(
                returnPosition
            )

            resetDigEquipment()

            if success then
                notify(
                    "Item Logic",
                    "Placed in open slot"
                )
            end

            return success

        elseif allowSell then
            local success,reason =
                sellUid(uid)

            restorePosition(
                returnPosition
            )

            resetDigEquipment()

            if not success then
                notify(
                    "Item Logic",
                    "Sell failed: "
                    .. tostring(reason)
                )
            end

            return success

        else
            restorePosition(
                returnPosition
            )

            resetDigEquipment()

            return true
        end
    end

    local weakest,weakestValue =
        weakestPedestal()

    if not weakest
        or newValue <= 0
        or newValue <= weakestValue then

        if allowSell then
            local success,reason =
                sellUid(uid)

            restorePosition(
                returnPosition
            )

            resetDigEquipment()

            if not success then
                notify(
                    "Item Logic",
                    "Sell failed: "
                    .. tostring(reason)
                )
            end

            return success
        end

        restorePosition(
            returnPosition
        )

        resetDigEquipment()

        return true
    end

    if not allowPlace then
        if allowSell then
            local success,reason =
                sellUid(uid)

            restorePosition(
                returnPosition
            )

            resetDigEquipment()

            if not success then
                notify(
                    "Item Logic",
                    "Sell failed: "
                    .. tostring(reason)
                )
            end

            return success
        end

        restorePosition(
            returnPosition
        )

        resetDigEquipment()

        return true
    end

    local oldUid =
        weakest:GetAttribute(
            "ItemUid"
        )

    local oldSlot =
        weakest:GetAttribute(
            "Slot"
        )

    local picked =
        pickupPedestal(
            weakest
        )

    if picked ~= oldUid then
        restorePosition(
            returnPosition
        )

        resetDigEquipment()

        notify(
            "Item Logic",
            "Pickup failed"
        )

        return false
    end

    task.wait(0.08)

    if allowSell then
        local soldOld,reason =
            sellUid(oldUid)

        if not soldOld then
            local original =
                findPedestalBySlot(
                    oldSlot
                )

            if original then
                placeUid(
                    original,
                    oldUid
                )
            end

            restorePosition(
                returnPosition
            )

            resetDigEquipment()

            notify(
                "Item Logic",
                "Old sale failed: "
                .. tostring(reason)
            )

            return false
        end
    end

    task.wait(0.06)

    local target =
        findPedestalBySlot(
            oldSlot
        )

    local placed =
        target
        and placeUid(
            target,
            uid
        )

    restorePosition(
        returnPosition
    )

    resetDigEquipment()

    if placed then
        if allowSell then
            notify(
                "Item Logic",
                "Replaced weaker item"
            )
        else
            notify(
                "Item Logic",
                "Replaced item; old item kept in inventory"
            )
        end
    end

    return placed == true
end

local function instantClean(
    item,
    manageAfter,
    forceManage
)
    if not item or not item.uid then
        return false
    end

    local uid = item.uid

    local returnPosition =
        savePosition()

    local bench =
        getWorkbench()

    if bench then
        teleportTo(bench)

        task.wait(0.08)
    end

    unequipTools()

    if not equipUid(uid) then
        restorePosition(
            returnPosition
        )

        resetDigEquipment()

        return false
    end

    local success,result =
        invoke(
            BeginCleaningRemote,
            4,
            uid
        )

    if not success then
        restorePosition(
            returnPosition
        )

        resetDigEquipment()

        return false
    end

    if type(result) == "number" then
        KnownValues[uid] = result

    elseif type(result) == "table" then
        for _,key in ipairs({
            "value",
            "Value",
            "sellValue",
            "SellValue",
            "worth",
            "Worth"
        }) do
            if type(result[key]) == "number" then
                KnownValues[uid] =
                    result[key]

                break
            end
        end
    end

    pcall(function()
        SaveCleanProgressRemote:FireServer(
            uid,
            FULL_CLEAN_MASK
        )
    end)

    task.wait(0.03)

    pcall(function()
        FinishCleaningRemote:FireServer(
            uid
        )
    end)

    local cleaned = false
    local started = os.clock()

    repeat
        local updated =
            findInventory(
                uid,
                refreshData()
            )

        if updated
            and updated.dirty == false then

            cleaned = true
            break
        end

        task.wait(0.04)

    until not alive()
        or os.clock() - started > 1.3

    restorePosition(
        returnPosition
    )

    resetDigEquipment()

    if cleaned
        and manageAfter then

        manageItem(
            uid,
            forceManage
        )
    end

    NextSearch =
        os.clock() + 0.1

    return cleaned
end

local function findDefinition(list,id)
    for _,gear in ipairs(list) do
        if gear[1] == id then
            return gear
        end
    end

    return nil
end

local function islandUnlocked(
    data,
    islandId
)
    if islandId == "starterIsland" then
        return true
    end

    return
        data
        and type(
            data.UnlockedIslands
        ) == "table"
        and table.find(
            data.UnlockedIslands,
            islandId
        ) ~= nil
end

local function gearOwned(data,key,id)
    return
        data
        and type(data[key]) == "table"
        and table.find(
            data[key],
            id
        ) ~= nil
end

local function bestOwnedGear(
    list,
    data,
    key
)
    if not data
        or type(data[key]) ~= "table" then

        return nil
    end

    local best
    local score = -math.huge

    for _,id in ipairs(
        data[key]
    ) do
        local gear =
            findDefinition(
                list,
                id
            )

        if gear
            and gear[4] > score then

            best = gear
            score = gear[4]
        end
    end

    return best
end

local function bestAffordableGear(
    list,
    data,
    ownedKey,
    equippedKey
)
    if not data then
        return nil
    end

    local money =
        tonumber(data.Gold)
        or 0

    local current =
        findDefinition(
            list,
            data[equippedKey]
        )

    local bestScore =
        current
        and current[4]
        or 0

    local best

    for _,gear in ipairs(list) do
        if gear[3] > 0
            and gear[3] <= money
            and gear[4] > bestScore
            and islandUnlocked(
                data,
                gear[5]
            )
            and not gearOwned(
                data,
                ownedKey,
                gear[1]
            ) then

            best = gear
            bestScore = gear[4]
        end
    end

    return best
end

local ShopCache = {}

local function findShopModel(
    gear,
    folderName
)
    local key =
        folderName
        .. ":"
        .. gear[1]

    local cached =
        ShopCache[key]

    if cached
        and cached.Parent then

        return cached
    end

    for _,object in ipairs(
        workspace:GetDescendants()
    ) do
        if object:IsA("Model")
            and object.Name == gear[2] then

            local ancestor =
                object.Parent

            while ancestor
                and ancestor ~= workspace do

                if ancestor.Name == folderName then
                    ShopCache[key] = object

                    return object
                end

                ancestor =
                    ancestor.Parent
            end
        end
    end

    return nil
end

local function autoBuyGear(
    category,
    list,
    ownedKey,
    equippedKey,
    shopFolder
)
    local data =
        refreshData()

    if not data then
        return false
    end

    local current =
        findDefinition(
            list,
            data[equippedKey]
        )

    local ownedBest =
        bestOwnedGear(
            list,
            data,
            ownedKey
        )

    if ownedBest
        and (
            not current
            or ownedBest[4] > current[4]
        ) then

        local success =
            invoke(
                EquipGearRemote,
                3,
                category,
                ownedBest[1]
            )

        refreshData()

        resetDigEquipment()

        return success == true
    end

    local gear =
        bestAffordableGear(
            list,
            data,
            ownedKey,
            equippedKey
        )

    if not gear then
        return false
    end

    local returnPosition =
        savePosition()

    local shop =
        findShopModel(
            gear,
            shopFolder
        )

    if shop then
        teleportTo(shop)

        task.wait(0.07)
    end

    local success,bought =
        invoke(
            BuyGearRemote,
            4,
            category,
            gear[1]
        )

    if success
        and bought == true then

        task.wait(0.04)

        invoke(
            EquipGearRemote,
            3,
            category,
            gear[1]
        )

        refreshData()
    end

    restorePosition(
        returnPosition
    )

    resetDigEquipment()

    return
        success
        and bought == true
end

local function autoBuyShovel()
    return autoBuyGear(
        "shovel",
        Shovels,
        "OwnedShovels",
        "EquippedShovel",
        "BuyShovels"
    )
end

local function autoBuyDetector()
    return autoBuyGear(
        "detector",
        Detectors,
        "OwnedDetectors",
        "EquippedDetector",
        "BuyDetectors"
    )
end

local function isDigUI()
    local gui =
        PlayerGui:FindFirstChild(
            "DigGui"
        )

    if not gui then
        return false
    end

    local bar =
        gui:FindFirstChild(
            "DigBar",
            true
        )

    return
        bar
        and bar:IsA("GuiObject")
        and bar.Visible
end

local function isDigging()
    if Digging then
        return true
    end

    if isDigUI() then
        return true
    end

    local hrp = root()
    local hum = humanoid()

    if hrp
        and hrp:FindFirstChild(
            "PowerBar"
        ) then

        return true
    end

    if hrp
        and hum
        and hrp.Anchored
        and hum.AutoRotate == false then

        return true
    end

    return false
end

DigSceneStartRemote.OnClientEvent:Connect(
    function(userId)
        if not alive() then
            return
        end

        if type(userId) == "number"
            and userId ~= Player.UserId then

            return
        end

        Digging = true

        CandidateLock =
            os.clock() + 1.5
    end
)

DigSceneEndRemote.OnClientEvent:Connect(
    function(userId)
        if not alive() then
            return
        end

        if type(userId) == "number"
            and userId ~= Player.UserId then

            return
        end

        task.delay(
            0.06,
            function()
                if not alive() then
                    return
                end

                Digging = false
                CandidateLock = 0

                NextSearch =
                    os.clock() + 0.15

                TestDigActive = false

                resetDigEquipment()
            end
        )
    end
)

BuriedNodesRemote.OnClientEvent:Connect(
    function(added,removed)
        if not alive() then
            return
        end

        if type(added) == "table" then
            for _,node in ipairs(added) do
                if type(node) == "table"
                    and type(node.id) == "string"
                    and typeof(node.position)
                    == "Vector3" then

                    BuriedNodes[node.id] =
                        node
                end
            end
        end

        if type(removed) == "table" then
            for _,id in ipairs(removed) do
                BuriedNodes[id] = nil
                FailedNodes[id] = nil
            end
        end
    end
)

local function physicalClick()
    if Caps.Mouse1Click then
        local success =
            pcall(function()
                mouse1click()
            end)

        if success then
            return
        end
    end

    local camera =
        workspace.CurrentCamera

    if not camera then
        return
    end

    local size =
        camera.ViewportSize

    pcall(function()
        VirtualInputManager:SendMouseButtonEvent(
            size.X / 2,
            size.Y / 2,
            0,
            true,
            game,
            0
        )

        VirtualInputManager:SendMouseButtonEvent(
            size.X / 2,
            size.Y / 2,
            0,
            false,
            game,
            0
        )
    end)
end

local function digClick()
    local char = character()

    if char then
        local tool =
            char:FindFirstChildOfClass(
                "Tool"
            )

        if tool then
            pcall(function()
                tool:Activate()
            end)
        end
    end

    physicalClick()
end

local IslandsFolder =
    workspace:WaitForChild(
        "Islands"
    )

local function isDigAreaPart(object)
    return
        object:IsA("BasePart")
        and object.Name == "DigArea"
end

local function invalidateIsland(island)
    IslandProfiles[island] = nil
    IslandStates[island] = nil
end

local function buildIslandProfile(island)
    local parts = {}

    if not island
        or not island.Parent then

        return {
            Island = island,
            Parts = parts,
            Mode = "None"
        }
    end

    for _,object in ipairs(
        island:GetDescendants()
    ) do
        if isDigAreaPart(object) then
            local ancestor =
                object.Parent

            local valid =
                object.Parent
                and object.Parent.Name
                == "DigAreas"

            while not valid
                and ancestor
                and ancestor ~= island do

                if ancestor.Name == "DigAreas" then
                    valid = true
                    break
                end

                ancestor =
                    ancestor.Parent
            end

            if valid then
                table.insert(
                    parts,
                    object
                )
            end
        end
    end

    table.sort(
        parts,
        function(a,b)
            return
                a:GetFullName()
                <
                b:GetFullName()
        end
    )

    return {
        Island = island,

        Parts = parts,

        Mode =
            #parts >= 4
            and "Hotspots"
            or "Zone"
    }
end

local function getIslandProfile(island)
    local cached =
        IslandProfiles[island]

    if cached then
        local valid = true

        for _,part in ipairs(
            cached.Parts
        ) do
            if not part.Parent then
                valid = false
                break
            end
        end

        if valid then
            return cached
        end
    end

    local profile =
        buildIslandProfile(
            island
        )

    IslandProfiles[island] =
        profile

    return profile
end

local function getIslandState(island)
    if not IslandStates[island] then
        IslandStates[island] = {
            Cursor = 0,
            FailedParts = {}
        }
    end

    return IslandStates[island]
end

local function insidePart(
    part,
    position,
    padding
)
    local point =
        part.CFrame:PointToObjectSpace(
            position
        )

    padding = padding or 0

    return
        math.abs(point.X)
        <= part.Size.X / 2 + padding
        and
        math.abs(point.Z)
        <= part.Size.Z / 2 + padding
end

local function getRaycastIgnore(
    island,
    profile
)
    local ignore = {}

    local char = character()

    if char then
        table.insert(
            ignore,
            char
        )
    end

    profile =
        profile
        or getIslandProfile(
            island
        )

    for _,part in ipairs(
        profile.Parts
    ) do
        table.insert(
            ignore,
            part
        )
    end

    return ignore
end

local function groundAt(
    island,
    position,
    profile
)
    local params =
        RaycastParams.new()

    params.FilterType =
        Enum.RaycastFilterType.Exclude

    params.IgnoreWater = false

    params.FilterDescendantsInstances =
        getRaycastIgnore(
            island,
            profile
        )

    return workspace:Raycast(
        position
        + Vector3.new(
            0,
            65,
            0
        ),

        Vector3.new(
            0,
            -200,
            0
        ),

        params
    )
end

local function teleportGround(
    island,
    position,
    profile
)
    if isDigging() then
        return false
    end

    local hrp = root()
    local hum = humanoid()

    if not hrp or not hum then
        return false
    end

    local result =
        groundAt(
            island,
            position,
            profile
        )

    if not result then
        return false
    end

    local offset =
        hum.HipHeight
        + hrp.Size.Y / 2
        + 0.02

    hrp.CFrame =
        CFrame.new(
            result.Position
            + Vector3.new(
                0,
                offset,
                0
            )
        )
        * hrp.CFrame.Rotation

    hrp.AssemblyLinearVelocity =
        Vector3.zero

    hrp.AssemblyAngularVelocity =
        Vector3.zero

    return true
end

local function randomPointInPart(
    part,
    inset
)
    inset = inset or 0.15

    local halfX =
        math.max(
            0,
            part.Size.X
            * 0.5
            * (1 - inset)
        )

    local halfZ =
        math.max(
            0,
            part.Size.Z
            * 0.5
            * (1 - inset)
        )

    local x =
        halfX > 0.5
        and RNG:NextNumber(
            -halfX,
            halfX
        )
        or 0

    local z =
        halfZ > 0.5
        and RNG:NextNumber(
            -halfZ,
            halfZ
        )
        or 0

    return
        part.CFrame
        * Vector3.new(
            x,
            0,
            z
        )
end

local function getNearestNodeForProfile(
    profile
)
    local hrp = root()

    if not hrp then
        return nil
    end

    local best
    local bestDistance = math.huge
    local now = os.clock()

    for id,node in pairs(
        BuriedNodes
    ) do
        if type(node) == "table"
            and typeof(node.position)
            == "Vector3" then

            local valid = false

            for _,part in ipairs(
                profile.Parts
            ) do
                if insidePart(
                    part,
                    node.position,
                    4
                ) then

                    valid = true
                    break
                end
            end

            if valid then
                local failed =
                    FailedNodes[id]

                if not failed
                    or now >= failed then

                    local distance =
                        (
                            hrp.Position
                            - node.position
                        ).Magnitude

                    if distance
                        < bestDistance then

                        bestDistance =
                            distance

                        best = node
                    end
                end
            end
        end
    end

    return best
end

local function nextHotspot(profile)
    local parts = profile.Parts

    if #parts == 0 then
        return nil
    end

    local state =
        getIslandState(
            profile.Island
        )

    local now = os.clock()

    for _ = 1,#parts do
        state.Cursor += 1

        if state.Cursor > #parts then
            state.Cursor = 1
        end

        local part =
            parts[state.Cursor]

        local failed =
            state.FailedParts[part]

        if part.Parent
            and (
                not failed
                or now >= failed
            ) then

            return part
        end
    end

    return
        parts[
            RNG:NextInteger(
                1,
                #parts
            )
        ]
end

local function nearestZone(profile)
    local hrp = root()

    if not hrp then
        return nil
    end

    for _,part in ipairs(
        profile.Parts
    ) do
        if insidePart(
            part,
            hrp.Position,
            1
        ) then

            return part
        end
    end

    local nearest
    local nearestDistance = math.huge

    for _,part in ipairs(
        profile.Parts
    ) do
        local distance =
            (
                hrp.Position
                - part.Position
            ).Magnitude

        if distance < nearestDistance then
            nearestDistance = distance
            nearest = part
        end
    end

    return nearest
end

local function tryDig(timeout)
    CandidateLock =
        os.clock() + 1.05

    setDetectorHeld(false)

    resetDigEquipment()

    local started = os.clock()

    repeat
        if isDigging() then
            CandidateLock =
                os.clock() + 1.5

            return true
        end

        digClick()

        task.wait(0.012)

    until not alive()
        or os.clock() - started
        > (timeout or 0.65)

    local grace = os.clock()

    repeat
        if isDigging() then
            CandidateLock =
                os.clock() + 1.5

            return true
        end

        task.wait(0.006)

    until not alive()
        or os.clock() - grace > 0.18

    CandidateLock = 0

    return false
end

local function tryNode(profile,node)
    if not teleportGround(
        profile.Island,
        node.position,
        profile
    ) then

        FailedNodes[node.id] =
            os.clock() + 0.7

        return false
    end

    setDetectorHeld(true)

    task.wait(0.035)

    setDetectorHeld(false)

    if tryDig(0.65) then
        return true
    end

    FailedNodes[node.id] =
        os.clock() + 0.75

    return false
end

local function tryHotspot(profile,part)
    if not part
        or not part.Parent then

        return false
    end

    local point =
        randomPointInPart(
            part,
            0.3
        )

    if not teleportGround(
        profile.Island,
        point,
        profile
    ) then

        getIslandState(
            profile.Island
        ).FailedParts[part] =
            os.clock() + 0.35

        return false
    end

    setDetectorHeld(true)

    task.wait(0.03)

    setDetectorHeld(false)

    if tryDig(0.6) then
        return true
    end

    getIslandState(
        profile.Island
    ).FailedParts[part] =
        os.clock() + 0.4

    return false
end

local function tryZone(profile,part)
    if not part
        or not part.Parent then

        return false
    end

    local point =
        randomPointInPart(
            part,
            0.1
        )

    if not teleportGround(
        profile.Island,
        point,
        profile
    ) then

        return false
    end

    setDetectorHeld(true)

    task.wait(0.025)

    setDetectorHeld(false)

    return tryDig(0.38)
end

local function searchDig(
    oneTest,
    island
)
    if not alive()
        or Searching
        or Busy
        or isDigging() then

        return false
    end

    island =
        island or ActiveIsland

    if not island
        or not island.Parent then

        return false
    end

    local profile =
        getIslandProfile(
            island
        )

    if #profile.Parts == 0 then
        notify(
            island.Name,
            "No DigArea parts found"
        )

        return false
    end

    Searching = true

    local found = false

    while alive()
        and (
            oneTest
            or (
                Settings.AutoFarm
                and Settings.AutoDig
                and ActiveIsland == island
            )
        ) do

        if Busy or isDigging() then
            found = isDigging()
            break
        end

        local node =
            getNearestNodeForProfile(
                profile
            )

        if node then
            if tryNode(
                profile,
                node
            ) then

                found = true
                break
            end

        elseif profile.Mode == "Hotspots" then
            local hotspot =
                nextHotspot(
                    profile
                )

            if hotspot
                and tryHotspot(
                    profile,
                    hotspot
                ) then

                found = true
                break
            end

        else
            local zone =
                nearestZone(
                    profile
                )

            if zone
                and tryZone(
                    profile,
                    zone
                ) then

                found = true
                break
            end
        end

        task.wait(0.005)
    end

    Searching = false

    return found
end

local function sellAll()
    if Busy then
        return
    end

    Busy = true

    local returnPosition =
        savePosition()

    local snapshot =
        inventoryEntries()

    local sold = 0
    local protected = 0
    local failed = 0

    for _,original in ipairs(snapshot) do
        if not alive() then
            break
        end

        local item =
            findInventory(
                original.uid,
                refreshData()
            )

        if not item then
            continue
        end

        if item.pedestalSlot ~= nil then
            continue
        end

        if item.favorited == true then
            protected += 1
            continue
        end

        local success,reason =
            sellUid(
                item.uid
            )

        if success then
            sold += 1

        elseif reason == "favorited" then
            protected += 1

        else
            failed += 1
        end

        task.wait(0.04)
    end

    restorePosition(
        returnPosition
    )

    resetDigEquipment()

    Busy = false

    notify(
        "Sell All",
        "Sold "
        .. tostring(sold)
        .. " | Protected "
        .. tostring(protected)
        .. " | Failed "
        .. tostring(failed)
    )
end

local function testClean()
    if Busy then
        return
    end

    local choices = {}

    for _,item in ipairs(
        inventoryEntries()
    ) do
        if item.pedestalSlot == nil
            and item.dirty == true then

            table.insert(
                choices,
                item
            )
        end
    end

    if #choices == 0 then
        notify(
            "Clean Test",
            "No dirty item"
        )

        return
    end

    local item =
        choices[
            RNG:NextInteger(
                1,
                #choices
            )
        ]

    Busy = true

    local success =
        instantClean(
            item,
            false,
            true
        )

    Busy = false

    notify(
        "Clean Test",
        success
            and "Worked"
            or "Failed"
    )
end

local function testSell()
    if Busy then
        return
    end

    local choices = {}

    for _,item in ipairs(
        inventoryEntries()
    ) do
        if item.pedestalSlot == nil
            and item.favorited ~= true then

            table.insert(
                choices,
                item
            )
        end
    end

    if #choices == 0 then
        notify(
            "Sell Test",
            "No sellable inventory item"
        )

        return
    end

    local item =
        choices[
            RNG:NextInteger(
                1,
                #choices
            )
        ]

    Busy = true

    local success,reason =
        sellUid(
            item.uid
        )

    Busy = false

    notify(
        "Sell Test",
        success
            and "Worked"
            or tostring(reason)
    )
end

local function testManage()
    if Busy then
        return
    end

    local choices = {}

    for _,item in ipairs(
        inventoryEntries()
    ) do
        if item.pedestalSlot == nil
            and item.dirty == false
            and item.favorited ~= true then

            table.insert(
                choices,
                item
            )
        end
    end

    if #choices == 0 then
        notify(
            "Item Test",
            "No clean unplaced item"
        )

        return
    end

    local item =
        choices[
            RNG:NextInteger(
                1,
                #choices
            )
        ]

    Busy = true

    local success =
        manageItem(
            item.uid,
            true
        )

    Busy = false

    notify(
        "Item Test",
        success
            and "Worked"
            or "Failed"
    )
end

Rayfield =
    loadstring(
        game:HttpGet(
            "https://sirius.menu/rayfield"
        )
    )()

local Window =
    Rayfield:CreateWindow({
        Name = "Dig OP",
        Icon = 0,

        LoadingTitle = "Dig OP",
        LoadingSubtitle = ExecutorName,

        ShowText = "Dig OP",

        Theme = "Default",

        ToggleUIKeybind = "K",

        DisableRayfieldPrompts = false,
        DisableBuildWarnings = false,

        ConfigurationSaving = {
            Enabled = false
        }
    })

local Main =
    Window:CreateTab(
        "Main",
        4483362458
    )

Main:CreateParagraph({
    Title = "Executor",

    Content =
        ExecutorName
        .. "\nNative Click: "
        .. tostring(Caps.Mouse1Click)
        .. "\nNative Prompt: "
        .. tostring(Caps.FirePrompt)
        .. "\nFile Save: "
        .. tostring(Caps.WriteFile)
})

Main:CreateParagraph({
    Title = "AUTO FARM FEATURES",

    Content =
        "Turn individual systems on/off below. These only affect AUTO FARM."
})

Main:CreateToggle({
    Name = "Auto Dig",

    CurrentValue =
        Settings.AutoDig,

    Flag = "AutoDig",

    Callback = function(value)
        Settings.AutoDig = value

        persist()

        if value
            and Settings.AutoFarm
            and ActiveIsland
            and not Busy
            and not Searching
            and not isDigging() then

            task.spawn(
                searchDig,
                false,
                ActiveIsland
            )
        end
    end
})

Main:CreateToggle({
    Name = "Auto Fast Click",

    CurrentValue =
        Settings.AutoFastClick,

    Flag = "AutoFastClick",

    Callback = function(value)
        Settings.AutoFastClick =
            value

        persist()
    end
})

Main:CreateToggle({
    Name = "Auto Clean",

    CurrentValue =
        Settings.AutoClean,

    Flag = "AutoClean",

    Callback = function(value)
        Settings.AutoClean =
            value

        persist()
    end
})

Main:CreateToggle({
    Name = "Auto Place / Replace",

    CurrentValue =
        Settings.AutoPlace,

    Flag = "AutoPlace",

    Callback = function(value)
        Settings.AutoPlace =
            value

        persist()
    end
})

Main:CreateToggle({
    Name = "Auto Sell",

    CurrentValue =
        Settings.AutoSell,

    Flag = "AutoSell",

    Callback = function(value)
        Settings.AutoSell =
            value

        persist()
    end
})

Main:CreateToggle({
    Name = "Auto Buy Best Shovel",

    CurrentValue =
        Settings.AutoBuyShovel,

    Flag = "AutoBuyShovel",

    Callback = function(value)
        Settings.AutoBuyShovel =
            value

        persist()
    end
})

Main:CreateToggle({
    Name = "Auto Buy Best Detector",

    CurrentValue =
        Settings.AutoBuyDetector,

    Flag = "AutoBuyDetector",

    Callback = function(value)
        Settings.AutoBuyDetector =
            value

        persist()
    end
})

Main:CreateParagraph({
    Title = "Hotkeys",

    Content =
        "F = Manual Fast Click | K = Hide UI"
})

Main:CreateButton({
    Name = "TEST - Auto Clean",

    Callback = function()
        task.spawn(
            testClean
        )
    end
})

Main:CreateButton({
    Name = "TEST - Sell Random Inventory Item",

    Callback = function()
        task.spawn(
            testSell
        )
    end
})

Main:CreateButton({
    Name = "TEST - Place / Replace / Sell",

    Callback = function()
        task.spawn(
            testManage
        )
    end
})

Main:CreateButton({
    Name = "TEST - Buy Best Shovel",

    Callback = function()
        if Busy then
            return
        end

        task.spawn(function()
            Busy = true

            local success =
                autoBuyShovel()

            Busy = false

            notify(
                "Shovel Test",
                success
                    and "Upgrade handled"
                    or "No upgrade"
            )
        end)
    end
})

Main:CreateButton({
    Name = "TEST - Buy Best Detector",

    Callback = function()
        if Busy then
            return
        end

        task.spawn(function()
            Busy = true

            local success =
                autoBuyDetector()

            Busy = false

            notify(
                "Detector Test",
                success
                    and "Upgrade handled"
                    or "No upgrade"
            )
        end)
    end
})

Main:CreateButton({
    Name = "SELL ALL INVENTORY ITEMS",

    Callback = function()
        task.spawn(
            sellAll
        )
    end
})

local Islands = {}

for _,island in ipairs(
    IslandsFolder:GetChildren()
) do
    local profile =
        getIslandProfile(
            island
        )

    if #profile.Parts > 0 then
        table.insert(
            Islands,
            island
        )
    end

    island.DescendantAdded:Connect(
        function(object)
            if not alive() then
                return
            end

            if object.Name == "DigArea"
                or object.Name == "DigAreas"
                or object.Name == "DigAreaMesh" then

                invalidateIsland(
                    island
                )
            end
        end
    )

    island.DescendantRemoving:Connect(
        function(object)
            if not alive() then
                return
            end

            if object.Name == "DigArea"
                or object.Name == "DigAreas"
                or object.Name == "DigAreaMesh" then

                invalidateIsland(
                    island
                )
            end
        end
    )
end

table.sort(
    Islands,
    function(a,b)
        return
            a.Name:lower()
            <
            b.Name:lower()
    end
)

local function disableOtherToggles(selected)
    ChangingToggle = true

    for island,toggle in pairs(
        IslandToggles
    ) do
        if island ~= selected then
            pcall(function()
                toggle:Set(false)
            end)
        end
    end

    ChangingToggle = false
end

local function startFarm(island)
    if not alive()
        or not island
        or not island.Parent then

        return
    end

    disableOtherToggles(
        island
    )

    ActiveIsland = island

    Settings.AutoFarm = true
    Settings.ActiveIsland = island.Name

    CandidateLock = 0
    NextSearch = 0
    NextGearCheck = 0

    persist()

    local profile =
        getIslandProfile(
            island
        )

    notify(
        island.Name,
        "Auto Farm ON | "
        .. profile.Mode
        .. " | "
        .. tostring(#profile.Parts)
        .. " DigAreas"
    )

    if Settings.AutoDig then
        task.spawn(function()
            resetDigEquipment()

            searchDig(
                false,
                island
            )
        end)
    end
end

local function stopFarm(island)
    if ActiveIsland ~= island then
        return
    end

    Settings.AutoFarm = false
    Settings.ActiveIsland = nil

    ActiveIsland = nil

    CandidateLock = 0

    persist()

    setDetectorHeld(false)

    resetDigEquipment()

    notify(
        island.Name,
        "Auto Farm OFF"
    )
end

for _,island in ipairs(Islands) do
    local Island = island

    local profile =
        getIslandProfile(
            Island
        )

    local tab =
        Window:CreateTab(
            Island.Name,
            4483362458
        )

    tab:CreateParagraph({
        Title = "Dig System",

        Content =
            "Detected: "
            .. profile.Mode
            .. "\nDigArea parts: "
            .. tostring(#profile.Parts)
    })

    local toggle

    toggle =
        tab:CreateToggle({
            Name =
                "AUTO FARM - "
                .. Island.Name,

            CurrentValue = false,

            Flag =
                "Farm_"
                .. Island.Name,

            Callback = function(value)
                if ChangingToggle
                    or not alive() then

                    return
                end

                if value then
                    startFarm(Island)
                else
                    stopFarm(Island)
                end
            end
        })

    IslandToggles[Island] =
        toggle

    tab:CreateButton({
        Name = "TEST - Dig System",

        Callback = function()
            if Busy
                or Searching
                or isDigging() then

                return
            end

            TestDigActive = true

            task.spawn(function()
                local success =
                    searchDig(
                        true,
                        Island
                    )

                if not success then
                    TestDigActive = false
                end

                notify(
                    Island.Name,
                    success
                        and "Dig started"
                        or "No dig found"
                )
            end)
        end
    })

    tab:CreateButton({
        Name = "TEST - Teleport Candidate",

        Callback = function()
            local current =
                getIslandProfile(
                    Island
                )

            if #current.Parts == 0 then
                notify(
                    Island.Name,
                    "No DigAreas"
                )

                return
            end

            local part

            if current.Mode == "Hotspots" then
                part =
                    nextHotspot(
                        current
                    )
            else
                part =
                    nearestZone(
                        current
                    )
            end

            if not part then
                return
            end

            local success =
                teleportGround(
                    Island,

                    randomPointInPart(
                        part,

                        current.Mode == "Hotspots"
                        and 0.3
                        or 0.1
                    ),

                    current
                )

            notify(
                Island.Name,
                success
                    and "Teleported"
                    or "Raycast failed"
            )
        end
    })

    tab:CreateButton({
        Name = "Refresh Dig Detection",

        Callback = function()
            invalidateIsland(
                Island
            )

            local updated =
                getIslandProfile(
                    Island
                )

            notify(
                Island.Name,
                updated.Mode
                .. " | "
                .. tostring(#updated.Parts)
                .. " DigAreas"
            )
        end
    })
end

UserInputService.InputBegan:Connect(
    function(input,processed)
        if not alive()
            or processed then

            return
        end

        if input.KeyCode
            == Enum.KeyCode.F then

            ManualFastClick =
                not ManualFastClick

            notify(
                "Fast Click",
                ManualFastClick
                    and "ON"
                    or "OFF"
            )
        end
    end
)

task.spawn(function()
    while alive() do
        task.wait(0.012)

        if isDigging()
            and (
                ManualFastClick
                or TestDigActive
                or (
                    Settings.AutoFarm
                    and Settings.AutoFastClick
                )
            ) then

            digClick()
        end
    end
end)

task.spawn(function()
    while alive() do
        task.wait(0.04)

        if not Settings.AutoFarm
            or not ActiveIsland then

            continue
        end

        if not ActiveIsland.Parent then
            Settings.AutoFarm = false
            ActiveIsland = nil

            persist()

            continue
        end

        if Busy
            or Searching
            or isDigging() then

            continue
        end

        if Settings.AutoClean then
            local dirty =
                getDirtyItem()

            if dirty then
                Busy = true

                instantClean(
                    dirty,

                    Settings.AutoPlace
                    or Settings.AutoSell,

                    false
                )

                Busy = false

                resetDigEquipment()

                continue
            end
        end

        if not Settings.AutoDig then
            continue
        end

        if os.clock() < NextSearch then
            continue
        end

        if CandidateLock > 0
            and os.clock() < CandidateLock then

            continue
        end

        local island = ActiveIsland

        task.spawn(
            searchDig,
            false,
            island
        )
    end
end)

task.spawn(function()
    while alive() do
        task.wait(0.7)

        if not Settings.AutoFarm
            or not ActiveIsland then

            continue
        end

        if Busy
            or Searching
            or isDigging() then

            continue
        end

        if os.clock()
            < NextGearCheck then

            continue
        end

        NextGearCheck =
            os.clock() + 1.3

        local didSomething = false

        Busy = true

        if Settings.AutoBuyShovel then
            autoBuyShovel()

            didSomething = true

            task.wait(0.05)
        end

        if Settings.AutoBuyDetector then
            autoBuyDetector()

            didSomething = true
        end

        if didSomething then
            resetDigEquipment()
        end

        Busy = false
    end
end)

Player.CharacterAdded:Connect(
    function()
        if not alive() then
            return
        end

        task.wait(0.6)

        Digging = false
        Searching = false
        CandidateLock = 0

        NextSearch =
            os.clock() + 0.3

        CachedPlot = nil
        CachedPedestals = nil

        resetDigEquipment()

        if Settings.AutoFarm
            and Settings.AutoDig
            and ActiveIsland
            and ActiveIsland.Parent then

            task.spawn(
                searchDig,
                false,
                ActiveIsland
            )
        end
    end
)

local SavedIsland

if Settings.ActiveIsland then
    SavedIsland =
        IslandsFolder:FindFirstChild(
            Settings.ActiveIsland
        )
end

if Settings.AutoFarm
    and SavedIsland
    and getIslandProfile(
        SavedIsland
    ).Mode ~= "None" then

    task.delay(
        1,
        function()
            if not alive() then
                return
            end

            local toggle =
                IslandToggles[
                    SavedIsland
                ]

            if toggle then
                pcall(function()
                    toggle:Set(true)
                end)
            else
                startFarm(
                    SavedIsland
                )
            end
        end
    )
end

Rayfield:Notify({
    Title = "Dig OP",

    Content =
        ExecutorName
        .. " | Auto Farm feature toggles added | K = UI",

    Duration = 5
})

Features & Information

Works with Madium, Xeno, Real, and other executors OPEN SOURCE i dont honestly care if you take this and use it as your own INCLUDES: Auto Farm, Auto sell, Auto dig, Auto place, Auto clean, Auto buy, and more i think
#Dig#Auto#Farm#Script

Community Comments (0)

No comments yet for this script.

More Scripts for [🎃] Dig & Clean 🧼