-- PuckAFK | Jump for Animals | v1.5.1 -- Smart autofarm for Jump for Animals. if not game:IsLoaded() then game.Loaded:Wait() end assert(game.PlaceId == 126870639873289, 'This script is for Jump for Animals.') local ENV = getgenv and getgenv() or _G local KEY = 'PuckAFK_JumpForAnimals' if ENV[KEY] and ENV[KEY].Stop then pcall(ENV[KEY].Stop) end local Players = game:GetService('Players') local RS = game:GetService('ReplicatedStorage') local P = Players.LocalPlayer local Remotes = assert(RS:WaitForChild('Remotes', 20), 'Game remotes not loaded') local Map = assert(workspace:WaitForChild('Map', 20), 'Map not loaded') local Modules = assert(RS:WaitForChild('Settings', 20), 'Game settings not loaded') local function module(name) local object = Modules:FindFirstChild(name) assert(object, 'Missing game settings: '..name) local ok, result = pcall(require, object) assert(ok and type(result) == 'table', 'Cannot read game settings: '..name) return result end local Eggs, Animals, Mutations = module('Eggs'), module('Animals'), module('Mutations') local Barbells, Trails, Coils = module('BarbellUpgrades'), module('Trails'), module('SpeedUpgrades') local Tutorial = module('Tutorial') local GroupReward = module('GroupReward') local Recommended, Carry = module('RecommendedJumps'), module('EggCarryUpgrades') local UI_URL = 'https://raw.githubusercontent.com/PuckAFK/Puck-Loader/refs/heads/main/ui/PuckUI.lua' local okUI, UI = pcall(function() return assert(loadstring(game:HttpGet(UI_URL)))() end) assert(okUI and type(UI) == 'table' and UI.CreateWindow, 'PuckUI could not load: '..tostring(UI)) local Window = UI:CreateWindow({Name='PuckAFK | Jump for Animals', GuiName='PuckAFK_JumpForAnimals_UI', ConfigId='JumpForAnimals', Configs={DefaultProfile='default',AutoSave=true,AutoLoad=true}, DisableConfigs=false}) local S = {alive=true, epoch=0, connections={}, inventory={}, snapshot=false, slots=7, maxSlots=14, status='Ready', lastError='None', target='None', cooldown={}, rejected=setmetatable({}, {__mode='k'}), started=os.clock(), collected=0, placed=0, hatched=0, upgrades=0, sold=0, goldCrafts=0, soldCash=0, lastCollect=0, trainUntil=0, inventoryVersion=0, eggCache={}, cacheAt=-math.huge, goal='None', lastUnlockPower=0, lastEquip='', lastBonus=-1, placementCursor=0, deliveryFailures=0, deliveryAfter=0, trainingSample=nil, trainingChangedAt=os.clock(), trainingRecoveries=0, petBusy=false, pendingSold={}, pendingMutation={}, mutationGoal=nil, blockedCells={}, placementPlot=nil, groupMember=nil, groupChecking=false} local C = {Enabled=false, Eggs=true, Train=true, Hatch=true, Equip=true, Upgrades=true, Sell=true, Mutate=true, Rewards=true, Barbell=true, Trail=true, Coil=false, Slots=true, Index=true, Offline=true, AntiAFK=true, Group=true, Recovery=true, Mode='Balanced', Movement='Instant', TrainSeconds=25, MaxIncubating=8, Reserve=0, UpgradeBudget=100, SaveWait=45, Adaptive=true, Tutorial=true, MaxPayback=300, CollectionBias=25, MoveSpeed=180, Settle=0.2, MutationPayback=3600} local function connect(signal, fn) local c = signal:Connect(fn); table.insert(S.connections,c); return c end local function child(parent, ...) for _, name in ipairs({...}) do parent = parent and parent:FindFirstChild(name) end return parent end local function value(object, default) if object then local v = object:FindFirstChild('V') or object if v:IsA('ValueBase') then return v.Value end end return default end local function number(name, default) return tonumber(value(P:FindFirstChild(name), P:GetAttribute(name))) or default or 0 end local function cash() return tonumber(value(child(P,'leaderstats','Cash'),0)) or 0 end local function plot() return value(P:FindFirstChild('Plot')) end local function body() local ch = P.Character local h = ch and ch:FindFirstChildOfClass('Humanoid') local r = ch and ch:FindFirstChild('HumanoidRootPart') if h and r and h.Health > 0 then return r,h end end local function part(x) if not x then return end if x:IsA('BasePart') then return x end if x:IsA('Model') and x.PrimaryPart then return x.PrimaryPart end return x:FindFirstChildWhichIsA('BasePart',true) end local function active(token) return S.alive and C.Enabled and (not token or token==S.epoch) end local function pause(seconds, token) local untilTime = os.clock()+seconds repeat if not active(token) then return false end task.wait(math.min(0.05, math.max(0.01,untilTime-os.clock()))) until os.clock()>=untilTime return active(token) end local function waitFor(fn, timeout, token) local deadline=os.clock()+timeout repeat if not active(token) then return false end if fn() then return true end task.wait(0.06) until os.clock()>=deadline return false end local function gate(key, interval) if os.clock() < (S.cooldown[key] or 0) then return false end S.cooldown[key]=os.clock()+interval; return true end local function fire(name, key, interval, ...) if not active() or not gate(key,interval) then return false end local remote=Remotes:FindFirstChild(name) if not remote or not remote:IsA('RemoteEvent') then S.lastError='Missing remote: '..name; return false end local ok,err=pcall(remote.FireServer,remote,...) if not ok then S.lastError=tostring(err) end return ok end local function stopTraining() local r=Remotes:FindFirstChild('StopSquattingRequest') if r and P:GetAttribute('IsSquatting') then pcall(r.FireServer,r) end end local function move(pos, token) if not active(token) then return false end local r,h=body(); if not r then return false end stopTraining() if (r.Position-pos).Magnitude < 2 then return pause(C.Settle,token) end if C.Movement=='Instant' then r.CFrame=CFrame.new(pos); r.AssemblyLinearVelocity=Vector3.zero; r.AssemblyAngularVelocity=Vector3.zero else local deadline=os.clock()+math.min(25,(r.Position-pos).Magnitude/C.MoveSpeed+4) while active(token) and os.clock()prompt.MaxActivationDistance then return false end local hold=math.max(prompt.HoldDuration,0) if type(fireproximityprompt)=='function' then local began=os.clock() local ok,err=pcall(fireproximityprompt,prompt,hold) if not ok then S.lastError=tostring(err); return false end return pause(math.max(0.05,hold+0.08-(os.clock()-began)),token) end prompt:InputHoldBegin() local waited=pause(hold+0.1,token) pcall(function() prompt:InputHoldEnd() end) return waited end local function tools() local out={} for index=1,2 do local container=index==1 and P:FindFirstChild('Backpack') or P.Character if container then for _,t in ipairs(container:GetChildren()) do if t:IsA('Tool') and t:GetAttribute('IsEggTool') and t:GetAttribute('EggId') then table.insert(out,t) end end end end return out end local function carried() return tonumber(P:GetAttribute('CarriedEggCount')) or 0 end local function eggCount() local total,incubating=0,0 for _,e in pairs(S.inventory) do if e.Kind=='Egg' then total=total+1 if (tonumber(e.HatchAt) or math.huge)>workspace:GetServerTimeNow() then incubating=incubating+1 end end end return total,incubating end local function petPending(id) local now=os.clock() local sold=S.pendingSold[id] if sold and sold<=now then S.pendingSold[id]=nil;sold=nil end local mutation=S.pendingMutation[id] if mutation and mutation<=now then S.pendingMutation[id]=nil;mutation=nil end return sold~=nil or mutation~=nil end local function petStats() local list={}; local income=0 for _,e in pairs(S.inventory) do if e.Kind~='Egg' and e.LockedInMutationMachine~=true and not petPending(e.Id) then table.insert(list,tonumber(e.CashPerSecond) or 0) if e.Equipped then income=income+(tonumber(e.CashPerSecond) or 0) end end end table.sort(list,function(a,b) return a>b end) return list,income end local function fingerprint() local ids={} for _,e in pairs(S.inventory) do if e.Kind~='Egg' and not petPending(e.Id) then table.insert(ids,tostring(e.Id)..':'..tostring(e.CashPerSecond)..':'..tostring(e.Equipped)..':'..tostring(e.LockedInMutationMachine)..':'..tostring(e.Mutation)) end end table.sort(ids); return tostring(S.slots)..'|'..table.concat(ids,'|') end local function petById(id) for _,e in pairs(S.inventory) do if e.Kind~='Egg' and e.Id==id then return e end end end local function petList() local out={} for _,e in pairs(S.inventory) do if e.Kind~='Egg' and e.LockedInMutationMachine~=true and not petPending(e.Id) then table.insert(out,e) end end return out end local function petCPS(e) return math.max(tonumber(e and e.CashPerSecond) or 0,0) end local function plainPet(e) return e and e.Kind~='Egg' and e.LockedInMutationMachine~=true and not petPending(e.Id) and tostring(e.Mutation or '')=='' and tostring(e.EventMutation or '')=='' and tostring(e.Rarity or '')~='Exclusive' end local function petTools() local out={} for _,container in ipairs({P:FindFirstChild('Backpack'),P.Character}) do if container then for _,tool in ipairs(container:GetChildren()) do if tool:IsA('Tool') and tool:GetAttribute('IsPetInventoryTool')==true then table.insert(out,tool) end end end end return out end local function toolPetName(tool) return tostring(tool and tool:GetAttribute('AnimalName') or '') end local function toolHasId(tool,id) if not tool or not id then return false end if tostring(tool.Name)==tostring(id) then return true end for _,v in pairs(tool:GetAttributes()) do if tostring(v)==tostring(id) then return true end end return false end local function findPetTool(pet,before) local matches={} for _,tool in ipairs(petTools()) do if toolHasId(tool,pet.Id) then return tool end if toolPetName(tool)==tostring(pet.Name) then if before and not before[tool] then return tool end table.insert(matches,tool) end end if #matches==1 then return matches[1] end if #matches>1 and plainPet(pet) then local sameCPS=nil for _,other in pairs(S.inventory) do if other.Kind~='Egg' and other.LockedInMutationMachine~=true and other.Equipped~=true and other.Name==pet.Name and not petPending(other.Id) then if not plainPet(other) then return nil end local cps=petCPS(other) if sameCPS~=nil and cps~=sameCPS then return nil end sameCPS=cps end end if sameCPS==petCPS(pet) then return matches[1] end end end local function sortedPets() local list=petList() table.sort(list,function(a,b) local ac,bc=petCPS(a),petCPS(b) if ac==bc then return tostring(a.Id)bc end) return list end local function topIncome(excluded,added) local values={} for _,pet in ipairs(petList()) do if not excluded or not excluded[pet.Id] then table.insert(values,petCPS(pet)) end end if added and added>0 then table.insert(values,added) end table.sort(values,function(a,b) return a>b end) local total=0 for i=1,math.min(S.slots,#values) do total=total+values[i] end return total end local function weakestSlotCPS() local list=sortedPets() if #listweakestSlotCPS() end local function mutationGroups() local groups={} for _,pet in ipairs(petList()) do if plainPet(pet) then groups[pet.Name]=groups[pet.Name] or {} table.insert(groups[pet.Name],pet) end end for _,group in pairs(groups) do table.sort(group,function(a,b) return petCPS(a)=required then local excluded={};local selected={} for i=1,required do excluded[group[i].Id]=true;table.insert(selected,group[i]) end local gold=goldCPS(name) local during=topIncome(excluded) local after=topIncome(excluded,gold) local gain=after-before if gain>0 then local deficit=math.max(before-during,0)*duration local saleValue=0 for _,pet in ipairs(selected) do saleValue=saleValue+(Animals.GetSellPrice and Animals.GetSellPrice(petCPS(pet)) or 0) end local payback=duration+(deficit+saleValue)/gain if payback<=C.MutationPayback then local score=gain/math.max(payback,1) if not bestScore or score>bestScore then bestScore=score;best={name=name,pets=selected,gold=gold,payback=payback,score=score} end end end end end return best end local function partialMutationTarget(groups) local current=sortedPets() if #current=2 and count0 then local progress=count/required local score=gain*(0.5+2.5*progress*progress) if name==S.mutationGoal then score=score*1.15 end if not bestScore or score>bestScore then bestScore=score;best={name=name,group=group,score=score} end end end end return best end local function mutationReserve() local reserve={} if not C.Mutate then S.mutationGoal=nil;return reserve end local required=math.max(math.floor(tonumber(Mutations.Machine and Mutations.Machine.RequiredMatchingPets) or 5),1) local stored=math.max(math.floor(tonumber(P:GetAttribute('MutationStoredCount')) or 0),0) local storedName=tostring(P:GetAttribute('MutationCraftAnimalName') or '') local groups=mutationGroups() if stored>0 and storedName~='' then S.mutationGoal=storedName local group=groups[storedName] or {} for i=1,math.min(#group,math.max(required-stored,0)) do reserve[group[i].Id]=true end return reserve end local plan=bestMutationPlan() if plan then S.mutationGoal=plan.name for _,pet in ipairs(plan.pets) do reserve[pet.Id]=true end return reserve end local partial=partialMutationTarget(groups) S.mutationGoal=partial and partial.name or nil if partial then for i=1,math.min(required,#partial.group) do reserve[partial.group[i].Id]=true end end return reserve end local function sellCandidates() if not C.Sell then return {} end local list=sortedPets() local protected={} local keepExtra=false if C.Slots and S.slots0 and S.slotPrice/extra<=C.MaxPayback end local keep=math.min(#list,S.slots+(keepExtra and 1 or 0)) for i=1,keep do protected[list[i].Id]=true end for id in pairs(mutationReserve()) do protected[id]=true end local candidates={} for _,pet in ipairs(list) do if plainPet(pet) and not protected[pet.Id] then table.insert(candidates,pet) end end table.sort(candidates,function(a,b) if a.Equipped~=b.Equipped then return a.Equipped~=true end local ac,bc=petCPS(a),petCPS(b) if ac==bc then return tostring(a.Id)1 and ('Selling '..#candidates..' extra pets') or ('Selling extra '..tostring(candidates[1].Name)) S.target='Sell Machine' if not goSellMachine(anchor,detector,token) then return false end local soldCount=0 for _,pet in ipairs(candidates) do if not active(token) then break end local current=petById(pet.Id) if current and plainPet(current) then if not atSellMachine(anchor,detector) and not goSellMachine(anchor,detector,token) then break end if sellPetAtMachine(current,anchor,prompt,detector,token) then soldCount=soldCount+1 if not pause(0.025,token) then break end else break end end end local _,hum=body();if hum then pcall(hum.UnequipTools,hum) end if soldCount>0 then if C.Equip then fire('PetInventory','equipAfterSell',0.15,'EquipBest') end S.status=soldCount==1 and 'Sold 1 extra pet' or ('Sold '..soldCount..' extra pets') S.target='Sell Machine' return true end S.status='Waiting to sell pets' return false end) end local MutationAnchor,MutationPrompt,MutationRemovePrompt local function mutationMachineParts() if MutationAnchor and MutationAnchor.Parent then return MutationAnchor,MutationPrompt,MutationRemovePrompt end local machine=Map:FindFirstChild('Mutations');if not machine then return nil,nil,nil end MutationPrompt=machine:FindFirstChild('MutationPrompt',true) MutationRemovePrompt=machine:FindFirstChild('MutationRemovePrompt',true) local detector=machine:FindFirstChild('Detector',true) MutationAnchor=(MutationPrompt and part(MutationPrompt.Parent)) or part(detector) or part(machine) return MutationAnchor,MutationPrompt,MutationRemovePrompt end local function goMutationPrompt(prompt,anchor,token) if promptInRange(prompt) then return true end local pos=promptPosition(prompt) or (anchor and anchor.Position) if not pos then return false end if not move(pos+Vector3.new(0,2,1),token) then return false end return promptInRange(prompt) end local function addMutationBatch(name,pets,token) if not pets or #pets==0 or not gate('smartMutateTrip',0.1) then return false end return runPetAction(function() local anchor,prompt=mutationMachineParts();if not prompt then return false end S.status='Building Gold '..tostring(name);S.target='Gold '..tostring(name) if not goMutationPrompt(prompt,anchor,token) then return false end local added=0 for _,pet in ipairs(pets) do if not active(token) then break end local current=petById(pet.Id) if current and plainPet(current) then if not promptInRange(prompt) and not goMutationPrompt(prompt,anchor,token) then break end local tool=preparePetTool(current,token);if not tool then break end local beforeStored=math.max(math.floor(tonumber(P:GetAttribute('MutationStoredCount')) or 0),0) if not activatePromptHere(prompt,token,false) then break end local accepted=waitFor(function() local nowPet=petById(pet.Id) return (tonumber(P:GetAttribute('MutationStoredCount')) or 0)>beforeStored or (nowPet and nowPet.LockedInMutationMachine==true) or not tool.Parent end,0.7,token) if not accepted then break end S.pendingMutation[pet.Id]=os.clock()+5 added=added+1 if not pause(0.035,token) then break end end end local _,hum=body();if hum then pcall(hum.UnequipTools,hum) end if added>0 then local required=math.max(math.floor(tonumber(Mutations.Machine and Mutations.Machine.RequiredMatchingPets) or 5),1) if (tonumber(P:GetAttribute('MutationStoredCount')) or 0)>=required and P:GetAttribute('MutationCraftActive')~=true then waitFor(function() return P:GetAttribute('MutationCraftActive')==true or P:GetAttribute('MutationCraftReady')==true end,0.8,token) end if C.Equip then fire('PetInventory','equipAfterMutationAdd',0.15,'EquipBest') end if P:GetAttribute('MutationCraftActive')==true then S.status='Gold '..tostring(name)..' started' else S.status=added==1 and ('Added 1 pet to Gold '..tostring(name)) or ('Added '..added..' pets to Gold '..tostring(name)) end return true end return false end) end local function startMutationIfReady(token) if not gate('startMutation',0.5) then return false end local required=math.max(math.floor(tonumber(Mutations.Machine and Mutations.Machine.RequiredMatchingPets) or 5),1) if (tonumber(P:GetAttribute('MutationStoredCount')) or 0)version then for _,pet in pairs(S.inventory) do if pet.Kind~='Egg' and pet.Name==name and tostring(pet.Mutation or '')==tostring(Mutations.Gold.Name or 'Gold') and pet.LockedInMutationMachine~=true then return true end end end return false end,1.8,token) if claimed then S.goldCrafts=S.goldCrafts+1;S.mutationGoal=nil if C.Equip then fire('PetInventory','equipAfterGold',0.15,'EquipBest') end S.status='Gold '..name..' ready';return true end return false end) end local function clearMutationInputs(token) if not gate('clearMutation',0.15) then return false end return runPetAction(function() local before=math.max(math.floor(tonumber(P:GetAttribute('MutationStoredCount')) or 0),0) if before<=0 or P:GetAttribute('MutationCanRemove')~=true then return false end local anchor,_,prompt=mutationMachineParts();if not prompt then return false end S.status='Updating Gold pets';S.target='Pets' if not goMutationPrompt(prompt,anchor,token) then return false end local removed=0 while active(token) and (tonumber(P:GetAttribute('MutationStoredCount')) or 0)>0 and P:GetAttribute('MutationCanRemove')==true do local count=math.max(math.floor(tonumber(P:GetAttribute('MutationStoredCount')) or 0),0) if not promptInRange(prompt) and not goMutationPrompt(prompt,anchor,token) then break end if not activatePromptHere(prompt,token,false) then break end if not waitFor(function() return (tonumber(P:GetAttribute('MutationStoredCount')) or 0)0 end) end local function managePets(token) if S.petBusy or not gate('smartPets',0.06) then return false end if C.Mutate then local craft=P:GetAttribute('MutationCraftActive')==true local ready=P:GetAttribute('MutationCraftReady')==true local finish=tonumber(P:GetAttribute('MutationCraftFinishAt')) or math.huge if craft and (ready or finish<=workspace:GetServerTimeNow()) then return claimMutation(token) end if not craft then local required=math.max(math.floor(tonumber(Mutations.Machine and Mutations.Machine.RequiredMatchingPets) or 5),1) local stored=math.max(math.floor(tonumber(P:GetAttribute('MutationStoredCount')) or 0),0) local name=tostring(P:GetAttribute('MutationCraftAnimalName') or '') if stored>0 and name~='' then S.mutationGoal=name if not mutationUseful(name) then return clearMutationInputs(token) end local need=math.max(required-stored,0) if need>0 then local group=mutationGroups()[name] or {} if #group>=need then local batch={};for i=1,need do batch[i]=group[i] end return addMutationBatch(name,batch,token) end else if startMutationIfReady(token) then return true end end elseif stored<=0 then local plan=bestMutationPlan() if plan then S.mutationGoal=plan.name return addMutationBatch(plan.name,plan.pets,token) end end end else S.mutationGoal=nil end if C.Sell then local extras=sellCandidates() if #extras>0 then return sellBatch(token,extras) end end return false end local notifyRemote=Remotes:FindFirstChild('Notification') if notifyRemote then connect(notifyRemote.OnClientEvent,function(kind,text) if type(text)=='string' then S.notification=text elseif type(kind)=='string' then S.notification=kind end end) end connect(P.CharacterAdded,function() S.epoch=S.epoch+1; S.trainUntil=0; S.target='Respawning';S.deliveryAfter=0;S.deliveryFailures=0;S.trainingSample=nil end) connect(P.Idled,function() if S.alive and C.AntiAFK then pcall(function() local vu=game:GetService('VirtualUser');vu:CaptureController();vu:ClickButton2(Vector2.zero) end) end end) local function eggValue(egg) local name=egg.Name local cps=Animals.GetCashPerSecond(name,tonumber(egg:GetAttribute('CPSMultiplier')) or 1) local delay=Eggs.GetHatchTime(name) return math.max(tonumber(cps) or 0,0), math.max(tonumber(delay) or 0,0) end local function worldEggs() if os.clock()-S.cacheAt<3 then return S.eggCache end local list={};local stages=Map:FindFirstChild('Stages') if stages then for _,stage in ipairs(stages:GetChildren()) do local folder=stage:FindFirstChild('SpawnedEggs') if folder then for _,egg in ipairs(folder:GetChildren()) do table.insert(list,egg) end end end end S.eggCache=list;S.cacheAt=os.clock();return list end local stages=Map:FindFirstChild('Stages') if stages then connect(stages.DescendantAdded,function(object) if object.Name=='CollectPrompt' or (object.Parent and object.Parent.Name=='SpawnedEggs') then S.cacheAt=-math.huge end end) connect(stages.DescendantRemoving,function(object) if object.Parent and object.Parent.Name=='SpawnedEggs' then S.cacheAt=-math.huge end end) end local function resetTrainingOnUnlock() local power=number('JumpPower',25);local previous=S.lastUnlockPower S.lastUnlockPower=power if previous<=0 or power<=previous then return end for _,threshold in pairs(Recommended.List) do if previous=threshold then S.trainUntil=0;S.cacheAt=-math.huge;S.goal='New egg zone available';return end end end local jumpValue=P:FindFirstChild('JumpPower') if jumpValue and jumpValue:IsA('ValueBase') then connect(jumpValue.Changed,resetTrainingOnUnlock) end resetTrainingOnUnlock() local function chooseEgg() local stages=Map:FindFirstChild('Stages');if not stages then return end local r=body();if not r then return end local pets=petStats() local plannedNames={} for _,e in pairs(S.inventory) do if e.Kind=='Egg' then local name=e.Name or e.AnimalName if name then local expected=Animals.GetCashPerSecond(name,tonumber(e.CPSMultiplier) or 1) table.insert(pets,tonumber(expected) or 0);plannedNames[name]=true end end end for _,tool in ipairs(tools()) do local cps=eggValue(tool);table.insert(pets,cps);plannedNames[tool.Name]=true end table.sort(pets,function(a,b) return a>b end) local weakest=#pets=required and (not rejected or os.clock()>=rejected.untilTime or power>rejected.power+5) then local cps,hatch=eggValue(egg) local discoveredValue=discovered and discovered:FindFirstChild(egg.Name) local isNew=not plannedNames[egg.Name] and (not discoveredValue or value(discoveredValue,false)==false) local gain=math.max(cps-weakest,0) local pos=promptPosition(prompt) if pos and (gain>0 or (isNew and C.CollectionBias>0)) then local distance=(pos-r.Position).Magnitude local route=2+distance/(C.Movement=='Instant' and 1000 or C.MoveSpeed) local score if C.Mode=='Highest Income' then score=cps/(1+route*0.02) else score=gain/(route+math.max(hatch,5)*0.12) end if isNew then score=score+(math.max(cps,1)*C.CollectionBias/100)/(route+math.max(hatch,5)*0.12) end if C.Mode=='Collection' and isNew then score=score*4 end if not P:GetAttribute('TutorialCompleted') and egg:GetAttribute('TutorialNearStart') then score=score+1e15 end if score>bestScore then best,bestScore=egg,score end end end end end return best,bestScore end local function placementCandidate(region) local ownedPlot=plot() if S.placementPlot~=ownedPlot then S.placementPlot=ownedPlot;S.blockedCells={} end local bounds={} for _,name in ipairs({'PlacedEggs','PlacedAnimals'}) do local folder=child(ownedPlot,name) if folder then for _,model in ipairs(folder:GetChildren()) do if model:IsA('Model') then local cf,size=model:GetBoundingBox();table.insert(bounds,{cf=cf,size=size}) elseif model:IsA('BasePart') then table.insert(bounds,{cf=model.CFrame,size=model.Size}) end end end end local choices={};local r=body() for cell=0,24 do if os.clock()>=(S.blockedCells[cell] or 0) then local x=((cell%5)-2)*region.Size.X/6 local z=(math.floor(cell/5)-2)*region.Size.Z/6 local point=region.CFrame:PointToWorldSpace(Vector3.new(x,region.Size.Y/2+12,z)) local clearance=math.min(region.Size.X,region.Size.Z)/2 for _,box in ipairs(bounds) do local relative=box.cf:PointToObjectSpace(point) local gap=math.max(math.abs(relative.X)-box.size.X/2,math.abs(relative.Z)-box.size.Z/2) clearance=math.min(clearance,gap) end local travel=r and (r.Position-point).Magnitude or 0 table.insert(choices,{cell=cell,point=point,score=clearance-travel*0.01}) end end table.sort(choices,function(a,b) return a.score>b.score end) return choices[1] end local function deliveryResult(success) if success then S.deliveryFailures=0;S.deliveryAfter=0 else S.deliveryFailures=math.min(S.deliveryFailures+1,5) S.deliveryAfter=os.clock()+math.min(2^S.deliveryFailures,20) end end local function deliver(token) if carried()>0 then S.status='Returning eggs' local start=part(Map:FindFirstChild('Start')) local home=part(child(plot(),Eggs.Placement.RegionName)) or part(plot()) if not start or not home then S.lastError='Start/plot unavailable';return false end local delta=home.Position-start.Position;delta=Vector3.new(delta.X,0,delta.Z) local dir=delta.Magnitude>0 and delta.Unit or Vector3.new(0,0,1) if not move(start.Position-dir*5+Vector3.new(0,4,0),token) then return false end if not move(start.Position+dir*7+Vector3.new(0,4,0),token) then return false end if not waitFor(function() return carried()==0 end,2,token) then move(home.Position+Vector3.new(0,4,0),token) if not waitFor(function() return carried()==0 end,2,token) then S.lastError='Egg delivery not acknowledged';return false end end end local region=child(plot(),Eggs.Placement.RegionName) if not region or not region:IsA('BasePart') then S.lastError='Plot placement region unavailable';return false end local list=tools() table.sort(list,function(a,b) local x=eggValue(a);local y=eggValue(b);return x>y end) for _,tool in ipairs(list) do local total,incubating=eggCount() if total>=(Eggs.Placement.MaximumPlacedEggs or 20) or incubating>=math.min(C.MaxIncubating,Eggs.Placement.MaximumIncubatingEggs or 8) then return false end if not active(token) then return false end S.status='Placing '..tool.Name local success=false for attempt=1,3 do local candidate=placementCandidate(region) if not candidate then S.lastError='Placement spots cooling down';break end local above=candidate.point local params=RaycastParams.new() params.FilterType=Enum.RaycastFilterType.Exclude local excluded={region} if P.Character then table.insert(excluded,P.Character) end for _,name in ipairs({'PlacedEggs','PlacedAnimals'}) do local folder=child(plot(),name);if folder then table.insert(excluded,folder) end end params.FilterDescendantsInstances=excluded;params.IgnoreWater=true local hit=workspace:Raycast(above,Vector3.new(0,-region.Size.Y-100,0),params) if not hit then S.blockedCells[candidate.cell]=os.clock()+20;S.lastError='No plot ground found for placement';break end local ground=hit.Position if not move(ground+Vector3.new(0,4,0),token) then break end local _,hum=body();if not hum then break end hum:EquipTool(tool) if not pause(0.12,token) then break end local id=tool:GetAttribute('EggId') fire('PlaceEggRequest','place',0.45,id,ground) success=waitFor(function() for _,e in pairs(S.inventory) do if e.Id==id then return true end end return false end,1.6,token) if success then S.placed=S.placed+1;S.blockedCells[candidate.cell]=os.clock()+5;break end if active(token) then S.blockedCells[candidate.cell]=os.clock()+20 end end if not success then S.lastError='Placement not confirmed; retrying later';return false end end return true end local function collect(egg,token) local prompt=egg:FindFirstChild('CollectPrompt',true) S.target=egg.Name..' | '..tostring(egg:GetAttribute('AreaName')) S.status='Collecting '..egg.Name local before=carried();local toolsBefore=#tools() if not trigger(prompt,token) then S.rejected[egg]={untilTime=os.clock()+15,power=number('JumpPower',25)} return false end local confirmed=waitFor(function() return carried()>before or #tools()>toolsBefore or tonumber(egg:GetAttribute('CarriedByUserId'))==P.UserId end,1.6,token) if confirmed then S.rejected[egg]={untilTime=math.huge,power=math.huge} S.collected=S.collected+1;S.lastCollect=os.clock();return true end S.rejected[egg]={untilTime=os.clock()+30,power=number('JumpPower',25)} S.lastError='Pickup not confirmed: '..egg.Name return false end local function housekeeping(token) if not S.snapshot then fire('PetInventory','snapshot',3,'Get');return end fire('PetInventory','refreshSnapshot',30,'Get') if C.Hatch then for _,e in pairs(S.inventory) do if e.Kind=='Egg' and type(e.HatchAt)=='number' and e.HatchAt<=workspace:GetServerTimeNow() then fire('PetInventory','hatch:'..tostring(e.Id),3,'EggAction',e.Id) end end end if C.Equip and not S.petBusy then local fp=fingerprint() local pets={} for _,e in pairs(S.inventory) do if e.Kind~='Egg' and e.LockedInMutationMachine~=true then table.insert(pets,e) end end table.sort(pets,function(a,b) return (tonumber(a.CashPerSecond) or 0)>(tonumber(b.CashPerSecond) or 0) end) local ideal,actual=0,0;local count=0 for i,e in ipairs(pets) do if i<=S.slots then ideal=ideal+(tonumber(e.CashPerSecond) or 0) end if e.Equipped then actual=actual+(tonumber(e.CashPerSecond) or 0);count=count+1 end end if actual0 then fire('OfflineRewards','offline',15,'Claim') end end if C.Train and P:GetAttribute('IsSquatting') and P:GetAttribute('SquatBonusAvailable') then local ver=tonumber(P:GetAttribute('SquatBonusVersion')) if ver and fire('SquatBonusRequest','bonus',0.3,ver) then S.lastBonus=ver end end end local bonusQueued=false local function queueBonus() if bonusQueued or not active() then return end bonusQueued=true task.defer(function() bonusQueued=false if active() then local ok,err=pcall(housekeeping,S.epoch);if not ok then S.lastError=tostring(err) end end end) end connect(P:GetAttributeChangedSignal('SquatBonusAvailable'),queueBonus) connect(P:GetAttributeChangedSignal('SquatBonusVersion'),queueBonus) local function upgrade(token) if not C.Upgrades or not gate('upgradeCycle',2) then return false end local available=math.max(cash()-C.Reserve,0);local budget=available*C.UpgradeBudget/100 local offers={} local function add(cost,gain,fn,confirmed,label) if type(cost)=='number' and cost>=0 and gain>0 then table.insert(offers,{cost=cost,score=gain/math.max(cost,1),run=fn,confirmed=confirmed,label=label}) end end if C.Barbell then local level=number('BarbellLevel',1);local nextUpgrade=Barbells.GetNext(level) local current=Barbells.Get and Barbells.Get(level) if nextUpgrade then local prompt=plot() and plot():FindFirstChild('BarbellUpgradePrompt',true) if prompt then add(nextUpgrade.CashPrice,(nextUpgrade.BaseJumps or 1)/math.max(current and current.BaseJumps or level,1)-1, function() return trigger(prompt,token) end,function() return number('BarbellLevel',1)>level end,'Barbell') end end end local function equipment(enabled,settings,dataName,remote,multiplier) if not enabled then return end local data=P:FindFirstChild(dataName);local owned=child(data,'Owned');local equipped=child(data,'Equipped') if not owned or not equipped then return end local old=value(equipped,'');local oldSettings=settings.Get(old) local oldMult=oldSettings and tonumber(oldSettings[multiplier]) or (multiplier=='BonusPercent' and 0 or 1) for name,info in pairs(settings.List) do local mult=tonumber(info[multiplier]) or 0 if mult>oldMult then local cost=value(owned:FindFirstChild(name),false) and 0 or info.Cost add(cost,(mult-oldMult)/math.max(oldMult,1),function() return fire(remote,'buy:'..remote,1,'Select',name) end, function() return value(equipped,'')==name end,name) end end end equipment(C.Trail,Trails,'TrailData','Trails','JumpMultiplier') equipment(C.Coil,Coils,'CoilData','Coils','BonusPercent') if C.Slots and S.slots0 and S.slotPrice/extra<=C.MaxPayback then local old=S.slots add(S.slotPrice,extra/math.max(income,1),function() return fire('PetInventory','slot',2,'BuyEquipSlot') end, function() return S.slots>old end,'Pet equip slot') end end table.sort(offers,function(a,b) return a.score>b.score end) local _,income=petStats() local affordable for _,offer in ipairs(offers) do if offer.cost<=budget then affordable=offer;break end end if C.Adaptive and income>0 then for _,offer in ipairs(offers) do local wait=(offer.cost/math.max(C.UpgradeBudget/100,0.1)-available)/income if wait>0 and wait<=C.SaveWait and (not affordable or offer.score>affordable.score*1.2) then S.goal=string.format('Saving for %s (about %.0fs)',offer.label,wait) return false end end end S.goal=affordable and ('Next upgrade: '..affordable.label) or 'Training / income progression' for _,offer in ipairs(offers) do if offer.cost<=budget and gate('offer:'..offer.label,12) then S.status='Upgrading '..offer.label if offer.run() and waitFor(offer.confirmed,2,token) then S.upgrades=S.upgrades+1;S.trainUntil=0;return true end S.lastError='Upgrade not confirmed: '..offer.label return false end end return false end local function train(token) if not C.Train then S.status='Waiting for next task';return end local detector=child(plot(),'SquatZone','Floor','Detector') if not detector then S.status='Waiting for training area';return end S.status='Training jump power';S.target='Training area' if P:GetAttribute('IsSquatting') then local sample=tostring(value(child(P,'leaderstats','Level'),0))..':'..tostring(number('JumpTrainingXP',0)) if sample~=S.trainingSample then S.trainingSample=sample;S.trainingChangedAt=os.clock() end if C.Recovery and number('JumpPower',25)<1000 and os.clock()-S.trainingChangedAt>15 and gate('trainingRecovery',20) then S.trainingRecoveries=S.trainingRecoveries+1;S.trainingChangedAt=os.clock() S.lastError='Training XP stalled; restarting squat session' stopTraining() pause(0.3,token) else return end else S.trainingSample=nil;S.trainingChangedAt=os.clock() end if not gate('trainStart',2) then return end if move(detector.Position+Vector3.new(0,3,0),token) then fire('SquatTrainingRequest','trainRemote',1,detector) if not waitFor(function() return P:GetAttribute('IsSquatting')==true end,1.5,token) then S.lastError='Training not acknowledged' end end end local function tutorialStep(token) if not C.Tutorial or P:GetAttribute('TutorialCompleted')==true then return false end local step=P:GetAttribute('TutorialStep') if step=='ReachTrails' or step=='BuyTrail' then if not C.Upgrades or not C.Trail then S.status='Tutorial needs Auto Upgrades and Trail enabled';return true end S.status='Tutorial: buy the starter jump trail' local zone=part(child(plot(),'Trails','Detector')) if zone and gate('tutorialMove',2) then move(zone.Position+Vector3.new(0,3,0),token) end if not active(token) then return true end local name=Tutorial.StarterTrailName or 'Blue Trail' local settings=Trails.Get(name) if settings and cash()>=settings.Cost then fire('Trails','tutorialTrail',2,'Select',name) else S.status='Tutorial: waiting for starter trail cash' end return true end if step=='ReachTrain' or step=='Train' then train(token);return true end if step=='WaitEggReady' or step=='HatchEgg' then S.status='Tutorial: waiting for starter egg hatch' return true end return false end local function cycle(token) if not body() then S.status='Waiting for character';return end if not plot() then S.status='Waiting for your plot';return end resetTrainingOnUnlock() if not S.snapshot then S.status='Loading pets';return end local total,incubating=eggCount() local maxIncubating=math.min(C.MaxIncubating,Eggs.Placement.MaximumIncubatingEggs or 8) if carried()>0 or (#tools()>0 and incubating0 then if not upgrade(token) and active(token) then train(token) end return end if tutorialStep(token) then return end if managePets(token) then return end if upgrade(token) then return end if not active(token) then return end local cave=tostring(Map:GetAttribute('CaveCycleState') or ''):lower() if cave:find('rebuild') or cave:find('evac') or cave:find('collapse') then train(token);return end if not C.Eggs or incubating>=maxIncubating or total>=(Eggs.Placement.MaximumPlacedEggs or 20) then train(token);return end if C.Train and os.clock()0 then local capacity=tonumber(P:GetAttribute('EggCarryCapacity')) or Carry.StartingCapacity or 1 local limit=math.min(capacity,maxIncubating-incubating-#tools()) while active(token) and carried()=S.slots and os.clock()-S.lastCollect<5 then local _,score=chooseEgg();S.trainScore=score;S.trainUntil=os.clock()+C.TrainSeconds end else S.trainScore=0;S.trainUntil=os.clock()+C.TrainSeconds;train(token) end end local function setEnabled(v) C.Enabled=v==true;S.epoch=S.epoch+1;S.trainUntil=0;S.deliveryAfter=0;S.deliveryFailures=0 if not C.Enabled then stopTraining();S.status='Paused';S.target='None' end end local function toggle(tab,key,name) tab:CreateToggle({Name=name,CurrentValue=C[key],Flag=key,Callback=function(v) C[key]=v if key=='Train' and not v then stopTraining() end end}) end local function dropdown(tab,key,name,options) tab:CreateDropdown({Name=name,Options=options,CurrentOption={C[key]},Flag=key,Callback=function(v) C[key]=type(v)=='table' and v[1] or v end}) end local home=Window:CreateTab('Home') home:CreateSection('Autofarm') home:CreateToggle({Name='Smart Autofarm',CurrentValue=false,Flag='Enabled',NoConfig=true,Callback=setEnabled}) home:CreateParagraph({Title='Jump for Animals',Content='Turn on Smart Autofarm and leave the rest on the defaults. The farm handles eggs, training, pets, selling, Gold mutations, rewards and upgrades automatically.'}) dropdown(home,'Mode','Farm Style',{'Balanced','Highest Income','Collection'}) home:CreateSection('Progress') local statusLabel=home:CreateLabel('Status: Ready') local progressLabel=home:CreateLabel('Jump 0 | Cash 0 | Income 0/s') local petsLabel=home:CreateLabel('Pets 0/0 | Incubating 0 | Carried 0') local activityLabel=home:CreateLabel('Collected 0 | Hatched 0 | Sold 0 | Gold 0 | Upgrades 0') local petsTab=Window:CreateTab('Pets') petsTab:CreateSection('Pet Management') toggle(petsTab,'Mutate','Smart Gold Mutations') toggle(petsTab,'Sell','Sell Extra Pets') petsTab:CreateParagraph({Title='Smart Pets',Content='Keeps the pets that help your income, makes useful Gold pets and sells safe extras automatically.'}) petsTab:CreateSection('Rewards') toggle(petsTab,'Rewards','Auto Rewards') local upgrades=Window:CreateTab('Upgrades') upgrades:CreateSection('Smart Upgrades') toggle(upgrades,'Upgrades','Auto Upgrades') toggle(upgrades,'Coil','Include Speed Coils') upgrades:CreateInput({Name='Keep Cash',CurrentValue='0',Flag='CashReserve',Callback=function(v) C.Reserve=math.max(tonumber(v) or 0,0) end}) upgrades:CreateParagraph({Title='Automatic Spending',Content='The farm chooses useful barbells, trails and pet slots automatically. Keep Cash lets you protect an amount you do not want spent.'}) local settings=Window:CreateTab('Settings') settings:CreateSection('Preferences') dropdown(settings,'Movement','Movement',{'Instant','Smooth'}) toggle(settings,'AntiAFK','Anti AFK') local function stop() if not S.alive then return end S.alive=false;C.Enabled=false;S.epoch=S.epoch+1;stopTraining() for _,c in ipairs(S.connections) do pcall(function() c:Disconnect() end) end table.clear(S.connections) pcall(function() Window:Destroy() end) if ENV[KEY] and ENV[KEY].State==S then ENV[KEY]=nil end end ENV[KEY]={Stop=stop,State=S} Window:SetCloseCallback(stop) settings:CreateSection('Script') settings:CreateButton({Name='Unload Script',Callback=stop}) if Window._EnsureConfigTab then Window:_EnsureConfigTab() end task.spawn(function() while S.alive do if C.Enabled then local ok,err=xpcall(function() cycle(S.epoch) end,debug.traceback) if not ok then S.lastError=tostring(err);S.status='Getting back on track';stopTraining();task.wait(1) end end task.wait(0.2) end end) task.spawn(function() while S.alive do if active() then local ok,err=pcall(housekeeping,S.epoch);if not ok then S.lastError=tostring(err) end end task.wait(0.2) end end) task.spawn(function() while S.alive do pcall(function() local _,incubating=eggCount();local pets,income=petStats() statusLabel:Set('Status: '..S.status) progressLabel:Set(string.format('Jump %.1f | Cash %.3g | Income %.3g/s',number('JumpPower',25),cash(),income)) petsLabel:Set(string.format('Pets %d/%d | Incubating %d | Carried %d',#pets,S.slots,incubating,carried())) activityLabel:Set(string.format('Collected %d | Hatched %d | Sold %d | Gold %d | Upgrades %d',S.collected,S.hatched,S.sold,S.goldCrafts,S.upgrades)) end) if gate('prune',60) then local now=os.clock() for key,expiry in pairs(S.cooldown) do if expiry