M
ScriptKeyless
[OPEN SOURCE] Rivals Auto Halloween Tower Script
RIVALS [❄️FREEZE RAY❄️]Desktop compatibleWave · Codex · Delta · Synapse Z · Volt · Potassium · Seliware · OpiumWare · Real · Matcha
Description
Join discord to report bugs and help me upload more and throw a follow.
GAMERIVALS [❄️FREEZE RAY❄️]
UPDATEDOct 22, 2026
ACCESSKeyless
PLATFORMDesktop
Script source Luau
-- [[ BloxScripts Risk Notice ]]
-- This script has not been verified by BloxScripts.fun. Deal with caution.
--
-- Stay safe:
-- • Never log in on unofficial Roblox sites or lookalike domains.
-- • Real Roblox links use roblox.com (check the .com ending).
-- • Treat fake Roblox login / "claim reward" pages as phishing.
-- [[ End BloxScripts Risk Notice ]]
--[[
RIVALS – Zombie Tower fists autofarm
Built from the place file. Facts it relies on:
* Fists: 30 dmg, 6 stud reach, 0.35s cooldown. The server checks a 6-stud sphere
around your CAMERA and raycasts to the hitbox against everything tagged
"RaycastWhitelist<EnvironmentID>" (= Zombie Tower.Extra + .Barriers). Walls/floors block hits.
* Lava/acid = parts tagged "OnyxLava". Kill zones = "OutOfBoundsPart".
* AoE attacks (explosive zombies, boss slam) spawn workspace.AttackWarning,
radius = Primary.Size.X / 2.
* Floors: Floors[n].MobGroups[*].Important.Trigger (tag MobTrigger, attr Triggered)
starts the waves; Floors[n].DeleteOnComplete (trapdoor) disappears when a floor is done;
the next floor starts at Floors[n+1].Important.Entrance.
How it fights: hit-and-run. Blink to a spot within reach that has a clear line to the
zombie (prefers above it), aim the game's own camera at it, punch, blink back to a
safe rest spot while the 0.35s cooldown runs. You are only in reach for ~0.15s per punch.
Hold your FISTS before running. Stop with: _G.ZTFarm = false
Re-running replaces the old instance.
]]
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local CollectionService = game:GetService("CollectionService")
local VirtualInputManager = game:GetService("VirtualInputManager")
local player = Players.LocalPlayer
local tower = workspace:WaitForChild("Zombie Tower")
local entities = tower:WaitForChild("Entities")
local floorsFolder = tower:WaitForChild("Floors")
-- ================= Settings =================
local S = {
ATTACK_DIST = 3.5, -- studs from the hitbox SURFACE to your camera when punching (reach is 6)
LEAD = 0.1, -- seconds of zombie movement to lead the aim/position by (server lag)
HIT_AND_RUN = true, -- false = stay at the attack spot between punches
PRE_HIT = 0.10, -- seconds in position before punching (lets the server see you there)
POST_HIT = 0.08, -- seconds to stay after punching
SWING_COOLDOWN = 0.36, -- fists cooldown is 0.35
REST_DIST = 12, -- rest spot distance from the target between punches
REST_CLEAR = 8, -- rest spot must be this far from every zombie
EXPLOSIVE_SAFE = 16, -- stay this far from explosive zombies unless punching one
WARNING_MARGIN = 5, -- extra studs outside AttackWarning circles
HAZARD_MARGIN = 3, -- extra studs around lava / out-of-bounds parts
HEAL_AT = 0.85, -- mid-fight: grab a health drop below this health fraction
TOPUP_WHEN_CLEAR = true,-- no zombies alive: grab health drops (if not full) and ammo drops
COLLECT_AMMO = true, -- also grab ammo drops when the floor is quiet
AUTO_TRIGGER = true, -- touch mob triggers to start waves
AUTO_NEXT_FLOOR = true,-- teleport to the next floor once the current one is done
FLOOR_CLEAR_WAIT = 4, -- seconds with no zombies before a floor counts as done
NOCLIP = true,
MELEE_NAME = "Fists", -- item name the script makes sure is equipped (skins don't matter)
FIST_DAMAGE = 30, -- used to spot the killing blow on an explosive zombie
DEBUG = true, -- prints state changes + each enemy type once (F9)
}
-- ============================================
local session = {}
_G.ZTFarmSession = session
_G.ZTFarm = true
local function running()
return _G.ZTFarm and _G.ZTFarmSession == session
end
local function log(...)
if S.DEBUG then print("[ZT]", ...) end
end
-- ---------- game modules (optional, used for exact aiming / explosive detection) ----------
local CameraController, EnemyController, ExplosiveClass
do
local ps = player:WaitForChild("PlayerScripts")
pcall(function() CameraController = require(ps.Controllers.CameraController) end)
pcall(function() EnemyController = require(ps.Controllers.EnemyController) end)
pcall(function() ExplosiveClass = require(ps.Modules.ClientReplicatedClasses.Enemies.ClientExplosiveZombie) end)
log("CameraController:", CameraController ~= nil, "EnemyController:", EnemyController ~= nil)
end
-- ---------- character ----------
local function getChar()
local c = player.Character
if not c then return end
local r = c:FindFirstChild("HumanoidRootPart")
local h = c:FindFirstChildOfClass("Humanoid")
if r and h and h.Health > 0 then return c, r, h end
end
local holdCF -- position we keep the character at every frame
player.CharacterAdded:Connect(function() holdCF = nil end)
local function moveTo(rootPos, lookAt)
local cf
if lookAt then
local flat = Vector3.new(lookAt.X, rootPos.Y, lookAt.Z)
cf = (flat - rootPos).Magnitude > 0.1 and CFrame.lookAt(rootPos, flat) or CFrame.new(rootPos)
else
cf = CFrame.new(rootPos)
end
holdCF = cf
local _, r = getChar()
if r then
r.CFrame = cf
r.AssemblyLinearVelocity = Vector3.zero
end
end
local function holdStep()
local c, r = getChar()
if not r then return end
if not holdCF then holdCF = r.CFrame end
r.CFrame = holdCF
r.AssemblyLinearVelocity = Vector3.zero
r.AssemblyAngularVelocity = Vector3.zero
if S.NOCLIP then
for _, p in ipairs(c:GetDescendants()) do
if p:IsA("BasePart") then p.CanCollide = false end
end
end
end
local stepConn, beatConn
stepConn = RunService.Stepped:Connect(function()
if not running() then stepConn:Disconnect() return end
holdStep()
end)
beatConn = RunService.Heartbeat:Connect(function()
if not running() then beatConn:Disconnect() return end
holdStep()
end)
local function waitFor(seconds, each)
local t = os.clock()
while running() and os.clock() - t < seconds do
if each and each() == false then return false end
RunService.Heartbeat:Wait()
end
return running()
end
-- ---------- geometry helpers ----------
local function pointInPart(part, p, margin)
margin = margin or 0
local lp = part.CFrame:PointToObjectSpace(p)
local hs = part.Size / 2 + Vector3.one * margin
return math.abs(lp.X) <= hs.X and math.abs(lp.Y) <= hs.Y and math.abs(lp.Z) <= hs.Z
end
local function extentAlong(part, dir)
local cf, hs = part.CFrame, part.Size / 2
return math.abs(dir:Dot(cf.RightVector)) * hs.X
+ math.abs(dir:Dot(cf.UpVector)) * hs.Y
+ math.abs(dir:Dot(cf.LookVector)) * hs.Z
end
-- line of sight exactly like the server's melee check
local losParams = RaycastParams.new()
losParams.FilterType = Enum.RaycastFilterType.Include
local losRefreshed = 0
local function clearLine(from, to)
if os.clock() - losRefreshed > 2 then
losRefreshed = os.clock()
local list = {}
local env = tower:GetAttribute("EnvironmentID")
if env ~= nil then
list = CollectionService:GetTagged("RaycastWhitelist" .. tostring(env))
end
if #list == 0 then
for _, n in ipairs({ "Extra", "Barriers" }) do
local f = tower:FindFirstChild(n)
if f then table.insert(list, f) end
end
end
losParams.FilterDescendantsInstances = list
end
local d = to - from
if d.Magnitude < 1e-3 then return true end
return workspace:Raycast(from, d, losParams) == nil
end
-- ---------- hazards ----------
local hazards, hazardsRefreshed = {}, 0
local function getHazards()
if os.clock() - hazardsRefreshed > 1 then
hazardsRefreshed = os.clock()
hazards = {}
for _, tag in ipairs({ "OnyxLava", "OutOfBoundsPart" }) do
for _, p in ipairs(CollectionService:GetTagged(tag)) do
if p:IsA("BasePart") and p:IsDescendantOf(workspace) then
table.insert(hazards, p)
end
end
end
end
return hazards
end
local warnings = {}
local function trackWarning(m)
if m.Name ~= "AttackWarning" then return end
task.defer(function()
local p = m:FindFirstChild("Primary") or m:WaitForChild("Primary", 1)
if p and m.Parent then
warnings[m] = { part = p }
log("AoE warning, radius", math.floor(p.Size.X / 2))
end
end)
end
workspace.ChildAdded:Connect(trackWarning)
workspace.ChildRemoved:Connect(function(m) warnings[m] = nil end)
for _, m in ipairs(workspace:GetChildren()) do trackWarning(m) end
-- ---------- enemies ----------
local seenTypes = {}
local function isExplosive(model)
if string.find(string.lower(model.Name), "explo", 1, true) then return true end
if EnemyController and ExplosiveClass then
local ok, obj = pcall(EnemyController.GetEnemy, EnemyController, model)
if ok and obj and getmetatable(obj) == ExplosiveClass then return true end
end
return false
end
local function enemyInfo(model)
if not model:IsA("Model") then return end
local hum = model:FindFirstChildOfClass("Humanoid")
if hum and hum.Health <= 0 then return end
local root = model:FindFirstChild("HumanoidRootPart") or model.PrimaryPart
if not root then return end
local hitboxes = {}
for _, d in ipairs(model:GetDescendants()) do
if d:IsA("BasePart") and CollectionService:HasTag(d, "EntityHitbox") then
table.insert(hitboxes, d)
if #hitboxes >= 6 then break end
end
end
if #hitboxes == 0 then hitboxes = { root } end
-- highest first: punching the head from above keeps your body over the zombie
table.sort(hitboxes, function(x, y) return x.Position.Y > y.Position.Y end)
local e = { model = model, root = root, hum = hum, hitboxes = hitboxes, explosive = isExplosive(model) }
if not seenTypes[model.Name] then
seenTypes[model.Name] = true
log("enemy type:", model.Name, e.explosive and "(EXPLOSIVE)" or "")
end
return e
end
local enemies = {}
local function refreshEnemies()
enemies = {}
for _, m in ipairs(entities:GetChildren()) do
local e = enemyInfo(m)
if e then table.insert(enemies, e) end
end
return enemies
end
-- ---------- safety ----------
-- ignore: enemy allowed to be close (the one we punch). crowd: min distance to every enemy.
local function isUnsafe(rootPos, ignore, crowd, allowExplosives)
local pts = { rootPos, rootPos - Vector3.new(0, 3, 0), rootPos + Vector3.new(0, 2, 0) }
for _, hp in ipairs(getHazards()) do
if hp.Parent then
for _, p in ipairs(pts) do
if pointInPart(hp, p, S.HAZARD_MARGIN) then return true, "hazard" end
end
end
end
for m, w in pairs(warnings) do
if m.Parent and w.part.Parent then
local r = w.part.Size.X / 2
local d = rootPos - w.part.Position
if Vector2.new(d.X, d.Z).Magnitude < r + S.WARNING_MARGIN and math.abs(d.Y) < r + 10 then
return true, "warning"
end
else
warnings[m] = nil
end
end
for _, e in ipairs(enemies) do
if e ~= ignore and e.root.Parent then
local dist = (e.root.Position - rootPos).Magnitude
if e.explosive and not allowExplosives and dist < S.EXPLOSIVE_SAFE then return true, "explosive" end
if crowd and dist < crowd then return true, "crowd" end
end
end
return false
end
local function escape()
local _, r = getChar()
if not r then return end
local p = r.Position
local cands = { Vector3.new(0, 20, 0) }
for _, dist in ipairs({ 20, 35, 55 }) do
for i = 0, 7 do
local a = i / 8 * math.pi * 2
table.insert(cands, Vector3.new(math.cos(a) * dist, 8, math.sin(a) * dist))
end
end
for _, v in ipairs(cands) do
if not isUnsafe(p + v, nil, 6) then
moveTo(p + v)
return true
end
end
return false
end
-- ---------- aiming / punching ----------
local function getCamOffset(r)
if CameraController then
local ok, cf = pcall(CameraController.GetCameraCFrame, CameraController)
if ok and typeof(cf) == "CFrame" then
local off = cf.Position - r.Position
if off.Magnitude < 5 then return off end
end
end
return Vector3.new(0, 1.5, 0)
end
local function aimAt(camPos, point)
if (point - camPos).Magnitude < 1e-3 then return end
local cf = CFrame.lookAt(camPos, point)
if CameraController then pcall(CameraController.MimicRotation, CameraController, cf) end
workspace.CurrentCamera.CFrame = cf
end
local function punch()
if typeof(mouse1click) == "function" then
mouse1click()
return
end
local v = workspace.CurrentCamera.ViewportSize / 2
VirtualInputManager:SendMouseButtonEvent(v.X, v.Y, 0, true, game, 0)
task.wait()
VirtualInputManager:SendMouseButtonEvent(v.X, v.Y, 0, false, game, 0)
end
-- ---------- make sure fists are equipped ----------
-- the game names your first-person viewmodel "<player> - <Item> - <Skin>"
local function fistsEquipped()
local vms = workspace:FindFirstChild("ViewModels")
local fp = vms and vms:FindFirstChild("FirstPerson")
if not fp then return nil end
local prefix = player.Name .. " - "
local sawOwn = false
for _, m in ipairs(fp:GetChildren()) do
if string.sub(m.Name, 1, #prefix) == prefix then
sawOwn = true
local item = string.match(string.sub(m.Name, #prefix + 1), "^(.-) %- ")
if item == S.MELEE_NAME then return true end
end
end
if sawOwn then return false end
return nil -- can't tell (e.g. third person), don't block punching
end
local SLOT_KEYS = { Enum.KeyCode.One, Enum.KeyCode.Two, Enum.KeyCode.Three, Enum.KeyCode.Four, Enum.KeyCode.Five }
local fistKey
local lastEquipWarn = 0
local function pressKey(kc)
VirtualInputManager:SendKeyEvent(true, kc, false, game)
task.wait()
VirtualInputManager:SendKeyEvent(false, kc, false, game)
end
-- true / nil = ok to punch, false = fists not found
local function ensureFists()
local st = fistsEquipped()
if st ~= false then return st end
local order = {}
if fistKey then table.insert(order, fistKey) end
for _, k in ipairs(SLOT_KEYS) do
if k ~= fistKey then table.insert(order, k) end
end
for _, k in ipairs(order) do
pressKey(k)
waitFor(0.4)
if fistsEquipped() then
fistKey = k
log("equipped", S.MELEE_NAME, "with key", k.Name)
waitFor(0.3) -- equip animation blocks attacks briefly
return true
end
end
if os.clock() - lastEquipWarn > 5 then
lastEquipWarn = os.clock()
warn("[ZT] couldn't equip " .. S.MELEE_NAME .. " – put them in your loadout / hotbar")
end
return false
end
-- directions from the zombie to the camera, best (highest) first
local DIRS = {}
for _, elev in ipairs({ 85, 60, 35, 10 }) do
local e = math.rad(elev)
local n = elev >= 85 and 1 or 8
for i = 0, n - 1 do
local a = i / n * math.pi * 2
table.insert(DIRS, Vector3.new(math.cos(e) * math.cos(a), math.sin(e), math.cos(e) * math.sin(a)).Unit)
end
end
local function spotFor(target, hb, dir, off)
local aim = hb.Position + target.root.AssemblyLinearVelocity * S.LEAD
local cam = aim + dir * (extentAlong(hb, dir) + S.ATTACK_DIST)
return { hb = hb, dir = dir, aim = aim, cam = cam, rootPos = cam - off }
end
local function findAttackSpot(target, off)
local best, bestScore
for _, hb in ipairs(target.hitboxes) do
if hb.Parent then
for i, dir in ipairs(DIRS) do
local s = spotFor(target, hb, dir, off)
if clearLine(s.cam, hb.Position) and not isUnsafe(s.rootPos, target, nil, true) then
local score = i
for _, e in ipairs(enemies) do
if e ~= target then
local d = (e.root.Position - s.rootPos).Magnitude
if d < 9 then score = score + (9 - d) * 4 end
end
end
if not bestScore or score < bestScore then
best, bestScore = s, score
end
end
end
end
end
return best
end
local function findRestSpot(target, spot)
local dist = target.explosive and (S.EXPLOSIVE_SAFE + 2) or S.REST_DIST
local cands = { spot.dir, Vector3.yAxis }
for i = 0, 7 do
local a = i / 8 * math.pi * 2
table.insert(cands, Vector3.new(math.cos(a), 0.6, math.sin(a)).Unit)
end
for _, d in ipairs(cands) do
local p = spot.aim + d * dist
if not isUnsafe(p, nil, S.REST_CLEAR) then return p end
end
end
local lastSwing = 0
local function attackCycle(target)
local _, r = getChar()
if not r then return false end
if ensureFists() == false then
task.wait(1)
return true
end
_, r = getChar()
if not r then return false end
local off = getCamOffset(r)
local spot = findAttackSpot(target, off)
if not spot then return false end
local rest = S.HIT_AND_RUN and findRestSpot(target, spot) or nil
-- wait for cooldown at the rest spot, then go in
if os.clock() < lastSwing + S.SWING_COOLDOWN - S.PRE_HIT then
waitFor(lastSwing + S.SWING_COOLDOWN - S.PRE_HIT - os.clock(), function()
local _, rr = getChar()
if rr and isUnsafe(rr.Position, target) then escape() end
end)
end
local aborted = false
local function follow()
if not target.root.Parent or (target.hum and target.hum.Health <= 0) then aborted = true return false end
spot = spotFor(target, spot.hb, spot.dir, off)
if not clearLine(spot.cam, spot.hb.Position) or isUnsafe(spot.rootPos, target, nil, true) then
aborted = true
return false
end
moveTo(spot.rootPos, spot.aim)
local _, rr = getChar()
if rr then aimAt(rr.Position + off, spot.aim) end
end
follow()
local stillOk = not aborted and waitFor(S.PRE_HIT, follow)
if not stillOk or aborted then
escape()
return true
end
follow()
-- killing blow on an explosive zombie: leave instantly in case it blows up on death
local killingBlowOnBomb = target.explosive and target.hum and target.hum.Health <= S.FIST_DAMAGE
punch()
lastSwing = os.clock()
if killingBlowOnBomb then
log("killing blow on explosive zombie, backing off")
if not rest or isUnsafe(rest, nil, S.REST_CLEAR) then escape() else moveTo(rest, spot.aim) end
return true
end
waitFor(S.POST_HIT, follow)
if rest and not isUnsafe(rest, nil, S.REST_CLEAR) then
moveTo(rest, spot.aim)
elseif S.HIT_AND_RUN or aborted then
escape()
end
return true
end
-- ---------- floors ----------
local function floorList()
local t = {}
for _, f in ipairs(floorsFolder:GetChildren()) do
local n = tonumber(f.Name)
if n then table.insert(t, { n = n, f = f }) end
end
table.sort(t, function(a, b) return a.n < b.n end)
return t
end
local function important(fl, name)
local imp = fl.f:FindFirstChild("Important")
return imp and imp:FindFirstChild(name)
end
local function floorAt(pos)
for _, fl in ipairs(floorList()) do
local v = important(fl, "Volume")
if v and pointInPart(v, pos) then return fl end
end
end
local function triggersOf(fl)
local t = {}
local mg = fl.f:FindFirstChild("MobGroups")
if not mg then return t end
for _, d in ipairs(mg:GetDescendants()) do
if d:IsA("BasePart") and (d.Name == "Trigger" or CollectionService:HasTag(d, "MobTrigger")) then
table.insert(t, d)
end
end
return t
end
local function enemiesOnFloor(fl)
local v = important(fl, "Volume")
local n = 0
for _, e in ipairs(enemies) do
if not v or pointInPart(v, e.root.Position, 5) then n = n + 1 end
end
return n
end
local lastTouch = {}
local function touchPart(part)
local _, r = getChar()
if not r then return end
local spots = {
part.Position,
part.Position - Vector3.new(0, part.Size.Y / 2 - 3, 0),
part.Position + Vector3.new(0, part.Size.Y / 2 - 3, 0),
}
local target = spots[1]
for _, p in ipairs(spots) do
if not isUnsafe(p, nil) then target = p break end
end
moveTo(target)
if typeof(firetouchinterest) == "function" then
pcall(firetouchinterest, r, part, 0)
task.wait()
pcall(firetouchinterest, r, part, 1)
end
waitFor(0.35)
lastTouch[part] = os.clock()
end
local quietSince, floorRetouch = nil, 0
local function floorWork(fl)
-- 1) start waves
if S.AUTO_TRIGGER then
for _, trig in ipairs(triggersOf(fl)) do
if trig:GetAttribute("Triggered") ~= true and os.clock() - (lastTouch[trig] or 0) > 3 then
log("touching trigger on floor", fl.n)
touchPart(trig)
return true
end
end
end
-- 2) floor done?
if enemiesOnFloor(fl) > 0 then quietSince = nil return false end
quietSince = quietSince or os.clock()
if os.clock() - quietSince < S.FLOOR_CLEAR_WAIT then return false end
if fl.f:FindFirstChild("DeleteOnComplete") then
-- not finished but nothing alive: re-touch triggers every 5s in case one didn't register
if S.AUTO_TRIGGER and os.clock() - floorRetouch > 5 then
floorRetouch = os.clock()
for _, trig in ipairs(triggersOf(fl)) do touchPart(trig) end
end
return false
end
-- 3) next floor
if S.AUTO_NEXT_FLOOR then
for _, nf in ipairs(floorList()) do
if nf.n > fl.n then
local ent = important(nf, "Entrance")
if ent then
log("floor", fl.n, "done -> floor", nf.n)
moveTo(ent.Position + Vector3.new(0, 4, 0))
quietSince = nil
waitFor(1)
return true
end
end
end
end
return false
end
-- ---------- pickups ----------
-- Ground items are server parts named "_drop" (8x8x8, CanTouch) in workspace.
-- The client clones the visual into them: a Model named Health / Ammo / AmmoBalanced / Revive / Weapon.
-- (The HealthPickup/AmmoPickup tagged parts are only spawn markers.)
local DROP_KINDS = { Health = "health", Ammo = "ammo", AmmoBalanced = "ammo" }
local dropTried = {}
local function dropKind(d)
for _, c in ipairs(d:GetChildren()) do
if c:IsA("Model") and DROP_KINDS[c.Name] then return DROP_KINDS[c.Name] end
end
return nil
end
local function findDrop(kind, r, fl)
local vol = fl and important(fl, "Volume")
local best, bestD
for _, d in ipairs(workspace:GetChildren()) do
if d.Name == "_drop" and d:IsA("BasePart") and os.clock() > (dropTried[d] or 0) and dropKind(d) == kind then
local dist = (d.Position - r.Position).Magnitude
local onFloor = (vol and pointInPart(vol, d.Position, 10)) or (not vol and dist < 300)
if onFloor and not isUnsafe(d.Position, nil, 8) and (not bestD or dist < bestD) then
best, bestD = d, dist
end
end
end
return best
end
local function grab(d, kind)
local _, r, hum = getChar()
if not r then return end
local before = hum.Health
log("grabbing", kind, "drop")
dropTried[d] = os.clock() + 4 -- don't retry the same one right away
moveTo(d.Position)
if typeof(firetouchinterest) == "function" then
pcall(firetouchinterest, r, d, 0)
task.wait()
pcall(firetouchinterest, r, d, 1)
end
-- follow it until the server removes it (picked up) or give up after 0.8s
waitFor(0.8, function()
if not d.Parent then return false end
moveTo(d.Position)
end)
local _, _, h2 = getChar()
if kind == "health" and h2 then
log(d.Parent and "health drop not taken" or "health drop taken", math.floor(before), "->", math.floor(h2.Health))
end
end
local function hpFraction(hum)
return hum.Health / math.max(hum.MaxHealth, 1)
end
-- returns true if it went for something
local function collectPickups(r, hum, fl, quiet)
if hpFraction(hum) < S.HEAL_AT or (quiet and S.TOPUP_WHEN_CLEAR and hpFraction(hum) < 0.99) then
local d = findDrop("health", r, fl)
if d then grab(d, "health") return true end
end
if quiet and S.TOPUP_WHEN_CLEAR and S.COLLECT_AMMO then
local d = findDrop("ammo", r, fl)
if d then grab(d, "ammo") return true end
end
return false
end
-- ---------- main loop ----------
local lastState
local function state(s)
if s ~= lastState then lastState = s log(s) end
end
log("started – hold your fists. Stop with _G.ZTFarm = false")
while running() do
local _, r, hum = getChar()
if not r then
state("waiting for character")
task.wait(0.5)
continue
end
refreshEnemies()
if isUnsafe(r.Position, nil) then
state("escaping danger")
escape()
RunService.Heartbeat:Wait()
continue
end
local fl = floorAt(r.Position)
-- low health mid-fight: heal first
if hpFraction(hum) < S.HEAL_AT and collectPickups(r, hum, fl, false) then
continue
end
-- targets: current floor first, non-explosive first, nearest first
local vol = fl and important(fl, "Volume")
table.sort(enemies, function(a, b)
local ia = vol and pointInPart(vol, a.root.Position, 5) and 0 or 1
local ib = vol and pointInPart(vol, b.root.Position, 5) and 0 or 1
if ia ~= ib then return ia < ib end
if a.explosive ~= b.explosive then return not a.explosive end
return (a.root.Position - r.Position).Magnitude < (b.root.Position - r.Position).Magnitude
end)
local attacked = false
for _, e in ipairs(enemies) do
if attackCycle(e) then
state("fighting " .. e.model.Name)
attacked = true
break
end
end
if not attacked then
if #enemies > 0 then
state("no safe spot to hit anything, waiting")
end
if #enemies == 0 and collectPickups(r, hum, fl, true) then
-- grabbed health/ammo while the floor is quiet
elseif fl then
if not floorWork(fl) then RunService.Heartbeat:Wait() end
else
-- not inside any floor (start area / boss arena): go to the first unfinished floor
local nextFloor
for _, f in ipairs(floorList()) do
if f.f:FindFirstChild("DeleteOnComplete") then nextFloor = f break end
end
if nextFloor and #enemies == 0 and S.AUTO_NEXT_FLOOR then
local ent = important(nextFloor, "Entrance")
if ent then
log("going to floor", nextFloor.n)
moveTo(ent.Position + Vector3.new(0, 4, 0))
waitFor(1)
end
else
state("idle")
task.wait(0.2)
end
end
end
end
log("stopped")Review the source before using it. Executions count successful copies, capped at 10 per user and network per script in 24 hours.
Community discussion
0Start the conversation.


Your community, in one place