-- [[ Rscripts Risk Notice ]] -- This script is not verified by rscripts.net. 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 Rscripts 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" (= 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 " - - " 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")