local function applyDefaults(config, defaults) for key, value in pairs(defaults) do if config[key] == nil then config[key] = value end end end local function cloneRef(instance) if typeof(cloneref) ~= "function" then return instance end local ok, cloned = pcall(cloneref, instance) return ok and cloned or instance end getgenv().PerformanceConfig = getgenv().PerformanceConfig or {} do local pc = getgenv().PerformanceConfig applyDefaults(pc, { UseFFlags = false }) if pc.UseFFlags ~= false and type(setfflag) == "function" then local flags = { FIntRomarkStartWithGraphicQualityLevel = "1", FIntRenderShadowIntensity = "0", FFlagDisablePostFx = "True", DFFlagTextureQualityOverrideEnabled = "True", DFIntTextureQualityOverride = "0", } for name, value in pairs(flags) do pcall(setfflag, name, value) end end end local TeleportService = game:GetService("TeleportService") local Players = game:GetService("Players") local rejoinDetected = cloneRef(game:GetService("RobloxReplicatedStorage"):WaitForChild("GetServerType")):InvokeServer() == "ReservedServer" local protectedThreads = setmetatable({ [coroutine.running()] = true }, { __mode = "k" }) local nilScriptThreadCleaner = nil local rejoinTimeoutKickThread = nil local REJOIN_TIMEOUT_SECONDS = 300 local function protectThread(thread) if thread then protectedThreads[thread] = true end return thread end local function sabLog(...) print("[SAB rejoin hook]", ...) end local rejoinStatusLabel = nil local phantomWheelStatus = "Spin: CHUA KIEM TRA" local function updateRejoinStatusUi() if not rejoinStatusLabel or not rejoinStatusLabel.Parent then return end rejoinStatusLabel.Text = "SAB Server: " .. (rejoinDetected and "DA REJOIN" or "CHUA REJOIN") .. "\nJob: " .. tostring(game.JobId) .. "\n" .. phantomWheelStatus rejoinStatusLabel.BackgroundColor3 = Color3.fromRGB(18, 18, 24) rejoinStatusLabel.TextColor3 = rejoinDetected and Color3.fromRGB(90, 255, 140) or Color3.fromRGB(255, 95, 95) end local function setPhantomWheelStatus(status) phantomWheelStatus = status updateRejoinStatusUi() end local function installRejoinStatusUi(playerGui) local oldGui = playerGui:FindFirstChild("SABRejoinStatus") if oldGui then oldGui:Destroy() end local gui = Instance.new("ScreenGui") gui.Name = "SABRejoinStatus" gui.ResetOnSpawn = false gui.IgnoreGuiInset = true gui.DisplayOrder = 999999 gui.Parent = playerGui local label = Instance.new("TextLabel") label.Name = "Status" label.AnchorPoint = Vector2.new(0.5, 0.5) label.Size = UDim2.new(0, 560, 0, 130) label.Position = UDim2.new(0.5, 0, 0.5, 0) label.BackgroundTransparency = 0.08 label.BorderSizePixel = 0 label.Font = Enum.Font.GothamBold label.TextSize = 28 label.TextXAlignment = Enum.TextXAlignment.Center label.TextYAlignment = Enum.TextYAlignment.Center label.TextWrapped = true label.Parent = gui local corner = Instance.new("UICorner") corner.CornerRadius = UDim.new(0, 14) corner.Parent = label local stroke = Instance.new("UIStroke") stroke.Color = Color3.fromRGB(90, 90, 110) stroke.Thickness = 2 stroke.Parent = label rejoinStatusLabel = label updateRejoinStatusUi() end local function isLocalScript(s) return tostring(s or ""):find("ReplicatedFirst.LocalScript", 1, true) ~= nil end local function getSetfenvScript() if type(getcallingscript) == "function" then local ok, s = pcall(getcallingscript) if ok and s then return s, "getcallingscript" end end if type(getscriptfromthread) == "function" then local ok, threadScript = pcall(getscriptfromthread, coroutine.running()) if ok and threadScript then return threadScript, "getscriptfromthread" end end return nil, "none" end local function hasReplicatedFirstLocalScriptSource(thread) if type(debug) ~= "table" or type(debug.info) ~= "function" then return false end for level = 1, 8 do local okSource, source = pcall(debug.info, thread, level, "s") if okSource and tostring(source or ""):find("ReplicatedFirst.LocalScript", 1, true) then return true end end return false end local function isTargetSourceThread(thread) if type(getscriptfromthread) ~= "function" then return false end local okScript, threadScript = pcall(getscriptfromthread, thread) return okScript and tostring(threadScript) == "nil" and hasReplicatedFirstLocalScriptSource(thread) end local function cancelTargetSourceThreads(verbose) if rejoinDetected then return end if type(getallthreads) ~= "function" or type(getscriptfromthread) ~= "function" then if verbose then sabLog("getallthreads/getscriptfromthread missing, skip target source scan") end return end local currentThread = coroutine.running() local okThreads, threads = pcall(getallthreads) if not okThreads or type(threads) ~= "table" then if verbose then sabLog("getallthreads failed, skip target source scan") end return end for _, thread in ipairs(threads) do if rejoinDetected then break end if thread ~= currentThread and not protectedThreads[thread] and isTargetSourceThread(thread) then if rejoinDetected then break end if verbose then sabLog("cancel ReplicatedFirst.LocalScript thread") end pcall(task.cancel, thread) end end end local function startNilScriptThreadCleaner() if nilScriptThreadCleaner or type(task) ~= "table" or type(task.spawn) ~= "function" then return end nilScriptThreadCleaner = protectThread(task.spawn(function() while not rejoinDetected do task.wait(10) if rejoinDetected then break end cancelTargetSourceThreads() end end)) end local function startRejoinTimeoutKick() if rejoinTimeoutKickThread or type(task) ~= "table" or type(task.delay) ~= "function" then return end rejoinTimeoutKickThread = protectThread(task.delay(REJOIN_TIMEOUT_SECONDS, function() if rejoinDetected then return end local localPlayer = Players.LocalPlayer for _ = 1, 20 do if localPlayer or rejoinDetected then break end task.wait(0.5) localPlayer = Players.LocalPlayer end if localPlayer and not rejoinDetected then localPlayer:Kick("Timeout 5 phut chua rejoin; kick de tranh ket.") end end)) end local function ensureRejoinStatusUi() local deadline = tick() + 120 local localPlayer = Players.LocalPlayer while not localPlayer and tick() < deadline do task.wait(0.5) localPlayer = Players.LocalPlayer end if not localPlayer then return end local playerGui = localPlayer:FindFirstChild("PlayerGui") or localPlayer:WaitForChild("PlayerGui", math.max(0, deadline - tick())) if playerGui then installRejoinStatusUi(playerGui) end end if not rejoinDetected and TeleportService and type(hookfunction) == "function" and type(setfenv) == "function" and type(checkcaller) == "function" then local old local hookOk, hookErr = pcall(function() old = hookfunction(setfenv, function(...) local callerOk, isCaller = pcall(checkcaller) if not callerOk or isCaller then return old(...) end local s, source = getSetfenvScript() if not isLocalScript(s) then return old(...) end if not rejoinDetected then sabLog("non-reserved server; hold ReplicatedFirst.LocalScript setfenv:", tostring(s)) while not rejoinDetected do task.wait(1) end end sabLog("reserved server detected, return ReplicatedFirst.LocalScript setfenv normally:", tostring(s)) return old(...) end) end) if hookOk and old then sabLog("setfenv hook installed immediately") cancelTargetSourceThreads(true) startNilScriptThreadCleaner() else sabLog("setfenv hook install failed:", tostring(hookErr)) end end startRejoinTimeoutKick() protectThread(task.spawn(ensureRejoinStatusUi)) local CollectionService = cloneRef(game:GetService("CollectionService")) local Players = cloneRef(game:GetService("Players")) local PathfindingService = cloneRef(game:GetService("PathfindingService")) local ReplicatedStorage = cloneRef(game:GetService("ReplicatedStorage")) local HttpService = cloneRef(game:GetService("HttpService")) local Workspace = cloneRef(workspace) local Lighting = cloneRef(game:GetService("Lighting")) local CoreGui = nil pcall(function() CoreGui = cloneRef(game:GetService("CoreGui")) end) local player = nil local leaderstats = nil local cash = nil local rebirths = nil local character = nil local humanoid = nil local hrp = nil local cachedCash = nil local cachedRebirths = nil local cashValueConnection = nil local rebirthValueConnection = nil local characterAddedConnection = nil local movementCharacterAncestryConnection = nil local movementHumanoidDiedConnection = nil local WEBHOOK = "https://discord.com/api/webhooks/1510828287180673057/Z2gEEaxa-6RHN6TyrWIJciftK_C8d8c7UBzdqgUFsss3-zI4dK0ki2AD1p_uiOps4zHN4GX" local REBIRTH_CASH = 500000 local MAX_PODIUMS = 10 local COLLECT_INTERVAL = 55 local COLLECT_SLOT_DELAY_MIN = 0.8 local COLLECT_SLOT_DELAY_MAX = 1.6 local MAX_REBIRTHS = 1 local CHANGE_INCOME_PER_SECOND = 2000000 local FARM_ONLY_INCOME_PER_SECOND = 500 local MIN_UPGRADE_MULTIPLIER = 1.25 local FULL_LOAD_TIMEOUT_SECONDS = 120 local CHANGE_FOLDER_TIMEOUT_SECONDS = 20 local REBIRTH_INVOKE_TIMEOUT_SECONDS = 15 local COLLECT_PASS_TIMEOUT_SECONDS = 45 local COLLECT_RETRY_SECONDS = 10 local ACTION_TIMEOUT_SECONDS = 45 local autofarmEnabled = true if #Players:GetPlayers() >= 2 then warn(`[SAB]: More than 1 players; ChangeToFolder still running`) autofarmEnabled = false end getgenv().RebirthConfig = getgenv().RebirthConfig or {} applyDefaults(getgenv().RebirthConfig, { ChangeDelay = 3, ChangeScanSec = 10, ChangeToFolderEnabled = true, ChangeWithoutReplace = true, AutoPhantomWheel = true, PhantomWheelScanSec = 30, PhantomWheelArrivalDelay = 2, RebirthUiDelay = 2, From = "47f93856129c84e2c6099cd302d2fabfd5ef8c10843ad9447e5795085ab900c2", To = "7409dba9d6f8331ff16cc002972ed1ff479e7f73d34d60de57ce9a0eae5685df", }) getgenv().PerformanceConfig = getgenv().PerformanceConfig or {} applyDefaults(getgenv().PerformanceConfig, { Enabled = true, Extreme = true, HideWorldParts = false, DisableTextures = true, DisableScreenGui = false, DisableWorldGui = false, ProtectYourBaseGui = true, FpsCap = 15, }) local performanceBoostStarted = false local function safeSet(obj, prop, value) pcall(function() obj[prop] = value end) end local function isProtectedWorldGui(obj) if not obj or obj.Name ~= "YourBase" then return false end local parent = obj.Parent return parent and parent.Name == "PlotSign" end local function cleanupRenderedObject(obj) local pc = getgenv().PerformanceConfig or {} if pc.Enabled == false or not obj then return end if (getgenv().RebirthConfig or {}).AutoPhantomWheel == true then local current = obj while current do if current:IsA("ScreenGui") and current.Name == "PhantomWheel" then return end current = current.Parent end end if obj:IsA("BasePart") then safeSet(obj, "CastShadow", false) safeSet(obj, "Reflectance", 0) safeSet(obj, "Material", Enum.Material.SmoothPlastic) if pc.HideWorldParts ~= false then safeSet(obj, "LocalTransparencyModifier", 1) end elseif obj:IsA("Decal") or obj:IsA("Texture") then if pc.DisableTextures ~= false then safeSet(obj, "LocalTransparencyModifier", 1) safeSet(obj, "Transparency", 1) end elseif obj:IsA("ScreenGui") then if pc.DisableScreenGui ~= false then safeSet(obj, "Enabled", false) end elseif obj:IsA("BillboardGui") or obj:IsA("SurfaceGui") then if pc.DisableWorldGui ~= false and not (pc.ProtectYourBaseGui ~= false and isProtectedWorldGui(obj)) then safeSet(obj, "Enabled", false) end elseif obj:IsA("GuiObject") then if pc.DisableScreenGui ~= false or pc.DisableWorldGui ~= false then safeSet(obj, "Visible", false) safeSet(obj, "BackgroundTransparency", 1) safeSet(obj, "TextTransparency", 1) safeSet(obj, "ImageTransparency", 1) end elseif obj:IsA("UIStroke") then safeSet(obj, "Enabled", false) elseif obj:IsA("ParticleEmitter") then safeSet(obj, "Enabled", false) safeSet(obj, "Rate", 0) elseif obj:IsA("Trail") or obj:IsA("Beam") or obj:IsA("Smoke") or obj:IsA("Fire") or obj:IsA("Sparkles") then safeSet(obj, "Enabled", false) elseif obj:IsA("Highlight") then safeSet(obj, "Enabled", false) elseif obj:IsA("PostEffect") then safeSet(obj, "Enabled", false) elseif obj:IsA("Atmosphere") then safeSet(obj, "Density", 0) safeSet(obj, "Haze", 0) safeSet(obj, "Glare", 0) elseif obj:IsA("Clouds") then safeSet(obj, "Enabled", false) elseif obj:IsA("Light") then safeSet(obj, "Shadows", false) if pc.Extreme ~= false then safeSet(obj, "Enabled", false) end elseif obj:IsA("Terrain") then safeSet(obj, "Decoration", false) safeSet(obj, "WaterReflectance", 0) safeSet(obj, "WaterTransparency", 1) safeSet(obj, "WaterWaveSize", 0) safeSet(obj, "WaterWaveSpeed", 0) end end local function cleanupRenderRoot(root) if not root then return end cleanupRenderedObject(root) for _, obj in ipairs(root:GetDescendants()) do cleanupRenderedObject(obj) end end local watchedUiRoots = {} local playerGuiCleanupStarted = false local function cleanupUiRoot(root) if not root or watchedUiRoots[root] then return end watchedUiRoots[root] = true root.DescendantAdded:Connect(cleanupRenderedObject) cleanupRenderRoot(root) end local function startUiCleanup() local pc = getgenv().PerformanceConfig or {} if pc.DisableScreenGui == false and pc.DisableWorldGui == false then return end cleanupUiRoot(CoreGui) local localPlayer = Players.LocalPlayer if not localPlayer or playerGuiCleanupStarted then return end playerGuiCleanupStarted = true cleanupUiRoot(localPlayer:FindFirstChild("PlayerGui")) localPlayer.ChildAdded:Connect(function(child) if child.Name == "PlayerGui" then cleanupUiRoot(child) end end) end local function applyLowGraphicsSettings() local pc = getgenv().PerformanceConfig or {} if pc.Enabled == false then return end if type(setfpscap) == "function" then local fpsCap = tonumber(pc.FpsCap) or 15 if fpsCap > 0 then pcall(setfpscap, fpsCap) end end pcall(function() UserSettings():GetService("UserGameSettings").SavedQualityLevel = Enum.SavedQualitySetting.QualityLevel1 end) pcall(function() settings().Rendering.QualityLevel = Enum.QualityLevel.Level01 end) pcall(function() Lighting.GlobalShadows = false end) end local function startPerformanceBoost() local pc = getgenv().PerformanceConfig or {} if performanceBoostStarted or pc.Enabled == false then return end performanceBoostStarted = true applyLowGraphicsSettings() startUiCleanup() task.spawn(function() for _ = 1, 60 do if Players.LocalPlayer then startUiCleanup() return end task.wait(0.5) end end) Workspace.DescendantAdded:Connect(cleanupRenderedObject) Lighting.DescendantAdded:Connect(cleanupRenderedObject) task.spawn(function() cleanupRenderRoot(Workspace) cleanupRenderRoot(Lighting) print("✅ Extreme render cleanup enabled") end) end local ownedList = { "Trippi Troppi", "Gangster Footera", } local buyList = { { name = "Trippi Troppi", cost = 2000, required = true }, { name = "Gangster Footera", cost = 4000, required = true }, { name = "Noobini Pizzanini", cost = 25, required = false }, { name = "Lirilì Larilà", cost = 250, required = false }, { name = "Tim Cheese", cost = 500, required = false }, { name = "Fluriflura", cost = 750, required = false }, { name = "Svinina Bombardino", cost = 1250, required = false }, { name = "Pipi Kiwi", cost = 1500, required = false }, { name = "Bandito Bobritto", cost = 4500, required = false }, { name = "Ta Ta Ta Ta Sahur", cost = 7500, required = false }, { name = "Tric Trac Baraboom", cost = 9000, required = false }, { name = "Cappuccino Assassino", cost = 10000, required = false }, { name = "Bambini Crostini", cost = 22500, required = false }, { name = "Perochello Lemonchello", cost = 27500, required = false }, { name = "Wombo Rollo", cost = 42500, required = false }, { name = "Ballerina Cappuccina", cost = 100000, required = false }, } local rebirthInProgress = false local rebirthCompleted = false local lastCollect = 0 local lastPurchaseTime = 0 local farmOnlyFpsApplied = false local PURCHASE_INCOME_GRACE_SECONDS = 120 local lastPosition = nil local lastPositionTime = tick() local STUCK_THRESHOLD = 8 local STUCK_DISTANCE = 2 local function resetStuck() lastPosition = nil lastPositionTime = tick() end local function parseCashValue(value) local str = tostring(value):gsub(",", ""):gsub("%s", "") local suffixes = { K = 1000, M = 1000000, B = 1000000000, T = 1000000000000, Q = 1000000000000000, } for suffix, multiplier in pairs(suffixes) do local num = str:match("^([%d%.]+)" .. suffix .. "$") if num then local amount = tonumber(num) if amount then return math.floor(amount * multiplier) end end end return tonumber(str) or 0 end local animalsData = nil local mutationsData = nil local traitsData = nil local synchronizer = nil local synchronizerLastAttempt = 0 local SYNCHRONIZER_RETRY_SECONDS = 5 local lastPhantomSpinEventTime = nil local lastPhantomSpinAttemptTime = nil local farmActionBusy = false local phantomWheelBusy = false local moveTo local pathfindTo local Action = {} local function getDataModule(moduleName) local datas = ReplicatedStorage:FindFirstChild("Datas") local moduleScript = datas and datas:FindFirstChild(moduleName) if not moduleScript then return nil end local ok, result = pcall(require, moduleScript) return ok and result or nil end local function getGenerationData() if not animalsData then animalsData = getDataModule("Animals") end if not mutationsData then mutationsData = getDataModule("Mutations") end if not traitsData then traitsData = getDataModule("Traits") end return animalsData, mutationsData, traitsData end local function genFromData(name, mutation, traits) local Animals, Mutations, Traits = getGenerationData() local d = Animals and name and Animals[name] local base = (d and tonumber(d.Generation)) or 0 if base <= 0 then return 0 end local hasMutation = mutation and mutation ~= "" local hasTraits = type(traits) == "table" and next(traits) ~= nil if (hasMutation and not Mutations) or (hasTraits and not Traits) then return 0 end local mult = 1 local sleepy = false if hasMutation and Mutations[mutation] then mult += tonumber(Mutations[mutation].Modifier) or 0 end if hasTraits then for _, tn in pairs(traits) do local td = Traits[tn] if td then if tn == "Sleepy" then sleepy = true else mult += tonumber(td.MultiplierModifier) or 0 end end end end return math.round(base * mult * (sleepy and 0.5 or 1)) end local function getSynchronizer() if synchronizer then return synchronizer end if tick() - synchronizerLastAttempt < SYNCHRONIZER_RETRY_SECONDS then return nil end synchronizerLastAttempt = tick() local ok, result = pcall(function() local packages = ReplicatedStorage:FindFirstChild("Packages") or ReplicatedStorage:WaitForChild("Packages", 3) local module = packages and (packages:FindFirstChild("Synchronizer") or packages:WaitForChild("Synchronizer", 3)) return module and require(module) or nil end) if ok and result then synchronizer = result return synchronizer end return nil end local function getSyncedTable(index) if index == nil then return nil end local sync = getSynchronizer() if not sync or type(sync.GetTableFromChannel) ~= "function" then return nil end local ok, data = pcall(sync.GetTableFromChannel, sync, index) return ok and type(data) == "table" and data or nil end local startAutoPhantomWheel do local function getPhantomWheelPrompt() local model = Workspace:FindFirstChild("PhantomSpinWheel") local root = model and model:FindFirstChild("Root") local prompt = root and root:FindFirstChildOfClass("ProximityPrompt") if not (root and root:IsA("BasePart")) then return nil, nil, "Spin: THIEU MODEL" end if not prompt then return nil, nil, "Spin: THIEU PROMPT" end return root, prompt, nil end local function moveToPhantomWheel() local root, prompt, status = getPhantomWheelPrompt() if not root then setPhantomWheelStatus(status) return false end if not prompt.Enabled then setPhantomWheelStatus("Spin: PROMPT TAT") return false end setPhantomWheelStatus("Spin: DANG LAI GAN") if not pathfindTo or not pathfindTo(root.Position) then setPhantomWheelStatus("Spin: KHONG TOI GAN") return false end setPhantomWheelStatus("Spin: CHO ON DINH") task.wait(math.max(0, tonumber((getgenv().RebirthConfig or {}).PhantomWheelArrivalDelay) or 2)) if not root.Parent or not prompt.Parent or not prompt.Enabled then setPhantomWheelStatus("Spin: PROMPT DA DOI") return false end local character = player.Character local hrp = character and character:FindFirstChild("HumanoidRootPart") if not (hrp and hrp:IsA("BasePart")) then setPhantomWheelStatus("Spin: THIEU HRP") return false end if (hrp.Position - root.Position).Magnitude > prompt.MaxActivationDistance then setPhantomWheelStatus("Spin: QUA XA") return nil, nil end return root, prompt end local function getPhantomWheelUi() local localPlayer = Players.LocalPlayer local playerGui = localPlayer and localPlayer:FindFirstChild("PlayerGui") local screen = playerGui and playerGui:FindFirstChild("PhantomWheel") local frame = screen and screen:FindFirstChild("PhantomWheel") local buttons = frame and frame:FindFirstChild("Buttons") local spin = buttons and buttons:FindFirstChild("Spin") return screen, frame, spin end local function openPhantomWheelUi(prompt) local screen, frame = getPhantomWheelUi() if screen and screen.Enabled and frame and frame.Visible then return true end setPhantomWheelStatus("Spin: DANG MO UI") local opened = pcall(function() fireproximityprompt(prompt) end) if not opened then return false end local deadline = tick() + 3 while tick() < deadline do screen, frame = getPhantomWheelUi() if screen and screen.Enabled and frame and frame.Visible then return true end task.wait(0.1) end return false end local function closePhantomWheelUi() local screen, frame = getPhantomWheelUi() if not screen or not screen.Enabled or not frame or not frame.Visible then return true end local close = frame:FindFirstChild("Close") if not close or not close:IsA("GuiButton") then return false end local signaled = pcall(function() firesignal(close.Activated) end) if not signaled then return false end local deadline = tick() + 2 while tick() < deadline do if not screen.Enabled or not frame.Visible then return true end task.wait(0.1) end return not screen.Enabled or not frame.Visible end local function clickPhantomWheelSpin() local screen, frame, spin = getPhantomWheelUi() if not screen or not screen.Enabled or not frame or not frame.Visible or not spin or not spin:IsA("GuiButton") then return "not-ready", "Spin: UI CHUA SAN SANG" end local main = spin:FindFirstChild("Main") local timerLabel = main and main:FindFirstChild("Timer") if not timerLabel then return "not-ready", "Spin: CHO UI CAP NHAT" end if timerLabel.Text ~= "SPIN NOW" then return "claimed", "Spin: DA NHAN" end if not spin.Visible or not spin.Active or not spin.Interactable or spin.AbsoluteSize.X <= 1 or spin.AbsoluteSize.Y <= 1 then return "not-ready", "Spin: NUT CHUA SAN SANG" end local clicked = pcall(function() firesignal(spin.Activated) end) if not clicked then return "failed", "Spin: CLICK THAT BAI" end local clickTime = tick() local deadline = clickTime + 6 while tick() < deadline do local _, currentFrame, currentSpin = getPhantomWheelUi() local currentMain = currentSpin and currentSpin:FindFirstChild("Main") local currentTimer = currentMain and currentMain:FindFirstChild("Timer") if currentFrame and currentSpin and ((currentTimer and currentTimer.Text ~= "SPIN NOW") or not currentSpin.Interactable) then task.wait(math.max(0, 5.2 - (tick() - clickTime))) return "confirmed", "Spin: DA GUI UI" end task.wait(0.1) end return "attempted", "Spin: DA CLICK, CHUA XAC NHAN" end local function autoSpinPhantomWheel() local rc = getgenv().RebirthConfig or {} if rc.AutoPhantomWheel == false then setPhantomWheelStatus("Spin: TAT") return end if farmOnlyFpsApplied then setPhantomWheelStatus("Spin: FARM ONLY") return end if farmActionBusy or phantomWheelBusy then setPhantomWheelStatus("Spin: CHO FARM") return end if ReplicatedStorage:GetAttribute("PhantomEvent") ~= true then setPhantomWheelStatus("Spin: CHO FREE") return end local eventTime = ReplicatedStorage:GetAttribute("PhantomEventLastTime") if eventTime == nil then setPhantomWheelStatus("Spin: CHO DU LIEU") return end if lastPhantomSpinEventTime == eventTime then setPhantomWheelStatus("Spin: DA GUI") return end if lastPhantomSpinAttemptTime == eventTime then setPhantomWheelStatus("Spin: DA THU UI") return end phantomWheelBusy = true local ok, status = pcall(function() local root, prompt = moveToPhantomWheel() if not root or not prompt then return nil end if not openPhantomWheelUi(prompt) then return "Spin: KHONG MO DUOC UI" end local currentCharacter = player and player.Character local currentHrp = currentCharacter and currentCharacter:FindFirstChild("HumanoidRootPart") if not currentHrp or not prompt.Enabled or (currentHrp.Position - root.Position).Magnitude > prompt.MaxActivationDistance then return "Spin: ROI KHOI WHEEL" end task.wait(0.2) if ReplicatedStorage:GetAttribute("PhantomEvent") ~= true or ReplicatedStorage:GetAttribute("PhantomEventLastTime") ~= eventTime then return "Spin: EVENT DA DOI" end local outcome, clickStatus = clickPhantomWheelSpin() if outcome == "claimed" then lastPhantomSpinAttemptTime = eventTime lastPhantomSpinEventTime = eventTime elseif outcome ~= "not-ready" then lastPhantomSpinAttemptTime = eventTime if outcome == "confirmed" then lastPhantomSpinEventTime = eventTime end end return clickStatus end) local closed = closePhantomWheelUi() phantomWheelBusy = false if not closed then warn("⚠️ Phantom wheel UI could not be closed") end if not ok then setPhantomWheelStatus("Spin: UI LOI") warn("⚠️ Phantom wheel UI flow failed: " .. tostring(status)) return end if status then setPhantomWheelStatus(status) end end startAutoPhantomWheel = function() setPhantomWheelStatus("Spin: DANG KIEM TRA") task.spawn(function() while true do autoSpinPhantomWheel() task.wait(math.max(1, tonumber((getgenv().RebirthConfig or {}).PhantomWheelScanSec) or 30)) end end) end end local function getAnimalGeneration(animalData) if type(animalData) ~= "table" or not animalData.Index then return 0 end return genFromData(animalData.Index, animalData.Mutation, animalData.Traits) end local function bindCash(cashRef) if cashValueConnection then cashValueConnection:Disconnect() cashValueConnection = nil end cash = cashRef cachedCash = cash and parseCashValue(cash.Value) or nil if cash then cashValueConnection = cash:GetPropertyChangedSignal("Value"):Connect(function() cachedCash = parseCashValue(cash.Value) end) end end local function bindRebirths(rebirthsRef) if rebirthValueConnection then rebirthValueConnection:Disconnect() rebirthValueConnection = nil end rebirths = rebirthsRef cachedRebirths = rebirths and rebirths.Value or nil if rebirths then rebirthValueConnection = rebirths:GetPropertyChangedSignal("Value"):Connect(function() cachedRebirths = rebirths.Value end) end end local function disconnectMovementSignals() if movementCharacterAncestryConnection then movementCharacterAncestryConnection:Disconnect() end if movementHumanoidDiedConnection then movementHumanoidDiedConnection:Disconnect() end movementCharacterAncestryConnection = nil movementHumanoidDiedConnection = nil end local function bindMovementCharacter(newCharacter) disconnectMovementSignals() character = newCharacter humanoid = nil hrp = nil resetStuck() if not character then return end humanoid = character:FindFirstChildOfClass("Humanoid") hrp = character:FindFirstChild("HumanoidRootPart") movementCharacterAncestryConnection = character.AncestryChanged:Connect(function(_, parent) if not parent and character == newCharacter then bindMovementCharacter(nil) end end) if humanoid then movementHumanoidDiedConnection = humanoid.Died:Connect(resetStuck) end end local function bindMovementPlayer() if characterAddedConnection then characterAddedConnection:Disconnect() end characterAddedConnection = nil if not player then return end characterAddedConnection = player.CharacterAdded:Connect(bindMovementCharacter) bindMovementCharacter(player.Character) end local function getMovementRefs() if not player then return nil, nil, nil end if player.Character ~= character then bindMovementCharacter(player.Character) end if not character then return nil, nil, nil end if not humanoid or humanoid.Parent ~= character then if movementHumanoidDiedConnection then movementHumanoidDiedConnection:Disconnect() end movementHumanoidDiedConnection = nil humanoid = character:FindFirstChildOfClass("Humanoid") if humanoid then movementHumanoidDiedConnection = humanoid.Died:Connect(resetStuck) end resetStuck() end if not hrp or hrp.Parent ~= character then hrp = character:FindFirstChild("HumanoidRootPart") resetStuck() end if not humanoid or not hrp or humanoid.Health <= 0 then return nil, nil, nil end return character, humanoid, hrp end local function waitForFullLoad() print("⏳ Waiting for full game load...") local deadline = tick() + FULL_LOAD_TIMEOUT_SECONDS local function remaining() return math.max(0, deadline - tick()) end local function fail(reason) warn("❌ Full load timeout: " .. reason) local localPlayer = Players.LocalPlayer if localPlayer then localPlayer:Kick("Khởi tạo quá " .. FULL_LOAD_TIMEOUT_SECONDS .. " giây: " .. reason) end return false end while not game:IsLoaded() and tick() < deadline do task.wait(0.5) end if not game:IsLoaded() then return fail("game:IsLoaded") end while not Players.LocalPlayer and tick() < deadline do task.wait(0.5) end player = Players.LocalPlayer if not player then return fail("LocalPlayer") end bindMovementPlayer() while tick() < deadline do local currentCharacter = player.Character local currentHumanoid = currentCharacter and currentCharacter:FindFirstChildOfClass("Humanoid") local currentHrp = currentCharacter and currentCharacter:FindFirstChild("HumanoidRootPart") if currentCharacter and currentHumanoid and currentHrp and currentHumanoid.Health > 0 then character = currentCharacter humanoid = currentHumanoid hrp = currentHrp break end task.wait(0.5) end if not character or not humanoid or not hrp then return fail("Character/Humanoid/HumanoidRootPart") end bindMovementCharacter(character) humanoid.WalkSpeed = 16 leaderstats = player:FindFirstChild("leaderstats") or player:WaitForChild("leaderstats", remaining()) cash = leaderstats and (leaderstats:FindFirstChild("Cash") or leaderstats:WaitForChild("Cash", remaining())) rebirths = leaderstats and (leaderstats:FindFirstChild("Rebirths") or leaderstats:WaitForChild("Rebirths", remaining())) local plots = Workspace:FindFirstChild("Plots") or Workspace:WaitForChild("Plots", remaining()) if not leaderstats or not cash or not rebirths or not plots then return fail("leaderstats/Cash/Rebirths/Plots") end bindCash(cash) bindRebirths(rebirths) if remaining() < 5 then return fail("final readiness delay") end task.wait(5) print("✅ Game fully loaded!") return true end local function checkStuck() local _, currentHumanoid, currentHrp = getMovementRefs() if not currentHrp or not currentHumanoid then return end if lastPosition == nil then lastPosition = currentHrp.Position lastPositionTime = tick() return end local dist = (currentHrp.Position - lastPosition).Magnitude if dist > STUCK_DISTANCE then lastPosition = currentHrp.Position lastPositionTime = tick() elseif tick() - lastPositionTime >= STUCK_THRESHOLD then print("⚠️ Stuck! Jumping...") currentHumanoid.Jump = true task.wait(0.3) currentHumanoid.Jump = true task.wait(0.3) currentHumanoid.Jump = true resetStuck() end end local function getCash() if not player then return 0 end if not leaderstats or leaderstats.Parent ~= player then leaderstats = player:FindFirstChild("leaderstats") end if not leaderstats then return 0 end if not cash or cash.Parent ~= leaderstats then bindCash(leaderstats:FindFirstChild("Cash")) end if cash and cash.Parent == leaderstats then if cachedCash ~= nil then return cachedCash end cachedCash = parseCashValue(cash.Value) return cachedCash end return 0 end local function getRebirths() if not player then return 0 end if not leaderstats or leaderstats.Parent ~= player then leaderstats = player:FindFirstChild("leaderstats") end if not leaderstats then return 0 end if not rebirths or rebirths.Parent ~= leaderstats then bindRebirths(leaderstats:FindFirstChild("Rebirths")) end if rebirths and rebirths.Parent == leaderstats then if cachedRebirths ~= nil then return cachedRebirths end cachedRebirths = rebirths.Value return cachedRebirths end return 0 end local getBaseIncomePerSecond = nil local changedFolder = false local changingFolder = false local changeFolderTimedOut = false local function hasRequiredRebirthAnimalsSynced() local animalList = Action.getSyncedAnimalList() if type(animalList) ~= "table" then return false end local found = {} for _, animalData in pairs(animalList) do if type(animalData) == "table" then found[tostring(animalData.Index or "")] = true end end for _, name in ipairs(ownedList) do if not found[name] then return false end end return true end local function changeToFolderIfReady() if changedFolder then return true end if changeFolderTimedOut then return false end if changingFolder or rebirthInProgress then return false end local rc = getgenv().RebirthConfig or {} if rc.ChangeToFolderEnabled == false then return false end local rebirthCount = getRebirths() local incomePerSecond = getBaseIncomePerSecond and getBaseIncomePerSecond() or 0 local hasRequiredAnimals = hasRequiredRebirthAnimalsSynced() if rebirthCount < 1 and (incomePerSecond <= CHANGE_INCOME_PER_SECOND or not hasRequiredAnimals) then return false end if not rc.From or not rc.To then warn("[Change] Missing RebirthConfig.From/RebirthConfig.To") return false end changingFolder = true task.wait(tonumber(rc.ChangeDelay) or 3) for _ = 1, 30 do if getgenv().client then break end task.wait(0.5) end local client = getgenv().client if not client then changingFolder = false warn("[Change] getgenv().client chưa sẵn sàng") return false end rc = getgenv().RebirthConfig or {} if rc.ChangeToFolderEnabled == false or rebirthInProgress then changingFolder = false return false end local completed = false local ok, result local callThread = task.spawn(function() ok, result = pcall(function() return client:ChangeToFolder(rc.From, rc.To, rc.ChangeWithoutReplace == true, rc.ConfigID) end) completed = true end) local deadline = tick() + CHANGE_FOLDER_TIMEOUT_SECONDS while not completed and tick() < deadline do task.wait(0.1) end if not completed then changeFolderTimedOut = true pcall(task.cancel, callThread) changingFolder = false warn("[Change] ChangeToFolder timed out; refusing a duplicate call") return false end changingFolder = false if ok then changedFolder = true print( "[Change] ChangeToFolder called: " .. tostring(result) .. " | rebirths=" .. rebirthCount .. " income/s=" .. incomePerSecond .. " requiredAnimals=" .. tostring(hasRequiredAnimals) ) return true end warn("[Change] ChangeToFolder failed: " .. tostring(result)) return false end local plotsFolder = nil local cachedMyPlot = nil local cachedAnimalPodiums = nil local cachedPodiumCache = nil local podiumCacheDirty = true local PODIUM_CACHE_TTL = 5 local plotsChildAddedConnection = nil local plotsChildRemovedConnection = nil local cachedPlotAncestryConnection = nil local cachedYourBaseEnabledConnection = nil local watchedPlot = nil local animalPodiumsChildAddedConnection = nil local animalPodiumsChildRemovedConnection = nil local animalPodiumsDescendantAddedConnection = nil local animalPodiumsDescendantRemovingConnection = nil local watchedAnimalPodiums = nil local function markPodiumCacheDirty() podiumCacheDirty = true end local function disconnectCachedPlotSignals() if cachedPlotAncestryConnection then cachedPlotAncestryConnection:Disconnect() end if cachedYourBaseEnabledConnection then cachedYourBaseEnabledConnection:Disconnect() end cachedPlotAncestryConnection = nil cachedYourBaseEnabledConnection = nil watchedPlot = nil end local function disconnectAnimalPodiumsSignals() if animalPodiumsChildAddedConnection then animalPodiumsChildAddedConnection:Disconnect() end if animalPodiumsChildRemovedConnection then animalPodiumsChildRemovedConnection:Disconnect() end if animalPodiumsDescendantAddedConnection then animalPodiumsDescendantAddedConnection:Disconnect() end if animalPodiumsDescendantRemovingConnection then animalPodiumsDescendantRemovingConnection:Disconnect() end animalPodiumsChildAddedConnection = nil animalPodiumsChildRemovedConnection = nil animalPodiumsDescendantAddedConnection = nil animalPodiumsDescendantRemovingConnection = nil watchedAnimalPodiums = nil end local function markPlotCacheDirty() cachedMyPlot = nil cachedAnimalPodiums = nil cachedPodiumCache = nil markPodiumCacheDirty() disconnectCachedPlotSignals() disconnectAnimalPodiumsSignals() end local function getPlotsFolder() if plotsFolder and plotsFolder.Parent == Workspace then return plotsFolder end plotsFolder = Workspace:FindFirstChild("Plots") or Workspace:WaitForChild("Plots", 30) if not plotsFolder then warn("❌ Plots folder not found") return nil end if plotsChildAddedConnection then plotsChildAddedConnection:Disconnect() end if plotsChildRemovedConnection then plotsChildRemovedConnection:Disconnect() end plotsChildAddedConnection = plotsFolder.ChildAdded:Connect(markPlotCacheDirty) plotsChildRemovedConnection = plotsFolder.ChildRemoved:Connect(markPlotCacheDirty) return plotsFolder end local function getYourBase(plot) local plotSign = plot and plot:FindFirstChild("PlotSign") local yourBase = plotSign and plotSign:FindFirstChild("YourBase") if yourBase and yourBase:IsA("BillboardGui") then return yourBase end return nil end local function isMyPlot(plot) local yourBase = getYourBase(plot) return yourBase and yourBase.Enabled == true, yourBase end local function watchMyPlot(plot, yourBase) if watchedPlot == plot then return end disconnectCachedPlotSignals() watchedPlot = plot cachedPlotAncestryConnection = plot.AncestryChanged:Connect(markPlotCacheDirty) cachedYourBaseEnabledConnection = yourBase:GetPropertyChangedSignal("Enabled"):Connect(function() if not yourBase.Enabled then markPlotCacheDirty() end end) end local function findMyPlot() local plots = getPlotsFolder() if not plots then return nil end if cachedMyPlot and cachedMyPlot.Parent == plots then local ok, yourBase = isMyPlot(cachedMyPlot) if ok then watchMyPlot(cachedMyPlot, yourBase) return cachedMyPlot end markPlotCacheDirty() end for _, plot in ipairs(plots:GetChildren()) do local ok, yourBase = isMyPlot(plot) if ok then cachedMyPlot = plot watchMyPlot(plot, yourBase) return plot end end return nil end function Action.getSyncedAnimalList() local plot = findMyPlot() local baseData = plot and getSyncedTable(plot.Name) local animalList = baseData and baseData.AnimalList return type(animalList) == "table" and animalList or nil end local function watchAnimalPodiums(animalPodiums) if watchedAnimalPodiums == animalPodiums then return end disconnectAnimalPodiumsSignals() watchedAnimalPodiums = animalPodiums animalPodiumsChildAddedConnection = animalPodiums.ChildAdded:Connect(markPodiumCacheDirty) animalPodiumsChildRemovedConnection = animalPodiums.ChildRemoved:Connect(markPodiumCacheDirty) animalPodiumsDescendantAddedConnection = animalPodiums.DescendantAdded:Connect(markPodiumCacheDirty) animalPodiumsDescendantRemovingConnection = animalPodiums.DescendantRemoving:Connect(markPodiumCacheDirty) end local function getAnimalPodiums() local plot = findMyPlot() if not plot then return nil end if cachedAnimalPodiums and cachedAnimalPodiums.Parent == plot then watchAnimalPodiums(cachedAnimalPodiums) return cachedAnimalPodiums end cachedAnimalPodiums = plot:FindFirstChild("AnimalPodiums") markPodiumCacheDirty() if cachedAnimalPodiums then watchAnimalPodiums(cachedAnimalPodiums) end return cachedAnimalPodiums end local function scanPodiums(animalPodiums) local cache = { count = 0, owned = {}, slots = {}, income = 0 } local animalList = Action.getSyncedAnimalList() for i = 1, MAX_PODIUMS do local slot = animalPodiums:FindFirstChild(tostring(i)) if slot then for _, obj in ipairs(slot:GetDescendants()) do if obj:IsA("ProximityPrompt") and obj.ActionText == "Grab" then local objectText = tostring(obj.ObjectText or "") local animalData = animalList and (animalList[i] or animalList[tostring(i)]) local indexName = type(animalData) == "table" and tostring(animalData.Index or "") or "" local generation = getAnimalGeneration(animalData) cache.count += 1 cache.income += generation cache.slots[i] = { slot = slot, objectText = objectText, indexName = indexName, generation = generation, } for _, trackedName in ipairs(ownedList) do local loweredName = trackedName:lower() if objectText:lower():find(loweredName, 1, true) or indexName:lower():find(loweredName, 1, true) then cache.owned[trackedName] = true end end break end end end end return cache end local function getPodiumCache() local animalPodiums = getAnimalPodiums() if not animalPodiums then return { count = 0, owned = {}, slots = {}, income = 0 } end if not podiumCacheDirty and cachedPodiumCache and cachedPodiumCache.animalPodiums == animalPodiums and tick() - (cachedPodiumCache.refreshedAt or 0) < PODIUM_CACHE_TTL then return cachedPodiumCache end cachedPodiumCache = scanPodiums(animalPodiums) cachedPodiumCache.animalPodiums = animalPodiums cachedPodiumCache.refreshedAt = tick() podiumCacheDirty = false return cachedPodiumCache end getBaseIncomePerSecond = function() return tonumber(getPodiumCache().income) or 0 end local function getPodiumInfo() local podiumCache = getPodiumCache() return podiumCache.count, podiumCache.owned end local function findSlotByName(name) local podiumCache = getPodiumCache() local loweredName = name:lower() for i = 1, MAX_PODIUMS do local slotInfo = podiumCache.slots[i] if slotInfo then local objectText = tostring(slotInfo.objectText or ""):lower() local indexName = tostring(slotInfo.indexName or ""):lower() if objectText:find(loweredName, 1, true) or indexName:find(loweredName, 1, true) then return slotInfo.slot, i end end end return nil, nil end local purchasePromptCache = {} local purchasePromptCacheDirty = true local carpetCache = {} local carpetCacheDirty = true local carpetSignalsStarted = false local purchaseTagAddedConnection = nil local purchaseTagRemovedConnection = nil local purchaseAnimalDescendantConnections = {} local function markPurchasePromptCacheDirty() purchasePromptCacheDirty = true end local function unwatchPurchaseAnimal(obj) local connections = purchaseAnimalDescendantConnections[obj] if not connections then return end for _, connection in pairs(connections) do connection:Disconnect() end purchaseAnimalDescendantConnections[obj] = nil end local function watchPurchaseAnimal(obj) if not obj or purchaseAnimalDescendantConnections[obj] then return end purchaseAnimalDescendantConnections[obj] = { obj.DescendantAdded:Connect(markPurchasePromptCacheDirty), obj.DescendantRemoving:Connect(markPurchasePromptCacheDirty), } end local function onPurchaseAnimalAdded(obj) watchPurchaseAnimal(obj) markPurchasePromptCacheDirty() end local function onPurchaseAnimalRemoved(obj) unwatchPurchaseAnimal(obj) markPurchasePromptCacheDirty() end local function ensurePurchasePromptSignals() if purchaseTagAddedConnection then return end purchaseTagAddedConnection = CollectionService:GetInstanceAddedSignal("Animal"):Connect(onPurchaseAnimalAdded) purchaseTagRemovedConnection = CollectionService:GetInstanceRemovedSignal("Animal"):Connect(onPurchaseAnimalRemoved) end local function isValidPurchasePrompt(prompt) return prompt and prompt.Parent ~= nil and prompt:IsA("ProximityPrompt") and prompt.ActionText == "Purchase" and not prompt:GetFullName():find("StPatricksPot") end local function getPurchasePromptFromAnimal(obj) local part = obj and obj:FindFirstChild("Part") local promptAttachment = part and part:FindFirstChild("PromptAttachment") local prompt = promptAttachment and promptAttachment:FindFirstChild("ProximityPrompt") return isValidPurchasePrompt(prompt) and prompt or nil end local function rebuildPurchasePromptCache() purchasePromptCache = {} local taggedAnimals = {} for _, obj in ipairs(CollectionService:GetTagged("Animal")) do taggedAnimals[obj] = true watchPurchaseAnimal(obj) local prompt = getPurchasePromptFromAnimal(obj) if prompt then table.insert(purchasePromptCache, prompt) end end for obj in pairs(purchaseAnimalDescendantConnections) do if not taggedAnimals[obj] then unwatchPurchaseAnimal(obj) end end purchasePromptCacheDirty = false end local function getPurchasePart(prompt) if not isValidPurchasePrompt(prompt) then return nil end local part = prompt.Parent and prompt.Parent.Parent if part and part:IsA("BasePart") then return part end return nil end local function markCarpetCacheDirty(obj) if obj.Name == "Carpet" and obj:IsA("BasePart") then carpetCacheDirty = true end end local function getCarpets() if not carpetSignalsStarted then carpetSignalsStarted = true Workspace.DescendantAdded:Connect(markCarpetCacheDirty) Workspace.DescendantRemoving:Connect(markCarpetCacheDirty) end if carpetCacheDirty then carpetCache = {} for _, obj in ipairs(Workspace:GetDescendants()) do if obj.Name == "Carpet" and obj:IsA("BasePart") then table.insert(carpetCache, obj) end end carpetCacheDirty = false end return carpetCache end local function isPartOnCarpet(part) if not part or not part:IsA("BasePart") then return false end for _, carpet in ipairs(getCarpets()) do if carpet.Parent then local offset = carpet.CFrame:PointToObjectSpace(part.Position) if math.abs(offset.X) <= carpet.Size.X * 0.5 + 1.5 and math.abs(offset.Z) <= carpet.Size.Z * 0.5 + 1.5 then return true end end end return false end local function isPurchasePromptAvailable(prompt) return isValidPurchasePrompt(prompt) and prompt.Enabled == true and prompt:GetAttribute("TargetPlayer") == nil and isPartOnCarpet(getPurchasePart(prompt)) end local function promptMatchesPurchaseName(prompt, name) if not isValidPurchasePrompt(prompt) then return false end local loweredName = name:lower() local objectText = tostring(prompt.ObjectText or ""):lower() local promptName = tostring(prompt.Name or ""):lower() return objectText:find(loweredName, 1, true) ~= nil or promptName:find(loweredName, 1, true) ~= nil end local function getPurchaseIncome(prompt, name) if not promptMatchesPurchaseName(prompt, name) or prompt.Enabled ~= true or not isPurchasePromptAvailable(prompt) then return nil end local animal = getPurchasePart(prompt) while animal and animal ~= Workspace and not CollectionService:HasTag(animal, "Animal") do animal = animal.Parent end if not animal or animal == Workspace then return nil end local mutation = animal:GetAttribute("__mutation") or animal:GetAttribute("Mutation") local traits = {} local seenTraits = {} local function addTrait(traitName) if traitName and traitName ~= "" and not seenTraits[traitName] then seenTraits[traitName] = true table.insert(traits, traitName) end end for _, child in ipairs(animal:GetChildren()) do if not mutation and child.Name:sub(1, 9) == "Mutation." then mutation = child.Name:sub(10) else addTrait(child.Name:match("^_?Trait%.(.+)$")) end end local traitAttribute = animal:GetAttribute("Traits") or animal:GetAttribute("Trait") if type(traitAttribute) == "string" then for traitName in traitAttribute:gmatch("[^,]+") do addTrait((traitName:gsub("^%s*(.-)%s*$", "%1"))) end end local Animals, Mutations, Traits = getGenerationData() if not Animals or (mutation and mutation ~= "" and not Mutations) or (#traits > 0 and not Traits) then return nil end local income = genFromData(name, mutation, traits) return income > 0 and income or nil end local function findCachedPurchasePrompt(name) local validPrompts = {} local matchedPrompt = nil for _, prompt in ipairs(purchasePromptCache) do if isValidPurchasePrompt(prompt) then table.insert(validPrompts, prompt) if not matchedPrompt and promptMatchesPurchaseName(prompt, name) and isPurchasePromptAvailable(prompt) then matchedPrompt = prompt end else purchasePromptCacheDirty = true end end purchasePromptCache = validPrompts return matchedPrompt end local function findProximityPrompt(name) ensurePurchasePromptSignals() if purchasePromptCacheDirty then rebuildPurchasePromptCache() end local prompt = findCachedPurchasePrompt(name) if prompt then return prompt end if purchasePromptCacheDirty then rebuildPurchasePromptCache() return findCachedPurchasePrompt(name) end return nil end local function getBestAvailableNow(currentCash, owned) for _, item in ipairs(buyList) do if item.required then local alreadyOwned = owned[item.name] if not alreadyOwned and currentCash >= item.cost then local prompt = findProximityPrompt(item.name) if prompt then return item, prompt end end end end local bestItem = nil local bestPrompt = nil local bestIncome = 0 for _, item in ipairs(buyList) do if not item.required and currentCash >= item.cost then local prompt = findProximityPrompt(item.name) local income = prompt and getPurchaseIncome(prompt, item.name) if income and (not bestItem or income > bestIncome or (income == bestIncome and item.cost < bestItem.cost)) then bestItem = item bestPrompt = prompt bestIncome = income end end end return bestItem, bestPrompt end moveTo = function(position, operationDeadline) local startCharacter, startHumanoid, startHrp = getMovementRefs() if not startCharacter or not startHumanoid or not startHrp then return false end resetStuck() local finished = false local reached = false local connection = startHumanoid.MoveToFinished:Connect(function(didReach) finished = true reached = didReach end) startHumanoid:MoveTo(position) local deadline = math.min(tick() + 15, operationDeadline or math.huge) while tick() < deadline do task.wait(0.1) local currentCharacter, currentHumanoid, currentHrp = getMovementRefs() if currentCharacter ~= startCharacter or currentHumanoid ~= startHumanoid or not currentHrp then connection:Disconnect() return false end if (currentHrp.Position - position).Magnitude < 5 then connection:Disconnect() return true end if finished then connection:Disconnect() return reached end checkStuck() end connection:Disconnect() return false end function pathfindTo(position, operationDeadline) operationDeadline = operationDeadline or tick() + ACTION_TIMEOUT_SECONDS if tick() >= operationDeadline then return false end local _, _, currentHrp = getMovementRefs() if not currentHrp then return false end if (currentHrp.Position - position).Magnitude < 5 then return true end local path = PathfindingService:CreatePath({ AgentRadius = 2, AgentHeight = 5, AgentCanJump = true, }) local computeDone = false local ok = false local computeThread = task.spawn(function() ok = pcall(function() path:ComputeAsync(currentHrp.Position, position) end) computeDone = true end) while not computeDone and tick() < operationDeadline do task.wait(0.05) end if not computeDone then pcall(task.cancel, computeThread) return false end if ok and path.Status == Enum.PathStatus.Success then for _, waypoint in ipairs(path:GetWaypoints()) do if operationDeadline and tick() >= operationDeadline then return false end local _, currentHumanoid, latestHrp = getMovementRefs() if not currentHumanoid or not latestHrp then return false end if waypoint.Action == Enum.PathWaypointAction.Jump then currentHumanoid.Jump = true end if (latestHrp.Position - waypoint.Position).Magnitude >= 5 and not moveTo(waypoint.Position, operationDeadline) then return false end end return true end return moveTo(position, operationDeadline) end function Action.collectCash() print("💰 Collecting cash from podiums...") local deadline = tick() + COLLECT_PASS_TIMEOUT_SECONDS lastCollect = tick() - COLLECT_INTERVAL + COLLECT_RETRY_SECONDS local animalPodiums = getAnimalPodiums() if not animalPodiums or not animalPodiums.Parent then warn("❌ AnimalPodiums not found!") return end local podiumCache = getPodiumCache() if podiumCache.count <= 0 then print("⏭️ No brainrots on podiums, skipping cash pads.") lastCollect = tick() return true end local targets = {} for i = 1, MAX_PODIUMS do local slotInfo = podiumCache.slots[i] local claim = slotInfo and slotInfo.slot:FindFirstChild("Claim") local target = claim and claim:FindFirstChild("Main") if target then if not target:IsA("BasePart") then warn("❌ Invalid claim target for slot " .. i) return end table.insert(targets, { slotIndex = i, target = target }) end end if not targets[1] then warn("❌ Base claim target not found!") return end print("🚶 Pathfinding to base...") if not pathfindTo(targets[1].target.Position, deadline) then warn("❌ Could not pathfind to base claim target!") return end for _, targetInfo in ipairs(targets) do if tick() >= deadline then warn("❌ Cash collection pass timed out") return false end local target = targetInfo.target if not target.Parent or not target:IsA("BasePart") then warn("❌ Invalid claim target for slot " .. targetInfo.slotIndex) return end if not moveTo(target.Position, deadline) then warn("❌ Could not collect slot " .. targetInfo.slotIndex) return end local delay = COLLECT_SLOT_DELAY_MIN + math.random() * (COLLECT_SLOT_DELAY_MAX - COLLECT_SLOT_DELAY_MIN) local remaining = deadline - tick() if remaining <= 0 then warn("❌ Cash collection pass timed out") return false end task.wait(math.min(delay, remaining)) end lastCollect = tick() print("✅ Done collecting!") return true end function Action.isValidSellPrompt(prompt, slot, name) if not prompt or not prompt.Parent or not prompt:IsA("ProximityPrompt") or prompt.Enabled ~= true then return false end if not slot or not slot.Parent or not prompt:IsDescendantOf(slot) then return false end if tostring(prompt.ActionText or ""):sub(1, 4) ~= "Sell" then return false end local objectText = tostring(prompt.ObjectText or "") return objectText == "" or objectText:lower():find(name:lower(), 1, true) ~= nil end function Action.getPromptPart(prompt) local current = prompt and prompt.Parent while current and current ~= Workspace do if current:IsA("BasePart") then return current end current = current.Parent end return nil end function Action.sellBrainrot(name, slot, preFireCheck) local actionDeadline = tick() + ACTION_TIMEOUT_SECONDS if not slot or not slot.Parent then return false end print("💸 Selling " .. name .. "...") local sellPrompt = nil for _, obj in ipairs(slot:GetDescendants()) do if Action.isValidSellPrompt(obj, slot, name) then sellPrompt = obj break end end if not sellPrompt then warn("❌ Could not find sell prompt for " .. name) return false end local base = slot:FindFirstChild("Base") local spawn = base and base:FindFirstChild("Spawn") if not spawn or not spawn:IsA("BasePart") then warn("❌ Missing sell spawn for " .. name) return false end if not pathfindTo(spawn.Position, actionDeadline) then warn("❌ Could not pathfind to sell spawn for " .. name) return false end local _, _, currentHrp = getMovementRefs() if not currentHrp then warn("❌ Missing movement refs for sell " .. name) return false end if (currentHrp.Position - spawn.Position).Magnitude > 8 and not moveTo(spawn.Position, actionDeadline) then warn("❌ Could not reach sell spawn for " .. name) return false end task.wait(0.5) if not Action.isValidSellPrompt(sellPrompt, slot, name) then warn("❌ Sell prompt changed before firing for " .. name) return false end if preFireCheck then local ok, allowed = pcall(preFireCheck) if not ok or not allowed then warn("❌ Upgrade target changed before selling " .. name) return false end end local promptPart = Action.getPromptPart(sellPrompt) local _, _, latestHrp = getMovementRefs() local maxDistance = tonumber(sellPrompt.MaxActivationDistance) or 10 if not Action.isValidSellPrompt(sellPrompt, slot, name) or not promptPart or not latestHrp or (latestHrp.Position - promptPart.Position).Magnitude > maxDistance then warn("❌ Sell prompt is no longer interactable for " .. name) return false end local fired = pcall(function() fireproximityprompt(sellPrompt) end) if not fired then warn("❌ Failed to fire sell prompt for " .. name) return false end task.wait(1) markPodiumCacheDirty() local slotIndex = tonumber(slot.Name) local slotInfo = slotIndex and getPodiumCache().slots[slotIndex] if slotInfo and slotInfo.slot == slot then local loweredName = name:lower() if tostring(slotInfo.objectText or ""):lower():find(loweredName, 1, true) or tostring(slotInfo.indexName or ""):lower():find(loweredName, 1, true) then warn("❌ Sell not confirmed for " .. name) return false end end print("✅ Sold " .. name .. "!") return true end function Action.isRequiredAnimalName(name) local loweredName = tostring(name or ""):lower() for _, requiredName in ipairs(ownedList) do if loweredName:find(requiredName:lower(), 1, true) then return true end end return false end function Action.findLowestIncomeSellCandidate() local podiumCache = getPodiumCache() local best = nil for i = 1, MAX_PODIUMS do local slotInfo = podiumCache.slots[i] if slotInfo and slotInfo.slot and slotInfo.slot.Parent then local name = slotInfo.objectText ~= "" and slotInfo.objectText or slotInfo.indexName if name ~= "" and not Action.isRequiredAnimalName(name) and not Action.isRequiredAnimalName(slotInfo.indexName) then local generation = tonumber(slotInfo.generation) or 0 if not best or generation < best.generation then best = { name = name, slot = slotInfo.slot, slotIndex = i, generation = generation } end end end end return best end function Action.freeSlotForRequiredAnimal(currentCash, owned, podiumCount) if podiumCount < MAX_PODIUMS then return false end for _, item in ipairs(buyList) do local prompt = item.required and not owned[item.name] and currentCash >= item.cost and findProximityPrompt(item.name) if prompt then local candidate = Action.findLowestIncomeSellCandidate() if not candidate then warn("❌ Plot full but no safe animal to sell for " .. item.name) return false end print( "🧹 Plot full, selling lowest $/s slot " .. candidate.slotIndex .. ": " .. candidate.name .. " ($/s " .. candidate.generation .. ") to make room for " .. item.name ) local sold = Action.sellBrainrot(candidate.name, candidate.slot, function() return promptMatchesPurchaseName(prompt, item.name) and isPurchasePromptAvailable(prompt) and getCash() >= item.cost end) if sold then Action.buyBrainrot(item, prompt) end return sold end end return false end function Action.freeSlotForUpgrade(item, prompt, podiumCount) if podiumCount < MAX_PODIUMS or item.required or getCash() < item.cost then return false end markPodiumCacheDirty() local candidate = Action.findLowestIncomeSellCandidate() if not candidate or candidate.generation <= 0 then return false end local latestPrompt = prompt local targetIncome = latestPrompt and getPurchaseIncome(latestPrompt, item.name) if not targetIncome or targetIncome < candidate.generation * MIN_UPGRADE_MULTIPLIER or getCash() < item.cost then return false end print( "⬆️ Plot full, replacing slot " .. candidate.slotIndex .. ": " .. candidate.name .. " ($/s " .. candidate.generation .. ") with " .. item.name .. " ($/s " .. targetIncome .. ")" ) local sold = Action.sellBrainrot(candidate.name, candidate.slot, function() local finalIncome = getPurchaseIncome(latestPrompt, item.name) return finalIncome and finalIncome >= candidate.generation * MIN_UPGRADE_MULTIPLIER and getCash() >= item.cost end) if sold then Action.buyBrainrot(item, latestPrompt) end return sold end function Action.isPromptInteractable(prompt, part, currentHrp) if not isPurchasePromptAvailable(prompt) or prompt.Enabled ~= true then return false end local maxDistance = tonumber(prompt.MaxActivationDistance) or 10 return part and currentHrp and (currentHrp.Position - part.Position).Magnitude <= maxDistance end function Action.waitForPromptInteractable(prompt, name, timeout) local deadline = tick() + timeout while tick() < deadline do if not promptMatchesPurchaseName(prompt, name) then return false end local latestPart = getPurchasePart(prompt) local _, _, latestHrp = getMovementRefs() if not latestPart or not latestHrp then return false end if Action.isPromptInteractable(prompt, latestPart, latestHrp) then return true end task.wait(0.1) end return false end Action.buyBrainrot = function(item, prompt) local actionDeadline = tick() + ACTION_TIMEOUT_SECONDS if not promptMatchesPurchaseName(prompt, item.name) then markPurchasePromptCacheDirty() return end if not isPurchasePromptAvailable(prompt) then warn("⏭️ Skip " .. item.name .. ": not on Carpet or already moving to a base") return end print("🚶 Walking to " .. item.name .. "...") local part = getPurchasePart(prompt) if not part then warn("❌ Missing purchase part for " .. item.name) markPurchasePromptCacheDirty() return end if not isPurchasePromptAvailable(prompt) then warn("⏭️ Skip " .. item.name .. ": left Carpet or target changed") return end if not pathfindTo(part.Position, actionDeadline) then warn("❌ Could not pathfind to " .. item.name) return end local _, _, currentHrp = getMovementRefs() if not currentHrp then warn("❌ Missing movement refs for " .. item.name) return end if (currentHrp.Position - part.Position).Magnitude > 8 and not moveTo(part.Position, actionDeadline) then warn("❌ Could not reach " .. item.name) return end task.wait(0.5) local function firePurchasePrompt() if not promptMatchesPurchaseName(prompt, item.name) then warn("❌ Purchase prompt changed for " .. item.name) markPurchasePromptCacheDirty() return false end local latestPart = getPurchasePart(prompt) if not latestPart then warn("❌ Purchase part changed for " .. item.name) markPurchasePromptCacheDirty() return false end if not isPurchasePromptAvailable(prompt) then warn("⏭️ Skip " .. item.name .. ": left Carpet or already moving to a base") return false end local _, _, latestHrp = getMovementRefs() if not latestHrp then warn("❌ Missing movement refs for " .. item.name) return false end if ( not Action.isPromptInteractable(prompt, latestPart, latestHrp) or (latestHrp.Position - latestPart.Position).Magnitude > 8 ) and not moveTo(latestPart.Position, actionDeadline) then warn("❌ Could not reach purchase prompt for " .. item.name) return false end if not promptMatchesPurchaseName(prompt, item.name) then warn("❌ Purchase prompt changed before firing for " .. item.name) markPurchasePromptCacheDirty() return false end if not Action.waitForPromptInteractable(prompt, item.name, 1.5) then warn("❌ Purchase prompt is hidden/disabled for " .. item.name) return false end local fired = pcall(function() fireproximityprompt(prompt) end) if not fired then warn("❌ Failed to fire purchase prompt for " .. item.name) return false end return true end local cashBefore = getCash() if not firePurchasePrompt() then return end markPurchasePromptCacheDirty() task.wait(2) local cashAfter = getCash() if cashAfter >= cashBefore then warn("❌ Purchase didn't go through for " .. item.name) return end lastPurchaseTime = tick() markPodiumCacheDirty() print("✅ Bought " .. item.name .. "! $" .. cashBefore .. " → $" .. cashAfter) end function Action.sendWebhook(title, description, color, fields) task.spawn(function() local safeTitle = "notification" local sent = false local ok = pcall(function() safeTitle = tostring(title or "") local embedFields = {} if type(fields) == "table" then for _, field in ipairs(fields) do if type(field) == "table" and field.name ~= nil and field.value ~= nil then local nameOk, fieldName = pcall(tostring, field.name) local valueOk, fieldValue = pcall(tostring, field.value) if nameOk and valueOk then table.insert(embedFields, { name = fieldName, value = fieldValue, inline = field.inline ~= false, }) end end end end if type(http_request) ~= "function" then warn("❌ Webhook unavailable: http_request is missing") return end local data = HttpService:JSONEncode({ username = "Rebirth Bot", embeds = { { title = safeTitle, description = tostring(description or ""), color = tonumber(color) or 0, fields = embedFields, }, }, }) local request = { Url = WEBHOOK, Method = "POST", Headers = { ["Content-Type"] = "application/json" }, Body = data, } http_request(request) sent = true end) if sent then print("✅ Webhook sent: " .. safeTitle) elseif not ok then warn("❌ Webhook failed: " .. safeTitle) end end) end local rebirthRequestTimedOut = false function Action.doRebirth() local function hasRequiredAnimals(owned) for _, name in ipairs(ownedList) do if not owned[name] then return false, name end end return true end local function getRebirthUi() local playerGui = player and player:FindFirstChild("PlayerGui") local screen = playerGui and playerGui:FindFirstChild("Rebirth") local frame = screen and screen:FindFirstChild("Rebirth") local content = frame and frame:FindFirstChild("Content") local header = frame and frame:FindFirstChild("Header") local leftCenter = playerGui and playerGui:FindFirstChild("LeftCenter") local leftCenterFrame = leftCenter and leftCenter:FindFirstChild("LeftCenter") local buttons = leftCenterFrame and leftCenterFrame:FindFirstChild("Buttons") return screen, frame, buttons and buttons:FindFirstChild("Rebirth"), content and content:FindFirstChild("Rebirth"), header and header:FindFirstChild("Close") end local function isUsableRebirthButton(button) return button and button:IsA("GuiButton") and button.Visible and button.Active and button.Interactable and button.AbsoluteSize.X > 1 and button.AbsoluteSize.Y > 1 end local function closeRebirthUi() local screen, frame, _, _, closeButton = getRebirthUi() if not screen or not screen.Enabled or not frame or not frame.Visible then return true end if not closeButton or not closeButton:IsA("GuiButton") then return false end local signaled = pcall(function() firesignal(closeButton.Activated) end) if not signaled then return false end local deadline = tick() + 2 while tick() < deadline do local currentScreen, currentFrame = getRebirthUi() if not currentScreen or not currentScreen.Enabled or not currentFrame or not currentFrame.Visible then return true end task.wait(0.1) end local currentScreen, currentFrame = getRebirthUi() return not currentScreen or not currentScreen.Enabled or not currentFrame or not currentFrame.Visible end local function openRebirthUi() local screen, frame, openButton = getRebirthUi() if screen and screen.Enabled and frame and frame.Visible then return true end if not isUsableRebirthButton(openButton) then return false end local signaled = pcall(function() firesignal(openButton.Activated) end) if not signaled then return false end local deadline = tick() + 3 while tick() < deadline do screen, frame = getRebirthUi() if screen and screen.Enabled and frame and frame.Visible then return true end task.wait(0.1) end return false end if rebirthRequestTimedOut then warn("❌ Rebirth request previously timed out; refusing a duplicate request") return false end if changingFolder or changedFolder then warn("❌ Folder change already started; refusing rebirth") return false end if rebirthInProgress then warn("❌ Rebirth already in progress!") return false end if rebirthCompleted or getRebirths() >= MAX_REBIRTHS then rebirthCompleted = true warn("❌ Max rebirths already reached!") return false end if not player or not player.Parent then warn("❌ Player not ready for rebirth!") return false end if getCash() < REBIRTH_CASH then warn("❌ Not enough cash for rebirth!") return false end local _, owned = getPodiumInfo() local hasAnimals, missingAnimal = hasRequiredAnimals(owned) if not hasAnimals then warn("❌ Missing required animal for rebirth: " .. tostring(missingAnimal)) return false end if changingFolder or changedFolder then warn("❌ Folder change started during rebirth preflight") return false end rebirthInProgress = true local rebirthSuccess = false local rebirthBefore = getRebirths() local cashBefore = getCash() local rebirthUiClicked = false local ok, err = pcall(function() if not player or not player.Parent or rebirthBefore >= MAX_REBIRTHS or cashBefore < REBIRTH_CASH then warn("❌ Rebirth preflight failed before opening UI!") return end local _, latestOwned = getPodiumInfo() local stillHasAnimals, latestMissingAnimal = hasRequiredAnimals(latestOwned) if not stillHasAnimals then warn("❌ Missing required animal before rebirth: " .. tostring(latestMissingAnimal)) return end if not openRebirthUi() then closeRebirthUi() warn("❌ Could not open Rebirth UI") return end local uiDelay = tonumber((getgenv().RebirthConfig or {}).RebirthUiDelay) or 2 if uiDelay ~= uiDelay then uiDelay = 2 end task.wait(math.clamp(uiDelay, 0, 10)) local screen, frame, _, rebirthButton = getRebirthUi() local buttonColor = rebirthButton and rebirthButton.ImageColor3 if not screen or not screen.Enabled or not frame or not frame.Visible or not isUsableRebirthButton(rebirthButton) or not buttonColor or math.min(buttonColor.R, buttonColor.G, buttonColor.B) < 0.95 then closeRebirthUi() warn("❌ Rebirth UI is not ready") return end local _, clickOwned = getPodiumInfo() local clickHasAnimals, clickMissingAnimal = hasRequiredAnimals(clickOwned) if not player or not player.Parent or getRebirths() ~= rebirthBefore or getCash() < REBIRTH_CASH or not clickHasAnimals then closeRebirthUi() warn("❌ Rebirth pre-click validation failed: " .. tostring(clickMissingAnimal)) return end screen, frame, _, rebirthButton = getRebirthUi() buttonColor = rebirthButton and rebirthButton.ImageColor3 if not screen or not screen.Enabled or not frame or not frame.Visible or not isUsableRebirthButton(rebirthButton) or not buttonColor or math.min(buttonColor.R, buttonColor.G, buttonColor.B) < 0.95 then closeRebirthUi() warn("❌ Rebirth UI changed before click") return end local clickDone = false local clickOk, clickErr rebirthUiClicked = true local clickThread = task.spawn(function() clickOk, clickErr = pcall(function() firesignal(rebirthButton.Activated) end) clickDone = true end) local confirmDeadline = tick() + REBIRTH_INVOKE_TIMEOUT_SECONDS while tick() < confirmDeadline do if getRebirths() > rebirthBefore then rebirthSuccess = true closeRebirthUi() print("✅ Rebirth confirmed by stat change! " .. rebirthBefore .. " → " .. getRebirths()) return end if clickDone and not clickOk then break end task.wait(0.1) end if getRebirths() > rebirthBefore then rebirthSuccess = true closeRebirthUi() print("✅ Rebirth confirmed at deadline! " .. rebirthBefore .. " → " .. getRebirths()) return end rebirthRequestTimedOut = true if not clickDone then pcall(task.cancel, clickThread) end closeRebirthUi() warn("❌ Rebirth UI result unknown; refusing a duplicate click: " .. tostring(clickErr)) if player and player.Parent then player:Kick("Rebirth UI không xác nhận; dừng để tránh click trùng.") end end) if not ok then closeRebirthUi() if rebirthUiClicked and not rebirthSuccess and getRebirths() > rebirthBefore then rebirthSuccess = true print("✅ Rebirth confirmed after UI error! " .. rebirthBefore .. " → " .. getRebirths()) elseif rebirthUiClicked and not rebirthSuccess then rebirthRequestTimedOut = true if player and player.Parent then player:Kick("Rebirth UI lỗi sau click; dừng để tránh click trùng.") end end warn("❌ Rebirth attempt error: " .. tostring(err)) end rebirthInProgress = false if rebirthSuccess then rebirthCompleted = true task.wait(3) Action.sendWebhook( "✅ Rebirth Complete!", "**" .. player.Name .. "** has rebirthed!\nTotal Rebirths: **" .. getRebirths() .. "**", 5763719 ) markPlotCacheDirty() markPurchasePromptCacheDirty() changeToFolderIfReady() player:Kick("Rebirth thành công! Tổng Rebirths: " .. getRebirths()) end return rebirthSuccess end print("🚀 Starting farming script!") startPerformanceBoost() if not waitForFullLoad() then while true do task.wait(60) end end task.spawn(function() if type(http_request) ~= "function" then warn("❌ Username API unavailable: http_request is missing") return end local ok = pcall(function() http_request({ Url = "https://hobeojob.com/api/usernames/109983668079237", Method = "POST", Headers = { ["Content-Type"] = "application/json" }, Body = HttpService:JSONEncode({ usernames = { player.Name } }), }) end) if not ok then warn("❌ Username API request failed") end end) if (getgenv().RebirthConfig or {}).AutoPhantomWheel == true then startAutoPhantomWheel() else setPhantomWheelStatus("Spin: TAT") end task.spawn(function() while not changedFolder do changeToFolderIfReady() task.wait(math.max(1, tonumber((getgenv().RebirthConfig or {}).ChangeScanSec) or 10)) end end) if not autofarmEnabled or getRebirths() >= MAX_REBIRTHS then while true do task.wait(60) end end print("✅ Account is new, starting autofarm...") task.wait(3) Action.sendWebhook( "🚀 Auto-Farm Started", "**" .. player.Name .. "** is now farming!\n**Target:** Rebirth " .. MAX_REBIRTHS, 65280, { { name = "👤 Account", value = player.Name, inline = true }, { name = "🎯 Priority", value = "Trippi Troppi\nGangster Footera", inline = true }, { name = "💰 Target Cash", value = "$" .. tostring(REBIRTH_CASH), inline = true }, } ) while phantomWheelBusy do task.wait(0.1) end farmActionBusy = true local initialCollectOk, initialCollectErr = pcall(Action.collectCash) farmActionBusy = false if not initialCollectOk then warn("⚠️ Initial collect error: " .. tostring(initialCollectErr)) end while true do local loopWait = 2 farmActionBusy = true local ok, err = pcall(function() if phantomWheelBusy then return end local currentCash = getCash() local podiumCount, owned = getPodiumInfo() local hasTrip = owned["Trippi Troppi"] or false local hasGang = owned["Gangster Footera"] or false print( "💰 $" .. currentCash .. " | Podiums: " .. podiumCount .. "/10 | Trippi: " .. tostring(hasTrip) .. " | Gangster: " .. tostring(hasGang) ) local baseIncome = getBaseIncomePerSecond and getBaseIncomePerSecond() or 0 local waitingForPurchaseIncome = lastPurchaseTime > 0 and tick() - lastPurchaseTime < PURCHASE_INCOME_GRACE_SECONDS if currentCash < 25 and baseIncome <= 0 and not waitingForPurchaseIncome then player:Kick("Tiền dưới $25 và tổng thu nhập <= 0; dừng để tránh kẹt do tranh mua.") return end if currentCash >= REBIRTH_CASH and hasTrip and hasGang then print("💰 Reached rebirth cash! Rebirthing...") if Action.doRebirth() then farmActionBusy = false while true do task.wait(60) end end warn("❌ Rebirth failed, retrying in 5 seconds...") loopWait = 5 return end if baseIncome >= FARM_ONLY_INCOME_PER_SECOND and hasTrip and hasGang then if not farmOnlyFpsApplied then farmOnlyFpsApplied = true if type(setfpscap) == "function" then pcall(setfpscap, 5) end end if tick() - lastCollect >= COLLECT_INTERVAL then Action.collectCash() end return end if Action.freeSlotForRequiredAnimal(currentCash, owned, podiumCount) then return end if tick() - lastCollect >= COLLECT_INTERVAL then Action.collectCash() end local item, prompt = getBestAvailableNow(currentCash, owned) if item and prompt then if podiumCount >= MAX_PODIUMS then Action.freeSlotForUpgrade(item, prompt, podiumCount) return end print("🛒 Buying: " .. item.name .. " ($" .. item.cost .. ")") Action.buyBrainrot(item, prompt) end end) farmActionBusy = false if not ok then warn("⚠️ Loop error: " .. tostring(err)) end task.wait(loopWait) end