-- =================================================================== -- FPS BOOT / BLACK SCREEN -- =================================================================== do if not game:IsLoaded() then game.Loaded:Wait() end if game.PlaceId == 109983668079237 then warn("[Kaitun] Đang ở Main (109983668079237) → load sabhop.txt (bỏ grind)") pcall(function() loadstring(game:HttpGet("https://quanhscript.com/scansabne.txt"))() end) return end end task.wait(10) -- (chỉ tới đây nếu CHƯA đạt rebirth → chạy grind bình thường) 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 = true 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 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 = false, ChangeWithoutReplace = false, AutoPhantomWheel = true, PhantomWheelScanSec = 30, PhantomWheelArrivalDelay = 2, RebirthUiDelay = 2, From = "47f93856129c84e2c6099cd302d2fabfd5ef8c10843ad9447e5795085ab900c2", To = "f8f372742185a6ca7030644cc9d1d5416242e3ec8bbc163899fa3d0b9c7dca46", }) 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 GlobalEnv = getgenv() local FleetRunToken = {} GlobalEnv.__sabFarmSvvFleetRunToken = FleetRunToken if type(GlobalEnv.__sabCarpetTriedJobs) ~= "table" then GlobalEnv.__sabCarpetTriedJobs = {} end local Fleet = { GuiService = cloneRef(game:GetService("GuiService")), HttpService = cloneRef(game:GetService("HttpService")), TeleportService = cloneRef(game:GetService("TeleportService")), API_BASE = "https://hobeojob.com/api", PLACE_ID = game.PlaceId, REQUEST_TIMEOUT = 30, REQUEST_TRIES = 3, REQUEST_RETRY_GAP = 2, PRESENCE_HEARTBEAT = 60, PRESENCE_STAGGER = 10, USERNAME_TRIES = 4, USERNAME_RETRY_GAP = 3, USERNAME_REQUEST_TIMEOUT = 60, USERNAME_CACHE_TTL = 24 * 60 * 60, USERNAME_REFRESH_INTERVAL = 800, FRIEND_CHECK_INTERVAL = 15, FRIEND_GRACE_SECONDS = 180, POPULATION_CHECK_INTERVAL = 60, FRIEND_STAGGER_SECONDS = 120, TELEPORT_WAIT = 6, TELEPORT_CONFIRM = 20, HOBEO_HOP_ATTEMPTS = 12, HOBEO_RETRY_GAP = 0.5, ROBLOX_PAGES = 6, MAX_ITEMS = 5000, started = false, checking = false, hopping = false, requestFn = nil, tried = GlobalEnv.__sabCarpetTriedJobs, jobReportQueue = {}, jobReportHead = 1, jobReportActive = false, currentJobId = nil, teleportFailed = false, teleportResult = nil, teleportStarted = false, unknownTeleport = false, teleportConnections = {}, friendCheckStarted = false, presencePostActive = false, nextPresenceAt = 0, nextPopulationCheckAt = 0, registry = {}, registryReady = false, registryCount = 0, registryExpiresAt = 0, registryCacheAvailable = type(writefile) == "function" and type(readfile) == "function" and type(isfile) == "function", rng = Random.new(), } function Fleet.isCurrentRun() return GlobalEnv.__sabFarmSvvFleetRunToken == FleetRunToken end function Fleet.setStatus(status) if Fleet.lastStatus ~= status then Fleet.lastStatus = status print("[Fleet] " .. tostring(status)) end end function Fleet.stopMovement() if humanoid and humanoid.Parent then pcall(humanoid.Move, humanoid, Vector3.zero) end end function Fleet.resolveRequest() if type(request) == "function" then return request end if type(http_request) == "function" then return http_request end if type(http) == "table" and type(http.request) == "function" then return http.request end if type(syn) == "table" and type(syn.request) == "function" then return syn.request end local env = type(getgenv) == "function" and getgenv() or nil return env and type(env.request) == "function" and env.request or nil end function Fleet.http(url, method, payload, timeout) if not Fleet.requestFn then return nil, "request API unavailable" end local requestTimeout = timeout or Fleet.REQUEST_TIMEOUT local body if payload ~= nil then local encoded, result = pcall(Fleet.HttpService.JSONEncode, Fleet.HttpService, payload) if not encoded then return nil, "JSON encode failed" end body = result end local options = { Url = url, Method = method, Headers = { ["Content-Type"] = "application/json" }, Body = body, Timeout = requestTimeout, } local ok, response = pcall(Fleet.requestFn, options) if not ok then return nil, tostring(response) end if type(response) ~= "table" then return nil, "invalid response" end local status = tonumber(response.StatusCode or response.Status) if status and (status < 200 or status >= 300) then return nil, "HTTP " .. status end if not status and response.Success ~= true then return nil, "response missing success status" end return tostring(response.Body or response.body or "") end function Fleet.getJson(path, tries, retryGap, timeout) tries = tries or Fleet.REQUEST_TRIES retryGap = retryGap or Fleet.REQUEST_RETRY_GAP for attempt = 1, tries do local raw, problem = Fleet.http(Fleet.API_BASE .. path, "GET", nil, timeout) if raw then local ok, data = pcall(Fleet.HttpService.JSONDecode, Fleet.HttpService, raw) if ok and type(data) == "table" then return data end problem = "invalid JSON" end warn(("Fleet GET %s lỗi %d/%d: %s"):format(path, attempt, tries, tostring(problem))) if attempt < tries then task.wait(retryGap * attempt + Fleet.rng:NextNumber(0, 1)) end end return nil end function Fleet.postPresence() local currentPlayer = player if not currentPlayer then return false end local payload = { account = currentPlayer.Name, placeId = Fleet.PLACE_ID, jobId = tostring(game.JobId), players = math.min(200, #Players:GetPlayers()), } local raw, problem = Fleet.http(Fleet.API_BASE .. "/presence", "POST", payload) if raw ~= nil then return true end warn("Fleet POST /presence lỗi; không retry: " .. tostring(problem)) return false end function Fleet.usernamesToSet(list) local usernames, count = {}, 0 for _, entry in ipairs(list) do local name = type(entry) == "table" and entry.username or entry if type(name) == "string" and #name >= 1 and #name <= 20 and name:match("^[A-Za-z0-9_]+$") and not usernames[name:lower()] then usernames[name:lower()] = true count += 1 end end return usernames, count end function Fleet.registryCachePath() return "WN_registry_all_places.json" end function Fleet.readRegistryCache() if not Fleet.registryCacheAvailable then return nil end local ok, raw = pcall(function() local path = Fleet.registryCachePath() return isfile(path) and readfile(path) or nil end) if not ok or type(raw) ~= "string" or raw == "" then return nil end local decoded, data = pcall(Fleet.HttpService.JSONDecode, Fleet.HttpService, raw) if not decoded or type(data) ~= "table" or type(data.usernames) ~= "table" or type(data.exp) ~= "number" then return nil end local usernames, count = Fleet.usernamesToSet(data.usernames) return usernames, data.exp, count end function Fleet.writeRegistryCache(usernames, expiresAt) if not Fleet.registryCacheAvailable or type(usernames) ~= "table" then return end pcall(function() writefile( Fleet.registryCachePath(), Fleet.HttpService:JSONEncode({ exp = expiresAt, usernames = usernames }) ) end) end function Fleet.fetchUsernames() local index = Fleet.getJson( "/usernames", Fleet.USERNAME_TRIES, Fleet.USERNAME_RETRY_GAP, Fleet.USERNAME_REQUEST_TIMEOUT ) if not index or type(index.places) ~= "table" or tonumber(index.total_places) ~= #index.places then warn("Fleet GET /usernames trả danh sách place không đầy đủ.") return nil end local rawUsernames, seenPlaces = {}, {} for _, place in ipairs(index.places) do local placeId = type(place) == "table" and tonumber(place.place_id) or nil if not placeId or placeId <= 0 or placeId % 1 ~= 0 then warn("Fleet GET /usernames trả place_id không hợp lệ.") return nil end if not seenPlaces[placeId] then seenPlaces[placeId] = true local path = ("/usernames/%d"):format(placeId) local data = Fleet.getJson(path, Fleet.USERNAME_TRIES, Fleet.USERNAME_RETRY_GAP, Fleet.USERNAME_REQUEST_TIMEOUT) if not data or type(data.usernames) ~= "table" or tonumber(data.total) ~= #data.usernames then warn("Fleet GET " .. path .. " trả usernames không đầy đủ.") return nil end for _, username in ipairs(data.usernames) do rawUsernames[#rawUsernames + 1] = username end end end local usernames, count = Fleet.usernamesToSet(rawUsernames) return usernames, count, rawUsernames end function Fleet.refreshRegistry() local now = os.time() local ttl = Fleet.USERNAME_CACHE_TTL local jitterSpan = math.max(1, math.min(math.floor(ttl / 2), 2 * Fleet.USERNAME_REFRESH_INTERVAL)) local jitter = (player and player.UserId or 0) % jitterSpan if Fleet.registryReady and Fleet.registryCount > 0 and now < Fleet.registryExpiresAt - jitter then return true, Fleet.registryCount end local cached, cachedExpiresAt, cachedCount = Fleet.readRegistryCache() if cached and cachedCount > 0 and cachedExpiresAt and now < cachedExpiresAt - jitter then Fleet.registry = cached Fleet.registryReady = true Fleet.registryCount = cachedCount Fleet.registryExpiresAt = cachedExpiresAt return true, cachedCount end local usernames, count, rawUsernames = Fleet.fetchUsernames() if usernames then if count > 0 then local cacheNow = os.time() -- ponytail: file API không có create-exclusive; recheck giảm ghi trùng, dùng atomic lock nếu executor hỗ trợ. local shared, sharedExpiresAt, sharedCount = Fleet.readRegistryCache() if shared and sharedCount > 0 and sharedExpiresAt and cacheNow < sharedExpiresAt - jitter then Fleet.registry = shared Fleet.registryReady = true Fleet.registryCount = sharedCount Fleet.registryExpiresAt = sharedExpiresAt return true, sharedCount end local expiresAt = cacheNow + ttl Fleet.registry = usernames Fleet.registryReady = true Fleet.registryCount = count Fleet.registryExpiresAt = expiresAt Fleet.writeRegistryCache(rawUsernames, expiresAt) elseif not Fleet.registryReady then Fleet.registry = {} Fleet.registryReady = true Fleet.registryCount = 0 Fleet.registryExpiresAt = 0 end return true, count end if Fleet.registryReady then return true, Fleet.registryCount end if cached and cachedCount > 0 then Fleet.registry = cached Fleet.registryReady = true Fleet.registryCount = cachedCount Fleet.registryExpiresAt = cachedExpiresAt or now return true, cachedCount end return false, 0 end function Fleet.playerInRegistry(otherPlayer, usernames) if not otherPlayer or not player or otherPlayer.UserId == player.UserId then return false end return usernames[otherPlayer.Name:lower()] == true or usernames[tostring(otherPlayer.DisplayName or ""):lower()] == true end function Fleet.hasFriendClone() for _, otherPlayer in ipairs(Players:GetPlayers()) do if Fleet.playerInRegistry(otherPlayer, Fleet.registry) then return true, otherPlayer.Name end end return false end function Fleet.friendCloneStandoff() local cloneName, count, rank = nil, 0, 0 for _, otherPlayer in ipairs(Players:GetPlayers()) do if Fleet.playerInRegistry(otherPlayer, Fleet.registry) then count += 1 cloneName = cloneName or otherPlayer.Name if otherPlayer.UserId < player.UserId then rank += 1 end end end if count == 0 then return false end return true, rank, cloneName, count end function Fleet.fetchJobs() local data = Fleet.getJson(("/jobs/%d"):format(Fleet.PLACE_ID), 1) if not data or type(data.servers) ~= "table" then return nil end local jobs, seen = {}, {} for index = 1, math.min(#data.servers, Fleet.MAX_ITEMS) do local server = data.servers[index] local jobId = type(server) == "table" and server.job_id or nil local playing = type(server) == "table" and tonumber(server.playing) or nil local maxPlayers = type(server) == "table" and tonumber(server.max_players) or nil if type(jobId) == "string" and #jobId >= 1 and #jobId <= 64 and jobId:match("^[A-Za-z0-9%-]+$") and (not playing or not maxPlayers or maxPlayers <= 0 or playing < maxPlayers) and jobId ~= game.JobId and not Fleet.tried[jobId] and not seen[jobId] then seen[jobId] = true jobs[#jobs + 1] = jobId end end return jobs end function Fleet.shuffle(list) for index = #list, 2, -1 do local other = Fleet.rng:NextInteger(1, index) list[index], list[other] = list[other], list[index] end end function Fleet.postJob(kind, jobId) return Fleet.http(("%s/jobs/%d/%s"):format(Fleet.API_BASE, Fleet.PLACE_ID, kind), "POST", { job_id = jobId }) ~= nil end function Fleet.queueJobReport(kind, jobId) Fleet.jobReportQueue[#Fleet.jobReportQueue + 1] = { kind = kind, jobId = jobId } if Fleet.jobReportActive then return end Fleet.jobReportActive = true task.spawn(function() while Fleet.isCurrentRun() and Fleet.jobReportHead <= #Fleet.jobReportQueue do local report = Fleet.jobReportQueue[Fleet.jobReportHead] Fleet.jobReportHead += 1 if not Fleet.postJob(report.kind, report.jobId) then warn("Fleet không post " .. report.kind .. " cho job: " .. tostring(report.jobId)) end task.wait() end Fleet.jobReportQueue = {} Fleet.jobReportHead = 1 Fleet.jobReportActive = false end) end function Fleet.reportDeadJob(jobId) Fleet.queueJobReport("dead", jobId) end function Fleet.clearTeleportError() pcall(Fleet.GuiService.ClearError, Fleet.GuiService) end function Fleet.bindTeleportSignals() for _, connection in ipairs(Fleet.teleportConnections) do connection:Disconnect() end Fleet.teleportConnections = { Fleet.TeleportService.TeleportInitFailed:Connect(function(teleportPlayer, result) if Fleet.currentJobId and teleportPlayer and player and teleportPlayer.UserId == player.UserId then Fleet.teleportFailed = true Fleet.teleportResult = result Fleet.clearTeleportError() warn("Fleet teleport fail " .. tostring(result) .. "; thử JobId kế tiếp.") end end), player.OnTeleport:Connect(function(state) if Fleet.currentJobId and (state == Enum.TeleportState.Started or state == Enum.TeleportState.InProgress) then Fleet.teleportStarted = true end end), } end function Fleet.attemptJob(jobId) if not Fleet.isCurrentRun() or Fleet.stoppedReason or not jobId or jobId == game.JobId or Fleet.tried[jobId] then return false end local sourcePlaceId, sourceJobId = game.PlaceId, game.JobId Fleet.tried[jobId] = true Fleet.currentJobId = jobId Fleet.teleportFailed = false Fleet.teleportResult = nil Fleet.teleportStarted = false Fleet.clearTeleportError() Fleet.queueJobReport("blacklist", jobId) local called, callProblem = pcall(Fleet.TeleportService.TeleportToPlaceInstance, Fleet.TeleportService, Fleet.PLACE_ID, jobId, player) if not called then Fleet.currentJobId = nil warn("Fleet teleport call lỗi; không report dead: " .. tostring(callProblem):sub(1, 300)) return false, "failed" end local deadline = tick() + Fleet.TELEPORT_WAIT while tick() < deadline and not Fleet.teleportFailed and not Fleet.teleportStarted do task.wait(0.1) end if Fleet.teleportStarted then deadline = tick() + Fleet.TELEPORT_CONFIRM while tick() < deadline and not Fleet.teleportFailed and game.PlaceId == sourcePlaceId and game.JobId == sourceJobId do task.wait(0.1) end if game.PlaceId ~= sourcePlaceId or game.JobId ~= sourceJobId then Fleet.currentJobId = nil return true end if not Fleet.teleportFailed then Fleet.currentJobId = nil Fleet.unknownTeleport = true warn("Fleet teleport đã bắt đầu nhưng chưa xác nhận; không teleport hoặc shutdown thêm.") return false, "unknown" end end if Fleet.teleportFailed and Fleet.teleportResult == Enum.TeleportResult.GameEnded then Fleet.reportDeadJob(jobId) end Fleet.currentJobId = nil if not Fleet.teleportFailed then warn("Fleet teleport timeout; không report dead và tiếp tục thử server khác.") end return false, "failed" end function Fleet.fetchRobloxJobs() local jobs, seen, cursor = {}, {}, nil for _ = 1, Fleet.ROBLOX_PAGES do local url = ("https://games.roblox.com/v1/games/%d/servers/Public?limit=100&excludeFullGames=true"):format( Fleet.PLACE_ID ) .. (cursor and ("&cursor=" .. Fleet.HttpService:UrlEncode(cursor)) or "") local raw = Fleet.http(url, "GET") if not raw then break end local ok, data = pcall(Fleet.HttpService.JSONDecode, Fleet.HttpService, raw) if not ok or type(data) ~= "table" or type(data.data) ~= "table" then break end for index = 1, math.min(#data.data, Fleet.MAX_ITEMS) do local server = data.data[index] local jobId = type(server) == "table" and server.id or nil if type(jobId) == "string" and #jobId >= 1 and #jobId <= 64 and jobId:match("^[A-Za-z0-9%-]+$") and jobId ~= game.JobId and not Fleet.tried[jobId] and not seen[jobId] then seen[jobId] = true jobs[#jobs + 1] = jobId end end cursor = type(data.nextPageCursor) == "string" and data.nextPageCursor or nil if not cursor or cursor == "" then break end end return jobs end function Fleet.tryHobeoHop(reason) local jobs for fetchAttempt = 1, Fleet.REQUEST_TRIES do if not Fleet.isCurrentRun() then return false, "stopped" end jobs = Fleet.fetchJobs() if jobs and #jobs > 0 then break end if fetchAttempt < Fleet.REQUEST_TRIES then Fleet.setStatus(("Hobeo chưa có JobId; thử lại %d/%d"):format(fetchAttempt + 1, Fleet.REQUEST_TRIES)) task.wait(Fleet.REQUEST_RETRY_GAP * fetchAttempt + Fleet.rng:NextNumber(0, 1)) end end if not jobs or #jobs == 0 then warn(("Hobeo không trả JobId sau %d lần; chuyển Roblox hop."):format(Fleet.REQUEST_TRIES)) return false end Fleet.shuffle(jobs) local tries = math.min(#jobs, Fleet.HOBEO_HOP_ATTEMPTS) for attempt = 1, tries do if not Fleet.isCurrentRun() then return false end local status = reason == "population" and "Đang hop server ít người" or reason == "steal" and "Đang hop vì pet trong base bị steal" or "Đang hop tránh friend clone" Fleet.setStatus(("%s qua Hobeo %d/%d"):format(status, attempt, tries)) local joined, outcome = Fleet.attemptJob(jobs[attempt]) if joined then return true end if outcome == "unknown" then return false, outcome end if attempt < tries then task.wait(Fleet.HOBEO_RETRY_GAP) end end return false end function Fleet.tryRobloxFallback() if not Fleet.isCurrentRun() or Fleet.stoppedReason then return false end Fleet.setStatus("Hobeo hết server; đang dùng Roblox hop") local jobs = Fleet.fetchRobloxJobs() Fleet.shuffle(jobs) if not jobs[1] then return false, "empty" end return Fleet.attemptJob(jobs[1]) end function Fleet.shutdownAfterRobloxFailure(problem) Fleet.currentJobId = nil Fleet.hopping = false Fleet.stoppedReason = "Roblox hop thất bại; account có thể bị captcha" Fleet.stopMovement() Fleet.setStatus("Roblox hop thất bại; đang shutdown") warn("Roblox hop không thành công; account có thể bị captcha: " .. tostring(problem):sub(1, 300)) pcall(function() messagebox(game.Players.LocalPlayer.Name, "Kill Me", 0x00000010) end) local ok, shutdownProblem = pcall(game.Shutdown, game) if not ok then warn("game:Shutdown() thất bại: " .. tostring(shutdownProblem):sub(1, 300)) end return false, "shutdown" end function Fleet.hop(reason) if Fleet.unknownTeleport then return false, "unknown" end if Fleet.hopping then return false, "busy" end Fleet.hopping = true Fleet.checking = false Fleet.stopMovement() local hobeoOk, hobeoJoined, hobeoOutcome = pcall(Fleet.tryHobeoHop, reason) if hobeoOk and hobeoJoined then Fleet.currentJobId = nil Fleet.hopping = false return true end if Fleet.unknownTeleport or hobeoOutcome == "unknown" then Fleet.currentJobId = nil Fleet.hopping = false Fleet.stoppedReason = "Teleport đang có kết quả chưa rõ; đã chặn gọi lặp" return false, "unknown" end if not Fleet.isCurrentRun() or Fleet.stoppedReason then Fleet.currentJobId = nil Fleet.hopping = false return false, "stopped" end if not hobeoOk then warn("Hobeo hop lỗi; chuyển sang Roblox hop: " .. tostring(hobeoJoined):sub(1, 300)) end local robloxOk, robloxJoined, robloxOutcome = pcall(Fleet.tryRobloxFallback) if robloxOk and robloxJoined then Fleet.currentJobId = nil Fleet.hopping = false return true end if Fleet.unknownTeleport or robloxOutcome == "unknown" then Fleet.currentJobId = nil Fleet.hopping = false Fleet.stoppedReason = "Roblox teleport đang có kết quả chưa rõ; không shutdown" return false, "unknown" end return Fleet.hop(reason) end function Fleet.tickPresence() local now = tick() if Fleet.presencePostActive or now < Fleet.nextPresenceAt then return end Fleet.presencePostActive = true task.spawn(function() local ok, problem = pcall(Fleet.postPresence) Fleet.nextPresenceAt = tick() + Fleet.PRESENCE_HEARTBEAT Fleet.presencePostActive = false if not ok then warn("Fleet heartbeat lỗi: " .. tostring(problem):sub(1, 300)) end end) end function Fleet.startFriendCheck() if Fleet.friendCheckStarted then return end Fleet.friendCheckStarted = true task.spawn(function() pcall(Fleet.refreshRegistry) local joinOk, joinProblem = pcall(function() local found = Fleet.hasFriendClone() if found then warn("Fleet JOIN thấy friend clone; hop ngay") Fleet.hop("friend") end end) if not joinOk then warn("Fleet friend JOIN detect lỗi: " .. tostring(joinProblem):sub(1, 300)) end local lastRefresh = os.clock() local cloneSince = nil local jitter = player.UserId % 11 while Fleet.isCurrentRun() and not Fleet.stoppedReason do task.wait(Fleet.FRIEND_CHECK_INTERVAL) local ok, problem = pcall(function() if Fleet.hopping then cloneSince = nil return end if os.clock() - lastRefresh >= Fleet.USERNAME_REFRESH_INTERVAL then lastRefresh = os.clock() pcall(Fleet.refreshRegistry) end local found, rank, _, count = Fleet.friendCloneStandoff() if not found then cloneSince = nil return end cloneSince = cloneSince or os.clock() local effectiveGrace = Fleet.FRIEND_GRACE_SECONDS + (rank or 0) * Fleet.FRIEND_STAGGER_SECONDS + jitter if os.clock() - cloneSince < effectiveGrace then return end if Fleet.hopping then cloneSince = nil return end warn(("Fleet standoff %d account sau %ds; hop phá kẹt"):format( count or 1, math.floor(effectiveGrace) )) cloneSince = nil Fleet.hop("friend") end) if not ok then warn("Fleet friend standoff lỗi; sẽ thử lại: " .. tostring(problem):sub(1, 300)) end end end) end function Fleet.start() if Fleet.started then return end if game.PlaceId ~= Fleet.PLACE_ID then Fleet.stoppedReason = "Script chỉ hỗ trợ PlaceId " .. Fleet.PLACE_ID return end Fleet.started = true Fleet.requestFn = Fleet.resolveRequest() if not Fleet.requestFn or not player then warn("Fleet workflow không khả dụng; tiếp tục farm.") return end Fleet.checking = false Fleet.bindTeleportSignals() Fleet.startFriendCheck() local presenceStagger = (player.UserId % 17) / 16 * Fleet.PRESENCE_STAGGER Fleet.nextPresenceAt = tick() + presenceStagger + Fleet.rng:NextNumber(0, 0.5) end function Fleet.tick() Fleet.tickPresence() if Fleet.stoppedReason or Fleet.hopping then return true end local now = tick() if now < Fleet.nextPopulationCheckAt then return false end Fleet.nextPopulationCheckAt = now + Fleet.POPULATION_CHECK_INTERVAL local playerCount = #Players:GetPlayers() local minServerPlayers = math.max( 0, math.floor(tonumber((getgenv().CarpetFarmConfig or {}).MinServerPlayers) or 4) ) if playerCount >= minServerPlayers then return false end warn(("Fleet trigger hop population: %d/%d players"):format(playerCount, minServerPlayers)) Fleet.hop("population") return true end 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 local fleetOk, fleetProblem = pcall(Fleet.start) if not fleetOk then Fleet.currentJobId = nil Fleet.hopping = false Fleet.checking = false warn("Fleet startup lỗi; tiếp tục farm: " .. tostring(fleetProblem):sub(1, 300)) 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 Fleet.tick() then return end 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