local Players = game:GetService("Players") local RunService = game:GetService("RunService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local UserInputService = game:GetService("UserInputService") local TweenService = game:GetService("TweenService") local LP = Players.LocalPlayer local Camera = workspace.CurrentCamera ;(function() local old = getgenv().TribesOverlay if old and old.unload then pcall(old.unload) end local oldAim = getgenv().SilentAim if oldAim and oldAim ~= old and oldAim.unload then pcall(oldAim.unload) end end)(); local Shared = ReplicatedStorage:WaitForChild("Shared") local Combat = require(Shared.Combat) local Weapons = require(Shared.Weapons) local Simulation = require(Shared.Simulation) local Ballistics = require(Shared.Combat.Ballistics) local Client = LP.PlayerScripts.Combat local Util = require(Client.Util) local Effects = require(Client.Effects) local WeaponAction = ReplicatedStorage.Remotes.WeaponAction local CONFIG = { enabled = true, fov = 75, -- angular degrees, matches the on-screen ring targets = "both", -- "dummies" | "players" | "both" playerHitPart = "auto",-- "auto" | "head" | "body" dummyHitPart = "auto", -- "auto" | "head" | "body" teamCheck = true, -- skip same Tribe/Team gravityComp = true, -- lift the shot by the drop it takes over the flight viewDelay = 0, -- trim on the incoming-shot timing used by the dodge leadScale = 0.2, -- share of the target's travel over the flight to aim ahead by instantFire = true, -- drawn weapons: full charge with no draw wait autoFire = true, -- fire the instant the server will accept the shot killAura = false, -- swing at every enemy in melee range automatically killAuraRange = 0, -- 0 = the weapon's own range autoHeal = true, -- take a healing item by itself when hurt autoHealBelow = 70, -- heal at or under this much health (%) autoHealCooldown = 3, -- seconds between heal cycles simulate = true, -- wait for a clear line first (target visible) wallbang = false, -- indicator only: the launch point cannot be moved on this server originProbe = 0, -- studs; arms a deliberate launch-point offset to test the server wallbangMax = 9, -- search radius for a clear launch point, studs (0-150) desync = false, -- stepped position desync: jump the replication desyncAmount = 5, -- studs of displacement per jump desyncInterval = 0.18, -- seconds held between jumps desyncLocalSteady = 100, -- % of the displacement to cancel on our own camera realBody = false, -- mark the body the server resolves against, not the one we see realBodyHeat = true, -- colour the marker by how far off the body is realBodyScale = 1, -- size of the marker realBodyTag = true, -- distance label over the marker realBodyChevron = true, -- direction chevron around the crosshair desyncWalkOnly = false, -- only displace while actually moving desyncAimAware = true, -- step across the line an attacker is aiming along desyncVertical = false, -- give each step a height component as well desyncVerticalAmount = 2.5, -- studs of height per step desyncJitter = 0.25, -- vary the interval, so the cadence cannot be fitted dodgeDouble = false, -- step again inside the flight, after their lead is committed jitterDodge = false, -- shake side to side from their draw until their shot lands jitterMin = 3, -- shortest shake, studs jitterMax = 15, -- longest shake, studs jitterInterval = 0.01, -- seconds between shakes; 10ms means never still dodgeSmart = false, -- time the dodge off the attacker's real weapon and distance dodgeFacing = true, -- turn the body toward whoever we dodge dodgeSteadyCamera = true, -- hold the camera still through the step meleeHop = false, -- leave melee's vertical window when a swing winds up meleeHopHeight = 10, -- studs to lift; melee is refused beyond |Y| of 8 desyncReactive = true, -- step the moment an attack is about to land dodge = true, -- jump when someone fires or swings nearby, desync or not dodgeDistanceMin = 6, -- shortest lateral step, drawn at random per dodge dodgeDistanceMax = 10, -- longest lateral step dodgeHold = 0.4, -- seconds to stay displaced dodgeRamp = 0, -- seconds to ease in and out; 0 is an instant step like the desync dodgeRange = 55, -- only react to attackers within this range castStep = 1 / 120, mode = "direct", -- "direct" | "mortar" } local VIS = { enabled = true, pathIndicator = true, -- is there a shot to take: clear line, a launch point, or a wall box = true, boxStyle = "Corners", filledBox = false, fillAlpha = 0.82, healthBar = true, name = true, distance = true, weapon = true, headDot = true, skeleton = false, snapline = false, fovRing = true, watermark = true, realBodyColor = Color3.fromRGB(120, 220, 255), maxDistance = 600, textSize = 13, enemyColor = Color3.fromRGB(255, 65, 65), teamColor = Color3.fromRGB(85, 170, 255), dummyColor = Color3.fromRGB(255, 190, 60), -- chams, ported from the reference list: a Highlight pass and a part-material pass -- tracers, impact markers, hitsounds tracers = true, tracersEnemy = false, tracerColor = Color3.fromRGB(120, 200, 255), tracerColorEnemy = Color3.fromRGB(255, 120, 90), tracerLife = 0.22, tracerThickness = 0.35, -- 6.2 px per stud at 50 studs, so this reads as ~2 px markers = true, markerSize = 26, markerLife = 0.35, markerColorWorld = Color3.fromRGB(230, 230, 230), markerColorPlayer = Color3.fromRGB(255, 70, 70), hitsounds = true, hitsoundVolume = 60, chams = false, chamsFill = Color3.fromRGB(119, 120, 255), chamsFillAlpha = 80, chamsOutline = Color3.fromRGB(0, 0, 0), chamsOutlineDummy = Color3.fromRGB(255, 190, 60), chamsOutlineAlpha = 80, chamsThermal = true, chamsThrough = true, chamsRainbow = false, chamsFade = false, matChams = false, matChamsColor = Color3.fromRGB(0, 255, 255), matChamsAlpha = 0.25, matChamsMaterial = "ForceField", matChamsPattern = "Wave", matChamsSpeed = 3, matChamsRainbow = false, healthGood = Color3.fromRGB(60, 220, 90), healthBad = Color3.fromRGB(240, 60, 60), textColor = Color3.fromRGB(240, 242, 246), } local DEFAULTS = {} for k, v in pairs(CONFIG) do DEFAULTS["c_" .. k] = v end for k, v in pairs(VIS) do DEFAULTS["v_" .. k] = v end local STATE = { redirected = 0, lastShot = nil, lastSolution = nil, lastDenial = nil, lastHit = nil, pool = 0, aimActive = false, selectDebug = nil, lastError = nil, } -- Ring buffer of everything the attitude of the last few shots: what the client -- sent, what the server echoed back, and whether damage was confirmed. local SHOTLOG = {} local function logEvent(kind, text) SHOTLOG[#SHOTLOG + 1] = { kind = kind, t = os.clock(), text = text } if #SHOTLOG > 30 then table.remove(SHOTLOG, 1) end end STATE.log = SHOTLOG local closed = false local pending -- ------------------------------------------------------------------ target model local CollectionService = game:GetService("CollectionService") local function dummyFolder() local lobby = workspace:FindFirstChild("Lobby") local range = lobby and lobby:FindFirstChild("Range") return range and range:FindFirstChild("Dummies") end -- A dummy is a dummy by name and nothing else. Membership of the range folder says -- nothing (real players stand in there), and neither does the absence of a Player -- instance, which is what bots and anyone mid-spawn look like. Those two mistakes are -- what painted people with the dummy colour and steered shots at the dummy hitbox. -- Real usernames never contain the word, and the range targets are DUMMY / -- TrainingDummy, so one substring test separates them correctly. local function isDummy(model) if typeof(model) ~= "Instance" then return false end return string.find(string.upper(model.Name), "DUMMY", 1, true) ~= nil end -- Match combatants (bots / objective NPCs) are tagged, not Players. local function isCombatant(model) return CollectionService:HasTag(model, "Combatant") or CollectionService:HasTag(model, "Bot") end -- Game-authoritative team test: same MatchId and same character Team attribute. local function isTeammate(model) local ok, result = pcall(Util.IsTeammate, model) if ok and result ~= nil then return result == true end return false end -- The server resolves arrow damage against dedicated hitboxes, not the visible -- mesh: players carry an invisible TribesHull under the root, range dummies use -- Target parts. The visible Head sits above the hull, so aiming at it passes over -- the only part that can take damage. local function subPart(part, name) if part then local child = part:FindFirstChild(name) if child and child:IsA("BasePart") then return child end end return nil end -- Region selector: which part the arrow is steered at. "auto" uses the volume -- the game scores damage on (the chest hitbox), head/body steer explicitly. local function hitboxOf(model, root, head, player, dummy) local mode = "auto" if player then mode = CONFIG.playerHitPart elseif dummy then mode = CONFIG.dummyHitPart end local torso = model:FindFirstChild("UpperTorso") or model:FindFirstChild("Torso") if mode == "head" then return subPart(head, "Target") or head elseif mode == "body" then if root then local hull = root:FindFirstChild("TribesHull") if hull and hull:IsA("BasePart") then return hull end end return subPart(torso, "Target") or torso or subPart(root, "Target") or head end if root then local hull = root:FindFirstChild("TribesHull") if hull and hull:IsA("BasePart") then return hull end local target = root:FindFirstChild("Target") if target and target:IsA("BasePart") then return target end end -- range dummies carry their scoring plates as direct children of the model local reference = torso and torso.Position or head.Position local plate, plateDist for _, part in ipairs(model:GetChildren()) do if part:IsA("BasePart") and part.Name == "Target" then local d = (part.Position - reference).Magnitude if not plateDist or d < plateDist then plate, plateDist = part, d end end end if plate then return plate end local target = subPart(torso, "Target") if target then return target end target = subPart(head, "Target") if target then return target end return head end local function validTarget(model) if not model or not model.Parent then return nil end local player = Players:GetPlayerFromCharacter(model) if player == LP then return nil end local dummy = isDummy(model) if not dummy and not (player or isCombatant(model)) then return nil end if CONFIG.teamCheck and isTeammate(model) then return nil end local humanoid = model:FindFirstChildOfClass("Humanoid") local head = model:FindFirstChild("Head") local root = model:FindFirstChild("HumanoidRootPart") or model.PrimaryPart if humanoid and humanoid.Health > 0 and head and head:IsA("BasePart") and root and root:IsA("BasePart") then return head, root, player, dummy, hitboxOf(model, root, head, player, dummy) end end local function rangeDummies() local out = {} local folder = dummyFolder() if folder then for _, model in ipairs(folder:GetChildren()) do if model:IsA("Model") and isDummy(model) then out[#out + 1] = model end end end return out end -- Util.Targets() is the game's own candidate list: other players plus every -- tagged Combatant / Bot. Using it keeps the aim aligned with what the server -- will actually accept damage against. local function combatTargets() local out = {} local ok, list = pcall(function() return Util.Targets() end) if ok and type(list) == "table" then for _, model in ipairs(list) do if model and model.Parent then out[#out + 1] = model end end end return out end local function targetPool() local out, seen = {}, {} local function add(model) if model and model.Parent and not seen[model] then seen[model] = true out[#out + 1] = model end end if CONFIG.targets == "dummies" then for _, model in ipairs(rangeDummies()) do add(model) end elseif CONFIG.targets == "players" then for _, model in ipairs(combatTargets()) do if not isDummy(model) then add(model) end end else for _, model in ipairs(combatTargets()) do add(model) end for _, model in ipairs(rangeDummies()) do add(model) end end return out end -- ------------------------------------------------------------------ aim -- Every weapon the game launches as a projectile: Bow, Crossbow, Musket, Pistol, -- Blunderbuss, Javelin, Knives, Sling, Bomb. Drawn weapons carry a charge in the -- payload, guns send a flat ShotSpeed instead. local function shotParams(tool) if not tool or not tool:IsA("Tool") then return nil end local weaponId = tool:GetAttribute("WeaponId") local definition = weaponId and Weapons.Get(weaponId) local tuning = definition and definition.tuning if not tuning then return nil end local kind = definition.kind or tool:GetAttribute("Kind") if not Combat.FiresProjectile(kind) then return nil end return tuning, kind, weaponId end local oneWayLatency local killAuraAim -- Where the shot is pointed: the volume the server resolves damage against, or the head -- when that is the configured hit part. local function aimPartFor(head, hitbox, player, dummy) local mode = player and CONFIG.playerHitPart or (dummy and CONFIG.dummyHitPart or "auto") local part = hitbox or head if hitbox and mode == "head" then part = head end return part end -- The one thing the aim can wait on, and only when "wait for target visible" is on: a -- straight line from the muzzle to the aim point that the world does not block. local function hasLine(origin, point, model) local toPoint = point - origin if toPoint.Magnitude < 0.01 then return false end local dir = toPoint.Unit local targetParams = RaycastParams.new() targetParams.FilterType = Enum.RaycastFilterType.Include targetParams.FilterDescendantsInstances = { model } local hit = workspace:Raycast(origin, dir * (toPoint.Magnitude + 2), targetParams) if not hit then return true end local worldParams = Util.WorldParams(Util.Teammates()) local exclude = worldParams.FilterDescendantsInstances exclude[#exclude + 1] = model worldParams.FilterDescendantsInstances = exclude return workspace:Raycast(origin, dir * math.max(0.1, hit.Distance - 0.05), worldParams) == nil end -- Target selection: everyone inside the FOV circle, whoever sits closest to the -- crosshair. Being in the circle is the whole requirement - no range gate, no speed gate, -- no hold. The target already being shot at keeps a small bonus so the pick does not -- flick between two bodies standing together, and that bonus never beats a target that is -- clearly closer to the crosshair. -- Filled in with the rest of the origin work further down; selection needs it to decide -- whether a target that is behind cover is worth taking at all, and books its results here. local findWallbangOrigin local viewLag local WALL = { open = 0, blocked = 0, moved = 0, noPath = 0, budgetHit = 0, fromSelect = 0, atFire = 0, pass = 0 } STATE.wall = WALL local function wallbangReach() return math.clamp(tonumber(CONFIG.wallbangMax) or 14, 1, 150) end -- Target selection: everyone inside the FOV circle, whoever sits closest to the crosshair. -- Being in the circle is the whole requirement - no range gate, no speed gate, no hold. The -- target already being shot at keeps a small bonus so the pick does not flick between two -- bodies standing together, and that bonus never beats a target that is clearly closer to -- the crosshair. -- -- "Wait for target visible" used to be applied here as a flat filter, which is what stopped -- the wallbang doing its job: a target behind cover was discarded before the shot was ever -- planned, so there was nothing left to relocate, and the wallbang only fired on the shots -- where the current position happened to be visible while the lead point was not. Candidates -- are gathered in crosshair order and then walked: the first one with a clear line wins, and -- a blocked one is kept when the wallbang can find it a launch point. local function selectTarget(origin, prefer) local look = Util.GetAimRay().Direction.Unit local candidates = {} for _, model in ipairs(targetPool()) do local head, root, player, dummy, hitbox = validTarget(model) if head then local part = aimPartFor(head, hitbox, player, dummy) local delta = part.Position - origin local distance = delta.Magnitude if distance > 0.01 then local angle = math.deg(math.acos(math.clamp(look:Dot(delta.Unit), -1, 1))) if angle <= CONFIG.fov then local score = angle if prefer == model then score = score - 3 end candidates[#candidates + 1] = { model = model, head = head, hitbox = hitbox, part = part, angle = angle, distance = distance, root = root, score = score, velocity = root.AssemblyLinearVelocity, direction = delta.Unit, } end end end end table.sort(candidates, function(a, b) return a.score < b.score end) -- only the few closest to the crosshair are worth a search; each one costs raycasts local searched, budget = 0, 3 for _, target in ipairs(candidates) do if hasLine(origin, target.part.Position, target.model) then target.blocked = false return target end target.blocked = true if CONFIG.wallbang and findWallbangOrigin and searched < budget then searched = searched + 1 local wallOrigin, _, wallPoint = findWallbangOrigin(origin, target.part.Position, target.model, wallbangReach()) if wallOrigin then -- recorded for the indicator only: nothing is solved from it target.wallOrigin = wallOrigin target.wallAim = wallPoint WALL.fromSelect = WALL.fromSelect + 1 return target end end if not CONFIG.simulate then return target end end if candidates[1] then STATE.selectDebug = { reason = "in FOV but behind cover, no launch point found" } end return nil end -- The direction that gets sent: straight at the aim point, lifted by the drop the -- projectile takes over the flight (off with "gravity compensation"). Nothing about the -- target's motion is predicted. local function aimDirection(origin, point, speed, gravity, arc) local distance = (point - origin).Magnitude if distance < 0.01 then return nil end local lift = 0 if CONFIG.gravityComp and (gravity or 0) > 0 and (speed or 0) > 0 then local flight = distance / speed lift = 0.5 * gravity * flight * flight * (arc == "high" and 1.8 or 1) end local delta = (point + Vector3.new(0, lift, 0)) - origin if delta.Magnitude < 0.01 then return nil end return delta.Unit end local function targetIsAirborne(model) local root = model and model:FindFirstChild("HumanoidRootPart") if not root then return false end return math.abs(root.AssemblyLinearVelocity.Y) > 8 end -- The shot path every hook uses: who to point at, and the direction to send. local function planShot(origin, speed, gravity, prefer) local target = selectTarget(origin, prefer) if not target then STATE.selectDebug = { reason = "no target in FOV" } return nil end -- The solve origin is the muzzle, always. A launch point found by the search cannot be -- sent (the server fires from where it thinks we are, not from the point we hand it), so -- solving from it would aim the shot from somewhere the round never leaves. local solveOrigin = origin -- origin probe: a deliberate offset, used to find out whether the server reads the launch -- point we send at all. Off unless armed. if (CONFIG.originProbe or 0) > 0 then solveOrigin = origin + Vector3.new(0, CONFIG.originProbe, 0) STATE.probeArmed = true end local point = target.wallAim or target.part.Position local lead = CONFIG.leadScale or 0 if lead > 0 and (speed or 0) > 0 then -- one pass: the flight to where they are now, and that share of their own -- travel carried forward local flight = (point - solveOrigin).Magnitude / speed point = point + target.velocity * flight * lead end local direction = aimDirection(solveOrigin, point, speed, gravity, CONFIG.mode == "mortar" and "high" or nil) if not direction then return nil end target.direction = direction target.aimPoint = point target.origin = ((CONFIG.originProbe or 0) > 0) and solveOrigin or nil target.firstPart = target.part.Name local sentFrom = target.origin or solveOrigin local direct = (point - sentFrom) local flatDir = Vector3.new(direction.X, 0, direction.Z) local flatPoint = Vector3.new(direct.X, 0, direct.Z) if flatDir.Magnitude > 0.001 and flatPoint.Magnitude > 0.001 then target.latErr = math.deg(math.acos(math.clamp(flatDir.Unit:Dot(flatPoint.Unit), -1, 1))) else target.latErr = 0 end target.originShift = (solveOrigin - origin).Magnitude STATE.lastLatErr = target.latErr return target end -- ------------------------------------------------------------------ wallbang origin -- World-only clearance between two points: terrain, props and everything else the game -- makes a projectile answer to, with the teams we do not shoot at filtered out the same way -- the rest of the aim does. local function worldClear(a, b) local delta = b - a if delta.Magnitude < 0.01 then return true end local params = Util.WorldParams(Util.Teammates()) return workspace:Raycast(a, delta, params) == nil end -- Offsets are tried nearest first, inside a frame built on the shot itself. Up and out over -- what is in front is the move that clears a wall, a rock or a ridge; straight forward -- clears a corner the barrel is tucked behind; sideways clears what is beside us. A -- candidate also has to be reachable through open air, or the shot would start inside -- geometry and expire on its first step. -- The clearance test hasLine runs, against raycast parameters built once for the whole -- search. Building them per candidate (a RaycastParams plus a teammates table each time) cost -- more than the raycasts did: a full walk took 15 ms on a phone, which is a dropped frame. local function clearWith(targetParams, rayParams, origin, point) local toPoint = point - origin -- zero length cannot reach anything: a ray starting inside a part does not report it if toPoint.Magnitude < 0.01 then return false end local dir = toPoint.Unit local hit = workspace:Raycast(origin, dir * (toPoint.Magnitude + 2), targetParams) if not hit then return true end -- the first thing the shot would meet being their own weapon is not reaching them if hit.Instance:FindFirstAncestorOfClass("Tool") then return false end return workspace:Raycast(origin, dir * math.max(0.1, hit.Distance - 0.05), rayParams) == nil end -- Where a search left off, so selection can call this every frame without paying for a -- walk: the same target from the same place answers from here for half a second. A failed -- search is remembered for less time than a successful one. local searchCache = { model = nil, origin = nil, aim = nil, at = 0, candidate = nil, mode = nil } -- How far behind the server's copy of a target our view is: half the round trip plus the -- interpolation the game buffers with. Aiming a point-blank shot without this aims at where -- they were, which is the one thing that can still miss once the flight is nearly zero. viewLag = function() local lag = 0 pcall(function() lag = oneWayLatency() end) return math.clamp((lag or 0) + (CONFIG.viewDelay or 0) + 0.065, 0, 0.6) end findWallbangOrigin = function(origin, aimPoint, model, maxOffset) local now = os.clock() local memo = searchCache if memo.model == model and memo.candidate ~= nil and now - memo.at < (memo.candidate and 0.8 or 0.5) and memo.origin and (memo.origin - origin).Magnitude < 2 and memo.aim and (memo.aim - aimPoint).Magnitude < 3 then if memo.candidate == false then return nil end return memo.candidate[1], memo.candidate[2], memo.candidate[3] end local delta = aimPoint - origin if delta.Magnitude < 1 then return nil end local forward = delta.Unit local right = forward:Cross(Vector3.new(0, 1, 0)) right = right.Magnitude > 0.01 and right.Unit or Vector3.new(1, 0, 0) local up = Vector3.new(0, 1, 0) -- the clearance test hasLine runs, against raycast parameters built once for the whole -- search: building them per candidate cost more than the raycasts did local targetParams = RaycastParams.new() targetParams.FilterType = Enum.RaycastFilterType.Include targetParams.FilterDescendantsInstances = { model } local rayParams = Util.WorldParams(Util.Teammates()) local rayExclude = rayParams.FilterDescendantsInstances rayExclude[#rayExclude + 1] = model rayParams.FilterDescendantsInstances = rayExclude local airParams = Util.WorldParams(Util.Teammates()) local overlap = OverlapParams.new() overlap.FilterType = Enum.RaycastFilterType.Exclude local overlapExclude = airParams.FilterDescendantsInstances -- the target's own body must not read as solid ground for a point placed beside them overlapExclude[#overlapExclude + 1] = model overlap.FilterDescendantsInstances = overlapExclude overlap.MaxParts = 8 local AIR_RANGE = 40 -- One ray for the case that dominates (the line is blocked): the world between the point -- and the body. The target's model is excluded from these params, so a clear result means -- the line reaches them through open air - which also means the weapon check, the only part -- that needs the model, can wait until a candidate actually passes. local function reachable(candidate, point) local delta = point - candidate local length = delta.Magnitude if length < 0.01 then return false end local dir = delta.Unit if workspace:Raycast(candidate, dir * length, rayParams) ~= nil then return false end local hit = workspace:Raycast(candidate, dir * (length + 2), targetParams) if hit and hit.Instance:FindFirstAncestorOfClass("Tool") then return false end return true end -- What counts as reaching them. The aim point first, then the body itself, biggest part -- down: a clear line to an arm or a leg is a path even when the chest is buried, and -- testing three fixed points was a large share of the searches that came back empty. local points, seenPoint = {}, {} local function addPoint(p) if p and not seenPoint[p] then seenPoint[p] = true points[#points + 1] = p end end addPoint(aimPoint) local bodyParts = {} for _, d in ipairs(model:GetDescendants()) do if d:IsA("BasePart") and d.Transparency < 0.9 and not d:FindFirstAncestorOfClass("Tool") then bodyParts[#bodyParts + 1] = d end end table.sort(bodyParts, function(a, b) return a.Size.Magnitude > b.Size.Magnitude end) for i = 1, math.min(#bodyParts, 2) do addPoint(bodyParts[i].Position) end if #points == 0 then addPoint(aimPoint) end local budget = 900 local function attempt(candidate, radius, requireAir) if budget <= 0 then WALL.budgetHit = WALL.budgetHit + 1 return nil end budget = budget - 1 if requireAir and radius <= AIR_RANGE then if workspace:Raycast(origin, candidate - origin, airParams) ~= nil then WALL.rejAir = (WALL.rejAir or 0) + 1 return nil end elseif #workspace:GetPartBoundsInRadius(candidate, 0.55, overlap) > 0 then -- nothing solid may overlap the point, or the projectile starts inside it WALL.rejSolid = (WALL.rejSolid or 0) + 1 return nil end for i = 1, #points do local p = points[i] if reachable(candidate, p) then return candidate, radius, p end end WALL.rejLine = (WALL.rejLine or 0) + 1 return nil end -- Sampling: a cone around the muzzle, 16 azimuths at four elevations over the near radii -- and 8 azimuths at three further out. The old set was 12 fixed directions plus a thin -- ring, which at arm's length left gaps wider than a doorway - a clear window between two -- spokes was never tested, so the search reported no path with the gap in plain sight. local nearSteps = { 1.5, 2.5, 4.5, 7, 11, 14 } local farSteps = { 19, 27, 38, 52, 70, 92, 120 } local nearElev = { 0.12, 0.55, 1.0 } local farElev = { 0.35, 0.75 } local found, foundRadius, foundPoint, passUsed local function searchPath(requireAir, limit) local function ring(steps, elevations, azimuths) for _, radius in ipairs(steps) do if radius > limit then break end for _, sinE in ipairs(elevations) do local cosE = math.sqrt(1 - sinE * sinE) -- straight up is the same point at every azimuth local count = sinE >= 0.999 and 1 or azimuths for a = 0, count - 1 do local theta = (a / count) * math.pi * 2 local dir = (forward * math.cos(theta) + right * math.sin(theta)) * cosE + up * sinE local hit, moved, point = attempt(origin + dir.Unit * radius, radius, requireAir) if hit then return hit, moved, point end end end end return nil end local hit, moved, point = ring(nearSteps, nearElev, 18) if hit then return hit, moved, point end return ring(farSteps, farElev, 8) end for pass = 1, 2 do local requireAir = pass == 1 local limit = requireAir and maxOffset or math.min(maxOffset, AIR_RANGE) found, foundRadius, foundPoint = searchPath(requireAir, limit) if found then passUsed = pass break end end if found then WALL.pass = passUsed or 0 searchCache = { model = model, origin = origin, aim = aimPoint, at = now, candidate = { found, foundRadius, foundPoint } } return found, foundRadius, foundPoint end WALL.pass = 0 searchCache = { model = model, origin = origin, aim = aimPoint, at = now, candidate = false } return nil end -- Only ever runs when the shot as the game built it would have hit the wall in front: a -- clear line is left exactly as it is. The direction is re-solved from the new origin -- rather than reused, because the aim point is a different vector from up there, and the -- lead has to be re-applied with it. local function applyWallbang(origin, solution, speed, gravity) if (CONFIG.originProbe or 0) > 0 and solution then -- probe: the shot is solved from the offset point so the outcome can be read local probe = origin + Vector3.new(0, CONFIG.originProbe, 0) solution.origin = probe solution.direction = aimDirection(probe, solution.aimPoint or solution.part.Position, speed, gravity) WALL.probe = (WALL.probe or 0) + 1 return end if not (CONFIG.wallbang and solution and solution.model and solution.model.Parent) then return end -- selection already moved this shot's origin; re-searching from the muzzle here would -- undo that pick and possibly land on a different point if solution.origin then return end local aimPoint = solution.aimPoint or (solution.part and solution.part.Position) if not aimPoint then return end if hasLine(origin, aimPoint, solution.model) then WALL.open = WALL.open + 1 WALL.last = "open" end WALL.blocked = WALL.blocked + 1 local reach = math.clamp(tonumber(CONFIG.wallbangMax) or 14, 1, 150) local candidate, moved, wallPoint = findWallbangOrigin(origin, aimPoint, solution.model, reach) if not candidate then WALL.noPath = WALL.noPath + 1 WALL.last = "blocked, no launch point" logEvent("wall", string.format("blocked, no clear origin within %.0f studs", reach)) return end local aimUse = wallPoint or aimPoint local close = (candidate - aimUse).Magnitude do local away = candidate - aimUse local along = away.Magnitude > 0.01 and away.Unit:Dot((aimUse - origin).Unit) or 0 WALL.spawnSide = along > 0.35 and "behind" or (along < -0.35 and "in front" or "side") end if close < 25 then -- Point-blank class: rebuild the aim, because the lead carried in here was solved for a -- flight from the muzzle. The base has to be the body point the path was actually -- checked against - re-solving onto a different one aims the round through whatever -- the search had just ruled out (their weapon, every time). local root = solution.model:FindFirstChild("HumanoidRootPart") local vel = root and root.AssemblyLinearVelocity or Vector3.new(0, 0, 0) local base = wallPoint or (solution.part and solution.part.Position) or aimUse local flight = (base - candidate).Magnitude / math.max(speed or 1, 1) aimUse = base + vel * (flight + viewLag()) WALL.spawnDist = close end local direction = aimDirection(candidate, aimUse, speed, gravity, CONFIG.mode == "mortar" and "high" or nil) if not direction then return end solution.origin = candidate solution.aimPoint = aimUse solution.direction = direction solution.wallband = moved WALL.moved = WALL.moved + 1 WALL.atFire = (WALL.atFire or 0) + 1 WALL.last = "moved " .. string.format("%.1f", moved) logEvent("wall", string.format("%s blocked -> origin %.1f studs over, line clear", solution.model.Name, moved)) end -- ------------------------------------------------------------------ calibration -- The server rebroadcasts every projectile it simulates (origin, velocity, the -- gravity it applied) and every impact position, keyed by "p:". -- A launch state plus its impact is a complete trajectory, and the horizontal -- equations are linear in time, so flight time and the applied gravity can both -- be solved exactly. Aggregating those gives the physics the server actually -- uses, not the physics the tuning table claims. local CAL = { gByKind = {}, gCount = {}, gSum = {}, leadByWeapon = {}, leadCount = {}, pending = {}, samples = 0, residuals = {}, lastG = nil, lastT = nil, dirMismatch = 0, originMismatch = 0, originChecked = 0, originDelta = nil, } STATE.cal = CAL STATE.median = function(list) if #list == 0 then return nil end local copy = table.clone(list) table.sort(copy) local mid = math.floor(#copy * 0.5) + 1 return copy[mid] end -- Solve flight time and gravity from origin + launch velocity + impact point. local function solveTrajectory(origin, velocity, impact) local t local vxz = math.sqrt(velocity.X * velocity.X + velocity.Z * velocity.Z) -- weak horizontal motion makes the flight time ill conditioned if vxz < 15 then return nil end -- least squares over the two linear (gravity-free) axes local tx, tz if math.abs(velocity.X) > 0.01 then tx = (impact.X - origin.X) / velocity.X end if math.abs(velocity.Z) > 0.01 then tz = (impact.Z - origin.Z) / velocity.Z end if tx and tz then if math.abs(tx - tz) > 0.05 * math.max(tx, tz) then return nil end t = (tx + tz) * 0.5 else t = tx or tz end if not t or t <= 0.02 or t > 2.5 then return nil end local g = 2 * (origin.Y + velocity.Y * t - impact.Y) / (t * t) if g < 0 or g > 160 then return nil end -- residual of the full parabola against the impact point local predicted = origin + velocity * t + Vector3.new(0, -0.5 * g * t * t, 0) return t, g, (predicted - impact).Magnitude end -- one round trip behind the server; half of it is the one-way view delay oneWayLatency = function() local ok, ping = pcall(function() return LP:GetNetworkPing() end) if ok and type(ping) == "number" and ping > 0 then return math.clamp(ping * 0.5, 0, 0.15) end return 0 end -- ------------------------------------------------------------------ target state -- Current target state from the tracking filter: position advanced to now and -- velocity. The root samples are filtered, so this is a smoothed estimate rather -- than the noisy instantaneous position. local function targetState(model, root) return root.Position, root.AssemblyLinearVelocity, nil end -- ------------------------------------------------------------------ ballistic constants -- Solve the server's ACTUAL launch speed from a single impact. We know the origin -- and unit direction we sent (O, u) and the server reports where the arrow hit (P). -- Along the horizontal axes gravity is irrelevant, so R = |P_xz - O_xz| / |u_xz| is -- the arc-length parameter, and the vertical axis then gives the speed in closed -- form: s^2 = 0.5 g R^2 / (O.y + u.y R - P.y). No fitting, no guessing. local function solveLaunchSpeed(origin, direction, impact, gravity) local u = direction local horizontalUnit = math.sqrt(u.X * u.X + u.Z * u.Z) if horizontalUnit < 1e-3 then return nil end local dx, dz = impact.X - origin.X, impact.Z - origin.Z local horizontal = math.sqrt(dx * dx + dz * dz) if horizontal < 1 then return nil end local R = horizontal / horizontalUnit local denominator = origin.Y + u.Y * R - impact.Y if denominator <= 0.05 then return nil end local speedSquared = 0.5 * gravity * R * R / denominator if speedSquared <= 0 then return nil end local speed = math.sqrt(speedSquared) if speed < 20 or speed > 2000 then return nil end return speed end -- ------------------------------------------------------------------ target lock -- Selection used to happen only on a shot, so the highlight appeared after -- firing. This resolves the best target continuously, keeps it while it stays in -- the cone, and hands it to both the highlight and the shot. local lock = { model = nil, solution = nil, at = 0 } STATE.lock = lock local LOCK_INTERVAL = 0.08 -- The muzzle, exactly where the game will launch from. The solve has to start at -- the same point the payload reports or the redirect is refused, so nothing here -- may move the origin: the anti-aim displaces the body, and this reads it back. local function aimOrigin(tool) return Util.ShotOrigin(tool, "Muzzle") end local function toolTuning() local char = LP.Character local tool = char and char:FindFirstChildWhichIsA("Tool") local tuning = tool and shotParams(tool) if not tuning then return nil end return tool, tuning end local function updateLock() if closed then return end if not CONFIG.enabled then lock.model, lock.solution = nil, nil return end -- with no prediction the lock needs no weapon: the muzzle when one is held, the -- camera otherwise, so the highlight and the target hud follow the circle at all times local char = LP.Character local tool = char and char:FindFirstChildWhichIsA("Tool") local origin if tool and shotParams(tool) then origin = aimOrigin(tool) else origin = Camera.CFrame.Position + Camera.CFrame.LookVector * 2 end local keep = lock.model and lock.model.Parent and lock.model or nil local solution = selectTarget(origin, keep) if solution then if lock.model ~= solution.model then lock.since = os.clock() end lock.model, lock.solution, lock.at = solution.model, solution, os.clock() STATE.lockSince = lock.since else lock.solution = nil -- Keep the acquisition clock across a brief flicker (a target stepping -- behind a post for a frame), but drop it if the gap is real. if not lock.at or os.clock() - lock.at > 0.5 then lock.model, lock.since = nil, nil STATE.lockSince = nil end end end task.spawn(function() while not closed do local ok, err = pcall(updateLock) if not ok then STATE.lastError = "lock: " .. tostring(err) end task.wait(LOCK_INTERVAL) end end) -- ------------------------------------------------------------------ tuning persistence -- The learned lead is the one thing that takes shots to rediscover, and a new -- match re-injects this script. Persist it to the executor's workspace so every -- match starts with the value the hit rate already converged on. local TUNE_FILE = "tribes_overlay_tuning.txt" local function loadTuning() if type(readfile) ~= "function" then return end local ok, data = pcall(readfile, TUNE_FILE) if not ok or type(data) ~= "string" then return end for key, value in data:gmatch("([%w_]+)=([%-%d%.]+)") do local number = tonumber(value) if number then if key == "leadTrim" then CONFIG.delayComp = number end if key == "viewDelay" then CONFIG.viewDelay = number end end end end local function saveTuning() if type(writefile) ~= "function" then return end pcall(writefile, TUNE_FILE, string.format("leadTrim=%.4f\nviewDelay=%.4f\n", CONFIG.delayComp or 0, CONFIG.viewDelay or 0)) end loadTuning() -- ------------------------------------------------------------------ aim hooks local originalProjectile = Effects.Projectile local projectileHook projectileHook = function(key, kind, origin, velocity, gravity, shooter, prediction) if not closed and CONFIG.enabled and shooter == LP.Character then local ok, err = pcall(function() local tool = shooter:FindFirstChildWhichIsA("Tool") local tuning = shotParams(tool) if not tuning then return end STATE.shotsSeen = (STATE.shotsSeen or 0) + 1 local speed = velocity.Magnitude -- this argument is the launch point the game just built and is about to -- put in the payload, so solving from it is what makes the redirect be -- accepted and the trajectory start where the server starts local stableOrigin = typeof(origin) == "Vector3" and origin or aimOrigin(tool) local kind = tuning.projectile or "Arrow" STATE.aimWeaponId = tool:GetAttribute("WeaponId") local locked = (lock.model and lock.model.Parent) and lock.model or nil local solution = planShot(stableOrigin, speed, gravity, locked) if solution then applyWallbang(stableOrigin, solution, speed, gravity) end if type(key) == "string" then local rec = CAL.pending[key] or {} rec.kind = kind rec.mine = true rec.weaponId = STATE.aimWeaponId rec.at = os.clock() -- recorded for every shot, not just solved ones: the tracer needs a -- launch point on any shot that lands, including ones with no target -- locked, and it used to be set only inside the branch below rec.fireOrigin = stableOrigin logEvent("shot", string.format("key=%s origin=%s", tostring(key), string.format("%.0f,%.0f,%.0f", stableOrigin.X, stableOrigin.Y, stableOrigin.Z))) if solution then rec.model = solution.model local tRoot = solution.model:FindFirstChild("HumanoidRootPart") rec.fireTargetPos = tRoot and tRoot.Position or nil local myRoot = LP.Character and LP.Character:FindFirstChild("HumanoidRootPart") local myHum = LP.Character and LP.Character:FindFirstChildOfClass("Humanoid") rec.mySpeed = myRoot and myRoot.AssemblyLinearVelocity.Magnitude or 0 rec.myMoving = myHum and myHum.MoveDirection.Magnitude > 0.1 or false rec.sentDir = solution.direction rec.sentOrigin = solution.origin or stableOrigin rec.assumedGravity = gravity end CAL.pending[key] = rec end pending = { at = os.clock(), tool = tool, origin = stableOrigin, rawOrigin = origin, speed = speed, solution = solution } STATE.lastSolution = solution if type(key) == "string" then STATE.myKeys = STATE.myKeys or {} STATE.myKeys[key] = os.clock() for k, t in pairs(STATE.myKeys) do if os.clock() - t > 8 then STATE.myKeys[k] = nil end end end if solution then velocity = solution.direction * speed local root = LP.Character and LP.Character:FindFirstChild("HumanoidRootPart") local vsRoot = root and math.deg(math.acos(math.clamp( solution.direction:Dot(root.CFrame.LookVector), -1, 1))) or -1 logEvent("aim", string.format( "-> %s angle=%.1f distance=%.0f speed=%.0f vsRoot=%.1f part=%s aimErr=%.2f\u{00B0}%s", solution.model.Name, solution.angle or 0, solution.distance or 0, speed, vsRoot, tostring(solution.firstPart), solution.latErr or 0, (solution.originShift or 0) > 0.01 and string.format(" origin moved %.1f studs", solution.originShift) or "")) else logEvent("aim", "no solution: " .. tostring(STATE.selectDebug and STATE.selectDebug.reason)) end end) if not ok then STATE.lastError = "aim: " .. tostring(err) pending = nil end end return originalProjectile(key, kind, origin, velocity, gravity, shooter, prediction) end Effects.Projectile = projectileHook -- Blunderbuss: the spread pattern is generated client-side from a unit direction, -- so redirect the unit vector and let the game's own spread maths follow it. local originalPellets = Effects.Pellets local pelletsHook pelletsHook = function(key, kind, origin, dir, speed, gravity, count, spread, seed, shooter, prediction) if not closed and CONFIG.enabled and shooter == LP.Character then local ok, err = pcall(function() local tool = shooter:FindFirstChildWhichIsA("Tool") local tuning = shotParams(tool) if not tuning then return end local solution = planShot(origin, speed, gravity) if solution then applyWallbang(origin, solution, speed, gravity) end pending = { at = os.clock(), tool = tool, origin = origin, rawOrigin = origin, speed = speed, solution = solution } STATE.lastSolution = solution if solution then dir = solution.direction logEvent("aim", string.format("-> %s (pellet spread) angle=%.1f distance=%.0f speed=%.0f", solution.model.Name, solution.angle or 0, solution.distance or 0, speed)) end end) if not ok then STATE.lastError = "pellets: " .. tostring(err) end end return originalPellets(key, kind, origin, dir, speed, gravity, count, spread, seed, shooter, prediction) end if type(originalPellets) == "function" then Effects.Pellets = pelletsHook end -- Keep this hook to plain table writes. Any :method() call inside a __namecall -- hook re-enters the metamethod on the same thread and Delta loses the pending -- namecall method, so the original FireServer dispatches under the wrong name. local originalNamecall local namecallHook = newcclosure(function(self, ...) if not closed and CONFIG.enabled and self == WeaponAction and getnamecallmethod() == "FireServer" then local action, payload = ... if action == "Shoot" and type(payload) == "table" and pending then local sol = pending.solution local sameOrigin = typeof(payload.origin) == "Vector3" and (payload.origin - pending.origin).Magnitude < 0.01 if sol and pending.tool == payload.tool and sameOrigin and os.clock() - pending.at < 0.25 then -- The launch point is pinned to the one the game built, always. A moved launch -- point was landing shots left and right of stationary targets: the server -- fires the direction we send from where it thinks we are, so a direction -- solved from a relocated point is simply wrong. Leaving it alone is what -- makes the aim exact rather than approximate. if (CONFIG.originProbe or 0) > 0 and sol.origin then -- probe shot: let the offset through and say what it should look like payload.origin = sol.origin logEvent("probe", string.format( "launch point sent %.1f studs above the muzzle - a hit means the server used it, a miss of about that much means it did not", (sol.origin - pending.origin).Magnitude)) STATE.probeLeft = (STATE.probeLeft or 1) - 1 if STATE.probeLeft <= 0 then CONFIG.originProbe = 0 logEvent("probe", "probe finished, launch point back to the muzzle") end elseif pending.origin and (payload.origin - pending.origin).Magnitude > 0.001 then payload.origin = pending.origin end payload.dir = sol.direction STATE.redirected += 1 STATE.aimActive = true STATE.lastShot = { id = payload.id, charge = payload.charge, speed = pending.speed, target = sol.model, part = sol.firstPart, direction = payload.dir, origin = payload.origin, wallband = sol.wallband, mode = CONFIG.mode, angle = sol.angle, distance = sol.distance } else STATE.aimActive = false if sol then STATE.payloadMiss = (STATE.payloadMiss or 0) + 1 STATE.payloadMissReason = string.format( "toolMatch=%s originMatch=%s ageMs=%.0f", tostring(pending.tool == payload.tool), tostring(sameOrigin), (os.clock() - (pending.at or 0)) * 1000) end end end pending = nil end return originalNamecall(self, ...) end) originalNamecall = hookmetamethod(game, "__namecall", namecallHook) -- The game sends FX over two channels: CombatFX (reliable) and CombatFXU (unreliable), -- and both feed the same handler in the game's own code. High-frequency events - swings -- above all - arrive on the unreliable one, so listening to CombatFX alone silently -- missed every melee swing and probably a share of the shots as well. local function onCombatFXEvent(action, data) -- tally every action the server sends us: the handlers below are only as good as -- the events that actually arrive STATE.fxTally = STATE.fxTally or {} STATE.fxTally[tostring(action)] = (STATE.fxTally[tostring(action)] or 0) + 1 if type(data) ~= "table" then return end if action == "Charge" and CONFIG.dodge then local char = LP.Character local root = char and char:FindFirstChild("HumanoidRootPart") local attacker = data.attacker local theirChar = typeof(attacker) == "Instance" and ( attacker:IsA("Player") and attacker.Character or (attacker:IsA("Model") and attacker)) or nil if root and theirChar and theirChar ~= char and data.on then local them = theirChar:FindFirstChild("HumanoidRootPart") -- Draw (bow), Throw (thrown weapon), Heavy/Lunge (melee charge): all of -- them are windups, and all of them are broadcast before the attack lands. if CONFIG.jitterDodge and them and (them.Position - root.Position).Magnitude <= (CONFIG.dodgeRange or 55) then -- Jitter: a draw means a shot is coming, so the body shakes side to side for -- the whole draw-to-landing window instead of sitting still for any of it. -- There is nothing to time and nothing to predict, because no position lasts -- longer than the interval - there is no position to aim at. local axis local flat = Vector3.new(them.Position.X - root.Position.X, 0, them.Position.Z - root.Position.Z) if flat.Magnitude > 0.5 then axis = Vector3.new(-flat.Unit.Z, 0, flat.Unit.X) else local a = hvRandom:NextNumber(0, math.pi * 2) axis = Vector3.new(math.cos(a), 0, math.sin(a)) end -- measure the room once so the shake stays inside the map local params = Util.WorldParams(Util.Teammates()) local reach = math.max(CONFIG.jitterMin or 3, CONFIG.jitterMax or 15) local each = reach if workspace:Raycast(root.Position, axis * (reach + 1), params) then each = reach * 0.5 end STATE.jitterAcross = axis STATE.jitterReach = math.max(each, 1) if not STATE.jitterOffset then -- first draw of this window: start the shake fresh STATE.jitterSide = nil STATE.jitterNext = 0 STATE.jitterStarted = os.clock() STATE.jitters = (STATE.jitters or 0) + 1 end -- A window runs until the shot lands, and another draw inside it extends it -- rather than restarting it: they are still drawing, so the shot is still -- coming, and the body should not stop shaking in between. The timer here is -- only a backstop for a draw that never turns into anything. STATE.jitterUntil = math.max(STATE.jitterUntil or 0, os.clock() + 3) if not STATE.jitterLogged or os.clock() - STATE.jitterLogged > 1 then STATE.jitterLogged = os.clock() logEvent("jitter", string.format("%s drew: shaking %.0f-%.0f studs every %.0fms until their shot lands", tostring(data.weaponId or "weapon"), CONFIG.jitterMin or 3, math.min(CONFIG.jitterMax or 15, STATE.jitterReach or 15), (CONFIG.jitterInterval or 0.01) * 1000)) end end if them and (them.Position - root.Position).Magnitude <= (CONFIG.dodgeRange or 55) then local tuning = data.weaponId and Weapons.Get(data.weaponId) local windup = tuning and tuning.tuning and ( tuning.tuning.drawTime or tuning.tuning.windupTime) windup = tonumber(windup) or 0.35 -- the windup is only half of it: the shot still has to travel, and the -- weapon table says how fast local weapon = CONFIG.dodgeSmart and STATE.weaponFor(theirChar, nil, data.weaponId) or nil if CONFIG.dodgeSmart then -- Flick before the shot, and again once it is in the air. The flick on the -- draw puts the body somewhere other than where their aim is settling, so -- the shot is released against a position that is already wrong; the move -- in flight, timed off the shot's own arc, is the one they cannot answer, -- because a projectile's path is fixed the moment it leaves. Two positions -- per shot, and neither of them is where they were looking. -- -- The hold here only has to reach the release. Once the shot is out its -- arc says exactly how long the next displacement needs to last, and the -- hold is re-cut to that. -- The flick's length is the flight time. That is the whole rule: the body -- has to be somewhere else for as long as the shot is in the air, and it -- has to still be somewhere else when the shot lands - so the hold is the -- draw that is still to come plus the flight that follows it. Flight comes -- from the weapon's real launch speed and the distance to the shooter, -- both of which are right here. local lived = weapon and weapon.speed or 320 local flight = (them.Position - root.Position).Magnitude / math.max(lived, 1) STATE.dodgeFlight = flight local hold = math.min(windup + flight + (STATE.netRtt and STATE.netRtt() or 0) + 0.25, 2) STATE.dodgeSmartHold = hold STATE.dodgeForce = true STATE.armDodge(nil, them.Position) if not STATE.smartLogged or os.clock() - STATE.smartLogged > 1 then STATE.smartLogged = os.clock() logEvent("smart", string.format( "%s drawn (%.0fms windup, %.0f studs/s): flicking before the shot, held %.0fms to the release%s", tostring(weapon and weapon.id or "weapon"), windup * 1000, weapon and weapon.speed or 0, hold * 1000, weapon and weapon.cooldown > 0 and weapon.cooldown <= 0.5 and string.format(" (cadence %.0fms, re-flicks per shot)", weapon.cooldown * 1000) or "")) end else STATE.pendingDodge = { at = os.clock() + math.max(0.06, windup - 0.12), position = them.Position, } end -- Melee is validated inside a vertical window around the attacker's aim, -- and past eight studs of height difference the swing does not connect at -- all. A lift on the windup is therefore a miss by rule rather than by luck, -- and it beats a sidestep for the same reason: they are checking distance -- and arc, and the arc does not reach above them. Their own client builds -- that hit list from its delayed copy of our position, which is what makes -- reacting to the windup in time possible. Skipped while our own melee is -- in use, because that same window is what our swing needs. if CONFIG.meleeHop and not CONFIG.killAura then local kind = tuning and tuning.kind local melee = kind == "Sword" or kind == "Axe" or kind == "Spear" or kind == "Dagger" or kind == "Club" or kind == "Shield" if melee and (them.Position - root.Position).Magnitude <= 14 then local height = CONFIG.meleeHopHeight or 10 local params = Util.WorldParams(Util.Teammates()) -- take what headroom there is, but never less than the window needs if workspace:Raycast(root.Position, Vector3.new(0, height + 1, 0), params) then height = height * 0.5 end if height >= 8.5 then STATE.meleeOffset = Vector3.new(0, height, 0) STATE.meleeSince = os.clock() STATE.meleeUntil = os.clock() + math.max(0.4, windup + 0.3) STATE.hops = (STATE.hops or 0) + 1 logEvent("hop", string.format("%s windup %.0fms: lifting %.0f studs out of the melee window", tostring(kind), windup * 1000, height)) end end end end elseif data.on == false then -- the draw ended. If it was released, the projectile broadcast lands within a -- frame or two and re-times the hold to the real travel; if it was cancelled, -- nothing is coming and the displacement should stop being paid for. -- a scheduled move against a shot in the air is not cancelled by a draw ending if STATE.pendingDodge and not STATE.pendingDodge.smart then STATE.pendingDodge = nil end -- Deliberately NOT shortening the hold here. A charge dropping means the draw -- ended, and for a bow it ends because it was RELEASED - the arrow is out. Cutting -- the hold at that moment puts the body back on the base position in the window -- between the release and the projectile broadcast, which is where the shot lands. -- The hold was sized as draw plus flight, so it already covers everything that is -- coming; the release broadcast re-times it when it arrives. end end if action == "Denied" then STATE.lastDenial = { reason = data.reason, id = data.id, at = os.clock() } logEvent("denied", tostring(data.reason)) elseif action == "Projectile" and (data.shooter == LP or data.shooter == LP.Character) then -- The server rebroadcast of our own shot: compare with what we sent. This is the -- branch our own shots land in (the game's own handler drops its echo, this one does -- not), so both checks below belong here as well as on the incoming path: if the -- server echoes a different launch point than the one we handed it, the origin is -- being rebuilt server side and a wallbang can never land. local v = data.velocity if typeof(v) == "Vector3" then STATE.serverShot = { direction = v.Unit, speed = v.Magnitude, origin = data.origin, gravity = data.gravity, kind = data.kind, at = os.clock() } logEvent("server", string.format("speed=%.1f dir=%.3f,%.3f,%.3f", v.Magnitude, v.Unit.X, v.Unit.Y, v.Unit.Z)) -- what we asked it to launch from, against what it says it launched from: if -- these differ, the origin is being rebuilt server side and any shot whose -- direction was solved from a moved origin is aimed somewhere else entirely local sent = STATE.lastShot and STATE.lastShot.origin if sent and typeof(data.origin) == "Vector3" then local off = (sent - data.origin).Magnitude CAL.originDelta = off CAL.originChecked = (CAL.originChecked or 0) + 1 if off > 0.5 then CAL.originMismatch = (CAL.originMismatch or 0) + 1 end logEvent("origin", string.format("sent %.1f,%.1f,%.1f -> server %.1f,%.1f,%.1f (off %.2f)", sent.X, sent.Y, sent.Z, data.origin.X, data.origin.Y, data.origin.Z, off)) end local rec = type(data.key) == "string" and CAL.pending[data.key] or nil if rec then if rec.sentOrigin and typeof(data.origin) == "Vector3" then local off = (rec.sentOrigin - data.origin).Magnitude CAL.originDelta = off CAL.originChecked = (CAL.originChecked or 0) + 1 if off > 0.5 then CAL.originMismatch = (CAL.originMismatch or 0) + 1 end end if rec.sentDir then local angle = math.deg(math.acos(math.clamp(rec.sentDir:Dot(v.Unit), -1, 1))) CAL.dirAngle = angle if angle > 2 then CAL.dirMismatch = (CAL.dirMismatch or 0) + 1 end end end end elseif action == "Projectile" then -- An arrow we can see coming: work out when it will reach us and step the -- hitbox at that moment. Their aim was committed at release, so moving on -- arrival is what turns a correct shot into an incorrect one. if CONFIG.dodge and data.shooter ~= LP and data.shooter ~= LP.Character then local char = LP.Character local root = char and char:FindFirstChild("HumanoidRootPart") if root and typeof(data.origin) == "Vector3" and typeof(data.velocity) == "Vector3" then local shooterChar = typeof(data.shooter) == "Instance" and ( data.shooter:IsA("Player") and data.shooter.Character or (data.shooter:IsA("Model") and data.shooter)) or nil local shooterRoot = shooterChar and shooterChar:FindFirstChild("HumanoidRootPart") local near = shooterRoot and (shooterRoot.Position - root.Position).Magnitude or (data.origin - root.Position).Magnitude -- fire close to us and we move, regardless of where it was aimed: the -- point is to be somewhere else before it gets here if near > (CONFIG.dodgeRange or 55) then -- worth saying out loud: a shot ignored by the react range looks exactly -- like a dodge that failed if not STATE.rangeLogged or os.clock() - STATE.rangeLogged > 2 then STATE.rangeLogged = os.clock() logEvent("smart", string.format( "%s fired from %.0f studs, outside the %.0f-stud react range - ignored", tostring(data.kind), near, CONFIG.dodgeRange or 55)) end end if near <= (CONFIG.dodgeRange or 55) then -- the launch point, so the dodge has the shot's own line to work from local aimAt = data.origin -- The manual dodge always happens. Smart is only ever allowed to change -- how long the flick is held and which side it takes - it can never be the -- reason no flick happened. So it runs first, inside a guard, and the flick -- is taken afterwards regardless of how that went: a throw up there would -- otherwise kill the whole handler and silently disable dodging, which is -- precisely how smart ended up worse than the manual dodge it was meant to -- improve. local hit, impactIn, miss, aimedAtBase, flight, weapon, rapid, offText if CONFIG.dodgeSmart then local ok, err = pcall(function() local rtt = STATE.netRtt and STATE.netRtt() or 0 local oneWay = rtt * 0.5 local offset = STATE.appliedOffset or Vector3.new(0, 0, 0) local gravity = tonumber(data.gravity) or 0 local base = root.Position - offset flight = (root.Position - data.origin).Magnitude / math.max(data.velocity.Magnitude, 1) weapon = STATE.weaponFor(shooterChar, data.kind) rapid = weapon and weapon.cooldown > 0 and weapon.cooldown <= 0.5 or false -- where the shot is headed, from its own arc, against both places the -- body can be: the displaced one it holds now, and the base it -- returns to when the hold ends local arcHit, impactAt, arcMiss = STATE.shotImpact(data.origin, data.velocity, gravity, oneWay, root.Position, 2.4) local baseHit, baseImpact, baseMiss = STATE.shotImpact(data.origin, data.velocity, gravity, oneWay, base, 2.4) hit, impactIn, miss = arcHit, arcHit and (impactAt - oneWay) or nil, arcMiss if not arcHit and baseHit then hit, impactIn, miss, aimedAtBase = true, baseImpact - oneWay, baseMiss, true end -- hold = the flight: displaced from this flick until the shot has -- passed where it could land, and never less than the manual hold, -- so smart can only extend a flick, never shorten one local led = math.max(impactIn or 0, flight) STATE.dodgeFlight = led STATE.dodgeSmartHold = math.max(CONFIG.dodgeHold or 0.4, led + 0.15) STATE.dodgeForce = true -- and the geometry, in the log: how far off the shot's own line the -- body was and how far off the flick leaves it local vflat = Vector3.new(data.velocity.X, 0, data.velocity.Z) if vflat.Magnitude > 0.5 then local perp = Vector3.new(-vflat.Unit.Z, 0, vflat.Unit.X) local wasOff = math.abs((root.Position - data.origin):Dot(perp)) local willBe = math.abs((base + (STATE.dodgeOffset or Vector3.new(0, 0, 0)) - data.origin):Dot(perp)) STATE.offBefore, STATE.offAfter = wasOff, willBe offText = string.format(" | off the shot's line %.1f -> %.1f studs", wasOff, willBe) end end) if not ok then STATE.smartError = tostring(err) STATE.dodgeSmartHold = nil if not STATE.smartErrorLogged or os.clock() - STATE.smartErrorLogged > 2 then STATE.smartErrorLogged = os.clock() logEvent("smart", "smart dodge failed (" .. tostring(err):sub(1, 80) .. ") - taking the manual dodge instead") end end end STATE.armDodge(data.velocity, aimAt) if CONFIG.dodgeSmart and hit ~= nil then local label = tostring(weapon and weapon.id or data.kind) if not STATE.smartLogged or os.clock() - STATE.smartLogged > 1 then STATE.smartLogged = os.clock() logEvent("smart", string.format( "%s %s out: flight %.0fms, %s, holding %.0fms%s%s", label, tostring(data.kind), (flight or 0) * 1000, hit and string.format("arc reaches us in %.0fms%s", impactIn * 1000, aimedAtBase and " (at the base position)" or "") or string.format("arc passes %.1f clear", miss or 0), (STATE.dodgeSmartHold or 0) * 1000, rapid and " (rapid: fresh side per shot)" or "", offText or "")) end end -- Every prediction is kept for a moment afterwards, so when a hit lands -- there is something to check it against: whether the arc called it, and -- whether the body was moved before it arrived. Without that a hit is -- just a hit, and there is no way to tell a model that was wrong from a -- model that was right and a step that came too late. if CONFIG.dodgeSmart then STATE.shotPredictions = STATE.shotPredictions or {} STATE.shotPredictions[#STATE.shotPredictions + 1] = { at = os.clock(), kind = tostring(data.kind), weapon = tostring((STATE.weaponFor(shooterChar, data.kind) or {}).id or data.kind), hit = hit, impactIn = impactIn, dist = near, atUs = root.Position, shooter = shooterChar and shooterChar.Name or "?", } STATE.shotsAtUs = (STATE.shotsAtUs or 0) + 1 CAL.shotsFor = CAL.shotsFor or {} local who = shooterChar and shooterChar.Name or "?" CAL.shotsFor[who] = (CAL.shotsFor[who] or 0) + 1 while #STATE.shotPredictions > 12 do table.remove(STATE.shotPredictions, 1) end end end end end if typeof(data.origin) == "Vector3" and typeof(data.velocity) == "Vector3" and type(data.key) == "string" then local existing = CAL.pending[data.key] local rec = existing or {} rec.origin, rec.velocity = data.origin, data.velocity rec.gravity = tonumber(data.gravity) or 0 rec.kind = data.kind or rec.kind rec.at = os.clock() rec.mine = (data.shooter == LP or data.shooter == LP.Character) -- does the server reuse the direction we redirected to? if rec.sentDir then local angle = math.deg(math.acos(math.clamp(rec.sentDir:Dot(data.velocity.Unit), -1, 1))) if angle > 2 then CAL.dirMismatch = (CAL.dirMismatch or 0) + 1 end end if rec.sentOrigin and typeof(data.origin) == "Vector3" then local off = (rec.sentOrigin - data.origin).Magnitude CAL.originDelta = off CAL.originChecked = (CAL.originChecked or 0) + 1 if off > 0.5 then CAL.originMismatch = (CAL.originMismatch or 0) + 1 end end CAL.pending[data.key] = rec for key, rec in pairs(CAL.pending) do if os.clock() - rec.at > 10 then CAL.pending[key] = nil end end end elseif action == "ProjectileHit" then -- A shake covers the window from their draw to their shot landing, so the landing is -- where it ends. This lives inside the impact branch that already exists: an extra -- branch of my own ahead of this one in the same chain would match first and make -- everything below it - tracing, impact markers, the impact accounting - unreachable. if STATE.jitterUntil and os.clock() < STATE.jitterUntil then STATE.jitterUntil = os.clock() if not STATE.jitterStopLogged or os.clock() - STATE.jitterStopLogged > 1 then STATE.jitterStopLogged = os.clock() logEvent("jitter", string.format("their shot landed: shake ended after %.0fms, %d positions", (os.clock() - (STATE.jitterStarted or os.clock())) * 1000, STATE.jitterTicks or 0)) end end -- one shot can report several impacts (the server emits "id/1", "id/2", ...) local hitKey = data.key if type(hitKey) == "string" then local base = hitKey:match("^(.-)/%d+$") if base then hitKey = base end end local rec = hitKey and CAL.pending[hitKey] local position = typeof(data.position) == "Vector3" and data.position or nil STATE.hitEvents = (STATE.hitEvents or 0) + 1 if not rec then STATE.hitsNoRecord = (STATE.hitsNoRecord or 0) + 1 end if not position then -- an impact with no position cannot be traced or marked; keep the event -- itself so the fields the server did send can be inspected STATE.hitsNoPos = (STATE.hitsNoPos or 0) + 1 STATE.lastNoPos = data end -- one hit can arrive as several impacts; a tracer or marker per impact would -- stack them on top of each other, so only the first of a key is drawn if position then local mine = (hitKey and STATE.myKeys and STATE.myKeys[hitKey]) and true or (rec and rec.mine) or false if mine then STATE.ourHits = (STATE.ourHits or 0) + 1 end local wanted = mine and VIS.tracers or (VIS.tracers and VIS.tracersEnemy) logEvent("fx", string.format("hit key=%s mine=%s rec=%s fireOrigin=%s origin=%s wanted=%s", tostring(hitKey), tostring(mine), tostring(rec ~= nil), tostring(rec and rec.fireOrigin and "yes" or "no"), tostring(rec and rec.origin and "yes" or "no"), tostring(wanted))) if not wanted then STATE.tracerSkipped = (STATE.tracerSkipped or 0) + 1 end if wanted then local from = rec and (rec.fireOrigin or rec.origin) if typeof(from) ~= "Vector3" then STATE.tracerNoOrigin = (STATE.tracerNoOrigin or 0) + 1 end local tracerFn = STATE.fxTracer if tracerFn then local ok, err = pcall(tracerFn, from, position, mine and VIS.tracerColor or VIS.tracerColorEnemy, VIS.tracerLife, VIS.tracerThickness) if not ok then logEvent("fx", "tracer error: " .. tostring(err)) end end end if VIS.markers and (mine or VIS.tracersEnemy) then local hitPlayer = data.character ~= nil and typeof(data.character) == "Instance" local markerFn = STATE.fxMarker if markerFn then local ok, err = pcall(markerFn, position, hitPlayer and VIS.markerColorPlayer or VIS.markerColorWorld, VIS.markerSize, VIS.markerLife) if not ok then logEvent("fx", "marker error: " .. tostring(err)) end end end end -- 1. measured physics. Needs the launch state the server broadcast, and a -- flight long enough that the collision-step overshoot is negligible: -- fast bolts read a 36 studs/s^2 gravity against a claimed 28, slow -- arrows read 55.5 against 55, so only the latter is trustworthy. if rec and rec.origin and rec.velocity and position then local t, g, residual = solveTrajectory(rec.origin, rec.velocity, position) if t and t >= 0.3 and g then CAL.samples = CAL.samples + 1 CAL.lastG, CAL.lastT = g, t local kind = rec.kind or "?" -- real launch speed from the server's own broadcast if rec.velocity then CAL.speedSamples = CAL.speedSamples or {} local sp = CAL.speedSamples[kind] or {} sp[#sp + 1] = rec.velocity.Magnitude if #sp > 80 then table.remove(sp, 1) end CAL.speedSamples[kind] = sp end CAL.gCount[kind] = (CAL.gCount[kind] or 0) + 1 CAL.residuals[kind] = CAL.residuals[kind] or {} local list = CAL.residuals[kind] list[#list + 1] = residual if #list > 60 then table.remove(list, 1) end local gs = CAL.gSamples and CAL.gSamples[kind] if not CAL.gSamples then CAL.gSamples = {} end gs = CAL.gSamples[kind] or {} gs[#gs + 1] = g if #gs > 60 then table.remove(gs, 1) end CAL.gSamples[kind] = gs CAL.gClaimed = CAL.gClaimed or {} local claimed = CAL.gClaimed[kind] or {} claimed[#claimed + 1] = rec.gravity or 0 if #claimed > 60 then table.remove(claimed, 1) end CAL.gClaimed[kind] = claimed if #gs >= 12 then CAL.gClaimedMedian = CAL.gClaimedMedian or {} CAL.gClaimedMedian[kind] = STATE.median(claimed) local spread = table.clone(gs) table.sort(spread) local lo = spread[math.max(1, math.floor(#spread * 0.25))] local hi = spread[math.min(#spread, math.floor(#spread * 0.75) + 1)] local mid = STATE.median(gs) CAL.gSpread = CAL.gSpread or {} CAL.gSpread[kind] = mid > 0 and (hi - lo) / mid or 1 CAL.gByKind[kind] = (CAL.gSpread[kind] <= 0.15) and mid or nil end end end -- 2. own-shot quality, scored once per shot if rec and not rec.scored and position then rec.scored = true -- solve the server's actual launch speed from this impact if rec.sentDir and rec.sentOrigin and rec.mine then local solved = solveLaunchSpeed(rec.sentOrigin, rec.sentDir, position, rec.assumedGravity or (rec.gravity or 0)) if solved then CAL.solvedSpeed = CAL.solvedSpeed or {} local list = CAL.solvedSpeed[rec.kind or "?"] or {} list[#list + 1] = solved if #list > 100 then table.remove(list, 1) end CAL.solvedSpeed[rec.kind or "?"] = list logEvent("speed", string.format("solved launch speed %.1f (assumed %.1f) from a %.1f stud shot", solved, rec.weaponSpeed or -1, (position - rec.sentOrigin).Magnitude)) end end -- prediction error, independent of the hitbox: how far the target was -- from where the solver said it would be at impact, and which way if false then local tRoot = rec.model:FindFirstChild("HumanoidRootPart") if tRoot then local err = tRoot.Position - rec.predictAt local signed = rec.velDir and err:Dot(rec.velDir) or 0 CAL.predSum = (CAL.predSum or 0) + err.Magnitude CAL.predAlongSum = (CAL.predAlongSum or 0) + signed CAL.predCount = (CAL.predCount or 0) + 1 -- Landing offset only counts when the arrow actually reached -- the target. Arrows that stopped on a wall say nothing about -- the prediction, they just make the average look over-led. -- Three outcomes, not two: hitting a DIFFERENT player means the -- shot connected, it just had someone else in the way. local hitChar = data.character local isTarget = (hitChar == rec.model) local isOther = (not isTarget) and hitChar ~= nil and typeof(hitChar) == "Instance" local landed = isTarget if landed then local off = (position - rec.predictAt) local advance = rec.velDir and off:Dot(rec.velDir) or 0 local lateral = rec.velDir and (off - rec.velDir * advance).Magnitude or off.Magnitude -- We sample the target when the impact event reaches us, so -- it has kept moving for one-way latency since the hit. local delay = oneWayLatency() + (CONFIG.viewDelay or 0) local speed = rec.targetSpeed or 0 local bias = advance - speed * delay CAL.latSum = (CAL.latSum or 0) + lateral CAL.latCount = (CAL.latCount or 0) + 1 if rec.myMoving then CAL.latMoveSum = (CAL.latMoveSum or 0) + lateral CAL.latMoveCount = (CAL.latMoveCount or 0) + 1 else CAL.latStillSum = (CAL.latStillSum or 0) + lateral CAL.latStillCount = (CAL.latStillCount or 0) + 1 end CAL.hitSum = (CAL.hitSum or 0) + bias CAL.hitCount = (CAL.hitCount or 0) + 1 CAL.recent = CAL.recent or {} CAL.recent[#CAL.recent + 1] = 1 if #CAL.recent > 20 then table.remove(CAL.recent, 1) end -- does our own movement degrade the shot? split the landing -- error by whether the shooter was moving when it fired if rec.myMoving then CAL.movingSum = (CAL.movingSum or 0) + bias CAL.movingCount = (CAL.movingCount or 0) + 1 else CAL.stillSum = (CAL.stillSum or 0) + bias CAL.stillCount = (CAL.stillCount or 0) + 1 end CAL.hitAbsSum = (CAL.hitAbsSum or 0) + off.Magnitude else CAL.missSum = (CAL.missSum or 0) + signed CAL.missCount = (CAL.missCount or 0) + 1 CAL.recent = CAL.recent or {} CAL.recent[#CAL.recent + 1] = isOther and 1 or 0 if #CAL.recent > 20 then table.remove(CAL.recent, 1) end CAL.otherHits = (CAL.otherHits or 0) + (isOther and 1 or 0) CAL.worldHits = (CAL.worldHits or 0) + (isOther and 0 or 1) end -- did the target actually travel as far as we assumed? This is -- the direct check on the velocity estimate driving the lead. local ratio = -1 if rec.fireTargetPos and rec.flight and rec.flight > 0.02 then local moved = tRoot.Position - rec.fireTargetPos local elapsed = (rec.flight or 0) + 0.4 if moved.Magnitude > 60 * math.max(0.1, elapsed) then moved = Vector3.new(0, 0, 0) -- stale/recycled model end local advance = rec.velDir and moved:Dot(rec.velDir) or 0 -- subtract the target's travel during event delivery advance = advance - (rec.targetSpeed or 0) * (oneWayLatency() + (CONFIG.viewDelay or 0)) local predicted = (rec.targetSpeed or 0) * rec.flight if predicted > 0.5 then ratio = advance / predicted CAL.ratioSum = (CAL.ratioSum or 0) + ratio CAL.ratioCount = (CAL.ratioCount or 0) + 1 end end logEvent("prederr", string.format("%s %s |err|=%.2f along=%+.2f advRatio=%.2f flight=%.3f spd=%.1f", tostring(rec.model.Name), landed and "HIT " or "wall", err.Magnitude, signed, ratio, rec.flight or 0, rec.targetSpeed or 0)) end end -- lead quality: only meaningful when the arrow actually landed on the -- target, measured along the target's own direction of travel local hitChar = data.character if rec.velDir and hitChar and typeof(hitChar) == "Instance" then local root = hitChar:FindFirstChild("HumanoidRootPart") local speed = rec.targetSpeed or 0 if root and speed > 2 then local along = (position - root.Position):Dot(rec.velDir) / speed if math.abs(along) < 0.4 then local w = rec.weaponId or "?" CAL.leadSum = CAL.leadSum or {} CAL.leadSum[w] = (CAL.leadSum[w] or 0) + along CAL.leadCount[w] = (CAL.leadCount[w] or 0) + 1 CAL.leadByWeapon[w] = CAL.leadSum[w] / CAL.leadCount[w] end end end end -- 3. where our own arrow ended up local seen = STATE.myKeys and hitKey and STATE.myKeys[hitKey] if seen and os.clock() - seen < 8 then local miss = -1 local ch = data.character if ch and typeof(ch) == "Instance" then local r = ch:FindFirstChild("HumanoidRootPart") or ch:FindFirstChild("Head") if r then miss = (r.Position - position).Magnitude end end logEvent("impact", string.format("char=%s part=%s miss=%.2f expired=%s", tostring(ch and ch.Name), tostring(data.part and data.part.Name), miss, tostring(data.expired))) STATE.lastImpact = { character = data.character, position = data.position, part = data.part } end elseif action == "Hit" then local victim = data.target if victim == LP or victim == LP.Character then -- incoming fire that connected, split by whether the desync was live: -- this is the only direct read on whether it is actually taking shots CAL.onUs = CAL.onUs or { on = 0, onDmg = 0, off = 0, offDmg = 0 } local dmg = tonumber(data.damage or data.amount) or 0 -- Match this hit against the last prediction made for a shot at us, and say which -- way it went wrong: the arc called a landing and the move did not save us, or -- the arc called a miss and it landed. CAL.taken = (CAL.taken or 0) + 1 do local prediction for index = #(STATE.shotPredictions or {}), 1, -1 do local entry = STATE.shotPredictions[index] if os.clock() - entry.at < 1.5 then prediction = entry break end end CAL.takenFor = CAL.takenFor or {} local attackerName local attacker = data.attacker if attacker == LP or attacker == LP.Character then attackerName = nil elseif typeof(attacker) == "Instance" then attackerName = (attacker:IsA("Player") and attacker.Name) or attacker.Name end if prediction and prediction.shooter then attackerName = attackerName or prediction.shooter end if attackerName then CAL.takenFor[attackerName] = (CAL.takenFor[attackerName] or 0) + 1 end if attackerName then CAL.takenPredictedHit = CAL.takenPredictedHit or 0 end if not prediction then logEvent("taken", string.format("hit for %.0f with no shot prediction on file", dmg)) elseif prediction.hit then CAL.takenPredictedHit = (CAL.takenPredictedHit or 0) + 1 logEvent("taken", string.format( "%s at %.0f studs: arc said it lands (%.0fms away), %s - and it still hit", prediction.weapon, prediction.dist or 0, (prediction.impactIn or 0) * 1000, prediction.moved and string.format("moved %.0fms before it arrived", (prediction.moveIn or 0) * 1000) or "no move was made")) else CAL.takenPredictedMiss = (CAL.takenPredictedMiss or 0) + 1 logEvent("taken", string.format( "%s at %.0f studs: arc said it passes clear and it hit anyway - the arc was wrong", prediction.weapon, prediction.dist or 0)) end end if CONFIG.desync then CAL.onUs.on = CAL.onUs.on + 1 CAL.onUs.onDmg = CAL.onUs.onDmg + dmg else CAL.onUs.off = CAL.onUs.off + 1 CAL.onUs.offDmg = CAL.onUs.offDmg + dmg end local measured = sampleViewDelay(data.position) if measured then CAL.delaySum = (CAL.delaySum or 0) + measured CAL.delayCount = (CAL.delayCount or 0) + 1 logEvent("delay", string.format("impact on us -> view delay %.3fs (mean %.3fs, n=%d)", measured, CAL.delaySum / CAL.delayCount, CAL.delayCount)) end end local attacker = data.attacker if attacker == LP or attacker == LP.Character then logEvent("hit", string.format("dmg=%s head=%s blocked=%s crit=%s", tostring(data.damage or data.amount), tostring(data.headshot), tostring(data.blocked), tostring(data.crit))) end end end local denialConnection = ReplicatedStorage.Remotes.CombatFX.OnClientEvent:Connect(onCombatFXEvent) ;(function() local unreliable = ReplicatedStorage.Remotes:FindFirstChild("CombatFXU") if unreliable and unreliable:IsA("UnreliableRemoteEvent") then STATE.unreliableFx = unreliable.OnClientEvent:Connect(onCombatFXEvent) end end)(); local hitConnection = ReplicatedStorage.Remotes.HitConfirm.OnClientEvent:Connect(function(data) if type(data) == "table" then STATE.lastHit = { headshot = data.headshot, amount = data.amount, victim = data.victim, id = data.id, at = os.clock(), killed = data.killed } logEvent("confirm", string.format("victim=%s dmg=%s head=%s kill=%s", tostring(data.victim), tostring(data.amount), tostring(data.headshot), tostring(data.killed))) -- Our own hit sound, deliberately not the names the game already plays on this -- same event: util.Sound drops a repeat of the same name inside 35ms, so using -- "Hitmarker_Ranged" here would be swallowed by the game's own hitmarker. if VIS.hitsounds then local name = data.killed and "Hit_Melee_Heavy" or (data.headshot and "Hit_Punch" or "Hit_Melee") pcall(function() Util.Sound(name, nil, { Volume = math.clamp((VIS.hitsoundVolume or 60) / 100, 0, 1) }) end) end end end) local UI = { theme = { bg = Color3.fromRGB(23, 24, 26), panel = Color3.fromRGB(32, 33, 36), panelHeader = Color3.fromRGB(41, 43, 47), border = Color3.fromRGB(9, 9, 10), borderLight = Color3.fromRGB(58, 60, 64), select = Color3.fromRGB(48, 50, 55), hover = Color3.fromRGB(44, 46, 50), text = Color3.fromRGB(205, 208, 212), bright = Color3.fromRGB(236, 239, 243), dim = Color3.fromRGB(128, 132, 138), accent = Color3.fromRGB(222, 226, 231), accentDim = Color3.fromRGB(92, 96, 102), track = Color3.fromRGB(16, 17, 19), fill = Color3.fromRGB(74, 78, 84), off = Color3.fromRGB(58, 60, 64), } } local function container() -- nested on purpose: the main chunk sits at its local limit, and this is only -- needed while picking a container local function canParent(host) if not host then return false end return (pcall(function() local probe = Instance.new("Frame") probe.Name = "TribesGuiProbe" probe.Parent = host probe:Destroy() end)) end local ok, hui = pcall(gethui) if ok and typeof(hui) == "Instance" and canParent(hui) then return hui end local core = game:GetService("CoreGui") if canParent(core) then return core end local playerGui = game:GetService("Players").LocalPlayer:FindFirstChildOfClass("PlayerGui") if playerGui then return playerGui end return core end local function prop(obj, props) for k, v in pairs(props) do local ok = pcall(function() obj[k] = v end) if not ok then STATE.setPropFails = (STATE.setPropFails or 0) + 1 STATE.lastPropFail = tostring(k) end end return obj end -- Some environments refuse instance writes from certain threads with "lacking -- capability Plugin". Nothing here can change that, so every step is attempted and -- a refusal is survivable: the object still exists and is still usable, it just -- misses the property that was refused. If the object cannot be parented where it -- was asked to go, it is parked in the player's own PlayerGui instead, which is -- writable everywhere, so the interface still appears. local function mk(class, props, parent) local obj = Instance.new(class) if props then pcall(prop, obj, props) end local ok = pcall(function() obj.Parent = parent end) if not ok or obj.Parent ~= parent then -- parked in the player's own PlayerGui instead: writable everywhere, so the -- interface still appears even where that parent is refused if not (STATE.fallbackHost and STATE.fallbackHost.Parent) then local playerGui = game:GetService("Players").LocalPlayer:FindFirstChildOfClass("PlayerGui") if playerGui then local host = Instance.new("ScreenGui") host.Name = "TribesFallback" host.IgnoreGuiInset = true host.ResetOnSpawn = false host.DisplayOrder = 2147482000 host.Parent = playerGui STATE.fallbackHost = host end end if STATE.fallbackHost then pcall(function() obj.Parent = STATE.fallbackHost end) end end return obj end local function round(obj, r) local c = Instance.new("UICorner") c.CornerRadius = UDim.new(0, r or 0) c.Parent = obj return c end local function outline(obj, color, thickness, transparency) local s = Instance.new("UIStroke") s.Color = color or UI.theme.border s.Thickness = thickness or 1 s.Transparency = transparency or 0 s.ApplyStrokeMode = Enum.ApplyStrokeMode.Border s.Parent = obj return s end -- the frame edge: a black hairline plus a faint inner highlight, which is what -- gives the flat panels their shape local function padding(obj, top, bottom, left, right) local pad = Instance.new("UIPadding") pad.PaddingTop = UDim.new(0, top or 0) pad.PaddingBottom = UDim.new(0, bottom or 0) pad.PaddingLeft = UDim.new(0, left or 0) pad.PaddingRight = UDim.new(0, right or 0) pad.Parent = obj return pad end ;(function() local host = container() for _, leftover in ipairs(host:GetChildren()) do if leftover:IsA("ScreenGui") and (leftover.Name == "TribesCSGO" or leftover.Name == "TribesESP") then pcall(function() leftover:Destroy() end) end end end)(); local sg = mk("ScreenGui", { Name = "TribesCSGO", IgnoreGuiInset = true, ResetOnSpawn = false, DisplayOrder = 2147483647, ZIndexBehavior = Enum.ZIndexBehavior.Sibling, }, container()) pcall(function() if protectgui then protectgui(sg) end end) -- ------------------------------------------------------------------ settings persistence -- Every control writes straight through to disk, so there is no save button to -- forget and no state that only lives until the next respawn. The write is -- debounced because a slider drag fires on every frame. local CONFIG_FILE = "tribes_overlay_config.json" local HttpService = game:GetService("HttpService") local settingsDirty = false local function configSnapshot() local out = {} -- keys a test has set are excluded: without this a save issued while a test is -- running writes the test's values into the file, and they survive the reload local pinned = STATE.testPins local function take(prefix, source) for key, value in pairs(source) do -- the lead scale is pinned in code, not a setting if not (pinned and pinned[key] ~= nil) then local kind = typeof(value) if kind == "number" or kind == "string" or kind == "boolean" then out[prefix .. key] = value elseif kind == "Color3" then out[prefix .. key] = { __color = true, r = value.R, g = value.G, b = value.B } end end end end take("c_", CONFIG) take("v_", VIS) return out end local function saveConfig() if type(writefile) ~= "function" then return false, "no filesystem access" end local snapshot = configSnapshot() local ok, encoded = pcall(function() return HttpService:JSONEncode(snapshot) end) if not ok or type(encoded) ~= "string" then return false, "encode failed: " .. tostring(encoded) end local written, err = pcall(writefile, CONFIG_FILE, encoded) if not written then return false, "write failed: " .. tostring(err) end local n = 0 for _ in pairs(snapshot) do n += 1 end return true, string.format("saved %d settings", n) end local function loadConfig() if type(readfile) ~= "function" then return false, "no filesystem access" end local ok, data = pcall(readfile, CONFIG_FILE) if not ok or type(data) ~= "string" or #data == 0 then return false, "no saved config" end local decoded ok, decoded = pcall(function() return HttpService:JSONDecode(data) end) if not ok or type(decoded) ~= "table" then return false, "saved config is corrupt" end local applied, skipped = 0, 0 for key, value in pairs(decoded) do local prefix, name = key:sub(1, 1), key:sub(3) local target = (prefix == "c" and CONFIG) or (prefix == "v" and VIS) or nil -- not `target and name ~= "" and target[name] or nil`: a stored false would -- collapse to nil there and every toggle that was off would be treated as -- an unknown key and skipped local current if target and name ~= "" then current = target[name] end if current == nil then skipped += 1 elseif typeof(current) == "Color3" then if type(value) == "table" and value.__color then target[name] = Color3.new(value.r or 0, value.g or 0, value.b or 0) applied += 1 else skipped += 1 end elseif type(value) == type(current) then target[name] = value applied += 1 else skipped += 1 end end -- pinned values win over whatever an older file happens to hold, and the -- displacement sliders are clamped: a saved 14-stud dodge makes a jump of nearly -- thirty studs once it stacks with the desync CONFIG.viewDelay = math.clamp(tonumber(CONFIG.viewDelay) or 0, 0, 0.2) CONFIG.leadScale = math.clamp(tonumber(CONFIG.leadScale) or 0.2, 0, 1) CONFIG.dodgeDistanceMin = math.clamp(tonumber(CONFIG.dodgeDistanceMin) or 6, 2, 40) CONFIG.dodgeDistanceMax = math.clamp(tonumber(CONFIG.dodgeDistanceMax) or 10, 2, 40) if CONFIG.dodgeDistanceMin > CONFIG.dodgeDistanceMax then CONFIG.dodgeDistanceMin, CONFIG.dodgeDistanceMax = CONFIG.dodgeDistanceMax, CONFIG.dodgeDistanceMin end CONFIG.desyncAmount = math.clamp(tonumber(CONFIG.desyncAmount) or 5, 0, 12) CONFIG.desyncVerticalAmount = math.clamp(tonumber(CONFIG.desyncVerticalAmount) or 2.5, 0, 6) CONFIG.desyncJitter = math.clamp(tonumber(CONFIG.desyncJitter) or 0.25, 0, 0.6) CONFIG.meleeHopHeight = math.clamp(tonumber(CONFIG.meleeHopHeight) or 10, 4, 20) -- a hold of zero is allowed: the step then lands and is released on the same frame, -- which is a dodge that exists only as a flick CONFIG.dodgeHold = math.clamp(tonumber(CONFIG.dodgeHold) or 0.4, 0, 1.5) CONFIG.dodgeRange = math.clamp(tonumber(CONFIG.dodgeRange) or 55, 10, 200) CONFIG.realBodyScale = math.clamp(tonumber(CONFIG.realBodyScale) or 1, 0.5, 2.5) CONFIG.jitterMin = math.clamp(tonumber(CONFIG.jitterMin) or 3, 1, 30) CONFIG.jitterMax = math.clamp(tonumber(CONFIG.jitterMax) or 15, 1, 30) CONFIG.jitterInterval = math.clamp(tonumber(CONFIG.jitterInterval) or 0.01, 0.003, 0.1) return true, string.format("loaded %d settings (%d skipped)", applied, skipped) end local function saveSoon() settingsDirty = true end task.spawn(function() while not closed do if settingsDirty then settingsDirty = false pcall(saveConfig) end task.wait(0.5) end end) -- applied before any row is built, so the menu opens on the saved state loadConfig() -- ------------------------------------------------------------------ interface (uip100) -- The menu is the uip100 library. Every control here is a thin pair of accessors over -- CONFIG and VIS, so the aim engine, the ESP and the hvH sections keep reading those -- two tables exactly as they did with the built-in menu. The library also keeps its own -- config files (Library's Configs tab) and trident keeps its JSON, both under uip100/. local uiWindow, uiRefresh -- driven from the render loop inside the interface block below, so it is declared here local updateIndicator do local Library = loadstring(game:HttpGet("https://pastebin.com/raw/ncKGkPTW"))() Library.Folders = { Directory = "uip100", Configs = "uip100/Configs", Assets = "uip100/Assets", } for _, Folder in pairs(Library.Folders) do if not isfolder(Folder) then makefolder(Folder) end end pcall(function() Library:ChangeTheme("Accent", Color3.fromRGB(0, 190, 180)) end) local Window = Library:Window({ Name = "Trident", Logo = "90829033870443", MobileButtonText = "Trident" }) local Watermark = Library:Watermark("trident") local KeybindList = Library:KeybindList() local TargetHud = Library:TargetHud() TargetHud:SetPlayer(LP) local HealthBar = TargetHud:AddBar(Color3.fromRGB(70, 200, 90)) local HitBar = TargetHud:AddBar(Color3.fromRGB(0, 190, 180)) -- the library's target hud wants a Player for the avatar and the name; the aim target -- is just as often a training dummy or a bot, so its label is written directly local HudName, HudAvatar do local holder = Library.Holder and Library.Holder.Instance for _, item in ipairs(holder and holder:GetDescendants() or {}) do if item:IsA("TextLabel") and item.Text and item.Text:find("@", 1, true) then HudName = item for _, sibling in ipairs(item.Parent:GetChildren()) do if sibling:IsA("ImageLabel") then HudAvatar = sibling end end break end end end local function HudTarget() local model = STATE.lock and STATE.lock.model if not (model and model.Parent) then model = STATE.lastSolution and STATE.lastSolution.model end if model and model.Parent then return model end return nil end -- Every element is registered with its accessor so one pass puts the whole menu -- back on the CONFIG/VIS state after a config load or a reset. local Controls = {} local function track(Element, Get) Controls[#Controls + 1] = { Element = Element, Get = Get } return Element end uiRefresh = function() for _, Entry in ipairs(Controls) do pcall(function() Entry.Element:Set(Entry.Get()) end) end end local function Section(Page, Name, Side) return Page:Section({ Name = Name, Side = Side }) end local function Toggle(Parent, Name, Flag, Get, Set) return track(Parent:Toggle({ Name = Name, Flag = Flag, Default = Get(), Callback = function(Value) Set(Value) saveSoon() end, }), Get) end local function Slider(Parent, Name, Flag, Min, Max, Get, Set, Decimals, Suffix) return track(Parent:Slider({ Name = Name, Flag = Flag, Min = Min, Max = Max, Decimals = Decimals or 0.01, Suffix = Suffix or "", Default = Get(), Callback = function(Value) Set(Value) saveSoon() end, }), Get) end local function Dropdown(Parent, Name, Flag, Items, Get, Set) return track(Parent:Dropdown({ Name = Name, Flag = Flag, Items = Items, Default = Get(), Callback = function(Value) Set(Value) saveSoon() end, }), Get) end local function Colour(Parent, Name, Flag, Get, Set) return track(Parent:Label(Name):Colorpicker({ Flag = Flag, Default = Get(), Callback = function(Value) Set(Value) saveSoon() end, }), Get) end -- A keybind attached to a toggle fires that toggle while the element is still being -- built (the key starts off), so the stored value is put back afterwards. local function Keyed(Element, Flag, Key, Mode, Get) local Value = Get() Element:Keybind({ Flag = Flag, Default = Key, Mode = Mode }) Element:Set(Value) return Element end local function Lower(Value) return string.lower(tostring(Value)) end -- dropdowns carry option labels; CONFIG keeps the lower-case api values local Labels = { targets = { dummies = "Dummies", players = "Players", both = "Both" }, playerHitPart = { auto = "Auto", head = "Head", body = "Body" }, dummyHitPart = { auto = "Auto", head = "Head", body = "Body" }, mode = { direct = "Direct", mortar = "Mortar" }, } local Combat = Window:Page({ Name = "Combat", Columns = 2 }) local Visuals = Window:Page({ Name = "Visuals", Columns = 2 }) local Misc = Window:Page({ Name = "Misc", Columns = 2 }) -- -------------------------------- combat: aim local Aim = Section(Combat, "Silent aim", 1) Keyed(Toggle(Aim, "Enable aim assist", "C_enabled", function() return CONFIG.enabled end, function(Value) CONFIG.enabled = Value end), "K_aim", Enum.KeyCode.X, "Toggle", function() return CONFIG.enabled end) Toggle(Aim, "Wait for target visible", "C_simulate", function() return CONFIG.simulate end, function(Value) CONFIG.simulate = Value end) Toggle(Aim, "Gravity compensation", "C_gravityComp", function() return CONFIG.gravityComp end, function(Value) CONFIG.gravityComp = Value end) Toggle(Aim, "Wallbang (move shot origin)", "C_wallbang", function() return CONFIG.wallbang end, function(Value) CONFIG.wallbang = Value end) Slider(Aim, "Wallbang reach", "C_wallbangMax", 2, 150, function() return CONFIG.wallbangMax end, function(Value) CONFIG.wallbangMax = Value end, 1, " studs") Dropdown(Aim, "Targets", "C_targets", { "Dummies", "Players", "Both" }, function() return Labels.targets[CONFIG.targets] or "Both" end, function(Value) CONFIG.targets = Lower(Value) end) Dropdown(Aim, "Player hitbox", "C_playerHitPart", { "Auto", "Head", "Body" }, function() return Labels.playerHitPart[CONFIG.playerHitPart] or "Auto" end, function(Value) CONFIG.playerHitPart = Lower(Value) end) Dropdown(Aim, "Dummy hitbox", "C_dummyHitPart", { "Auto", "Head", "Body" }, function() return Labels.dummyHitPart[CONFIG.dummyHitPart] or "Auto" end, function(Value) CONFIG.dummyHitPart = Lower(Value) end) Dropdown(Aim, "Trajectory", "C_mode", { "Direct", "Mortar" }, function() return Labels.mode[CONFIG.mode] or "Direct" end, function(Value) CONFIG.mode = Lower(Value) end) Slider(Aim, "FOV / aim cone", "C_fov", 5, 180, function() return CONFIG.fov end, function(Value) CONFIG.fov = Value end, 1, " deg") Slider(Aim, "Lead scale", "C_leadScale", 0, 1, function() return CONFIG.leadScale end, function(Value) CONFIG.leadScale = Value end, 0.01, "x") Aim:Label("target = closest to the crosshair in the circle") Aim:Label("lead = their travel over the flight x scale") Toggle(Aim, "Instant draw (full charge)", "C_instantFire", function() return CONFIG.instantFire end, function(Value) CONFIG.instantFire = Value end) Keyed(Toggle(Aim, "Auto-fire when server ready", "C_autoFire", function() return CONFIG.autoFire end, function(Value) CONFIG.autoFire = Value end), "K_autoFire", Enum.KeyCode.C, "Toggle", function() return CONFIG.autoFire end) -- -------------------------------- combat: support + status local Support = Section(Combat, "Support", 2) Toggle(Support, "Melee kill aura", "C_killAura", function() return CONFIG.killAura end, function(Value) CONFIG.killAura = Value end) Slider(Support, "Kill aura range", "C_killAuraRange", 0, 20, function() return CONFIG.killAuraRange end, function(Value) CONFIG.killAuraRange = Value end, 0.1, " studs") Toggle(Support, "Auto-heal with bandages", "C_autoHeal", function() return CONFIG.autoHeal end, function(Value) CONFIG.autoHeal = Value end) Slider(Support, "Heal below", "C_autoHealBelow", 5, 95, function() return CONFIG.autoHealBelow end, function(Value) CONFIG.autoHealBelow = Value end, 1, "%") Slider(Support, "Heal cooldown", "C_autoHealCooldown", 1, 15, function() return CONFIG.autoHealCooldown end, function(Value) CONFIG.autoHealCooldown = Value end, 0.1, " s") Toggle(Support, "Team check (skip own tribe)", "C_teamCheck", function() return CONFIG.teamCheck end, function(Value) CONFIG.teamCheck = Value end) Toggle(Support, "Draw FOV ring", "V_fovRing", function() return VIS.fovRing end, function(Value) VIS.fovRing = Value end) Toggle(Support, "Watermark", "V_watermark", function() return VIS.watermark end, function(Value) VIS.watermark = Value end) Toggle(Support, "Target hud", "V_targetHud", function() return VIS.targetHud end, function(Value) VIS.targetHud = Value end) local Status = Section(Combat, "Status", 2) local TargetLine = Status:Label("target") local AimLine = Status:Label("aim") local PoolLine = Status:Label("pool") local LeadLine = Status:Label("lead") local HitLine = Status:Label("hit rate") local CalLine = Status:Label("calibration") local EnvLine = Status:Label("environment") local ErrLine = Status:Label("errors") -- -------------------------------- visuals: esp local Esp = Section(Visuals, "ESP", 1) Keyed(Toggle(Esp, "Enable ESP", "V_enabled", function() return VIS.enabled end, function(Value) VIS.enabled = Value end), "K_esp", Enum.KeyCode.V, "Toggle", function() return VIS.enabled end) Toggle(Esp, "Path indicator", "V_pathIndicator", function() return VIS.pathIndicator end, function(Value) VIS.pathIndicator = Value end) Toggle(Esp, "Box", "V_box", function() return VIS.box end, function(Value) VIS.box = Value end) Dropdown(Esp, "Box style", "V_boxStyle", { "Corners", "Full" }, function() return VIS.boxStyle end, function(Value) VIS.boxStyle = Value end) Toggle(Esp, "Filled box", "V_filledBox", function() return VIS.filledBox end, function(Value) VIS.filledBox = Value end) Slider(Esp, "Fill opacity", "V_fillAlpha", 0.5, 0.98, function() return VIS.fillAlpha end, function(Value) VIS.fillAlpha = Value end, 0.01) Toggle(Esp, "Health bar", "V_healthBar", function() return VIS.healthBar end, function(Value) VIS.healthBar = Value end) Toggle(Esp, "Name", "V_name", function() return VIS.name end, function(Value) VIS.name = Value end) Toggle(Esp, "Distance", "V_distance", function() return VIS.distance end, function(Value) VIS.distance = Value end) Toggle(Esp, "Weapon / tag", "V_weapon", function() return VIS.weapon end, function(Value) VIS.weapon = Value end) Toggle(Esp, "Head dot", "V_headDot", function() return VIS.headDot end, function(Value) VIS.headDot = Value end) Toggle(Esp, "Skeleton", "V_skeleton", function() return VIS.skeleton end, function(Value) VIS.skeleton = Value end) Toggle(Esp, "Snapline", "V_snapline", function() return VIS.snapline end, function(Value) VIS.snapline = Value end) Slider(Esp, "Max distance", "V_maxDistance", 100, 2000, function() return VIS.maxDistance end, function(Value) VIS.maxDistance = Value end, 1, " studs") Slider(Esp, "Text size", "V_textSize", 8, 20, function() return VIS.textSize end, function(Value) VIS.textSize = Value end, 1, " px") -- -------------------------------- visuals: effects local Effects = Section(Visuals, "Effects", 2) Toggle(Effects, "Tracers", "V_tracers", function() return VIS.tracers end, function(Value) VIS.tracers = Value end) Toggle(Effects, "Tracers on enemy shots", "V_tracersEnemy", function() return VIS.tracersEnemy end, function(Value) VIS.tracersEnemy = Value end) Colour(Effects, "Tracer colour", "V_tracerColor", function() return VIS.tracerColor end, function(Value) VIS.tracerColor = Value end) Colour(Effects, "Enemy tracer colour", "V_tracerColorEnemy", function() return VIS.tracerColorEnemy end, function(Value) VIS.tracerColorEnemy = Value end) Slider(Effects, "Tracer life", "V_tracerLife", 0.05, 1, function() return VIS.tracerLife end, function(Value) VIS.tracerLife = Value end, 0.001, " s") Slider(Effects, "Tracer thickness", "V_tracerThickness", 0.1, 0.8, function() return VIS.tracerThickness end, function(Value) VIS.tracerThickness = Value end, 0.01) Toggle(Effects, "Impact markers", "V_markers", function() return VIS.markers end, function(Value) VIS.markers = Value end) Colour(Effects, "Marker colour (world)", "V_markerColorWorld", function() return VIS.markerColorWorld end, function(Value) VIS.markerColorWorld = Value end) Colour(Effects, "Marker colour (player)", "V_markerColorPlayer", function() return VIS.markerColorPlayer end, function(Value) VIS.markerColorPlayer = Value end) Slider(Effects, "Marker size", "V_markerSize", 8, 48, function() return VIS.markerSize end, function(Value) VIS.markerSize = Value end, 1, " px") Slider(Effects, "Marker life", "V_markerLife", 0.1, 1.5, function() return VIS.markerLife end, function(Value) VIS.markerLife = Value end, 0.001, " s") Toggle(Effects, "Hitsounds", "V_hitsounds", function() return VIS.hitsounds end, function(Value) VIS.hitsounds = Value end) Slider(Effects, "Hitsound volume", "V_hitsoundVolume", 0, 100, function() return VIS.hitsoundVolume end, function(Value) VIS.hitsoundVolume = Value end, 1, "%") -- -------------------------------- chams local Highlight = Section(Visuals, "Chams (highlight)", 1) Toggle(Highlight, "Chams", "V_chams", function() return VIS.chams end, function(Value) VIS.chams = Value end) Toggle(Highlight, "Through walls", "V_chamsThrough", function() return VIS.chamsThrough end, function(Value) VIS.chamsThrough = Value end) Toggle(Highlight, "Thermal breathing", "V_chamsThermal", function() return VIS.chamsThermal end, function(Value) VIS.chamsThermal = Value end) Toggle(Highlight, "Rainbow fill", "V_chamsRainbow", function() return VIS.chamsRainbow end, function(Value) VIS.chamsRainbow = Value end) Toggle(Highlight, "Fade with distance", "V_chamsFade", function() return VIS.chamsFade end, function(Value) VIS.chamsFade = Value end) Colour(Highlight, "Fill colour", "V_chamsFill", function() return VIS.chamsFill end, function(Value) VIS.chamsFill = Value end) Slider(Highlight, "Fill alpha", "V_chamsFillAlpha", 0, 100, function() return VIS.chamsFillAlpha end, function(Value) VIS.chamsFillAlpha = Value end, 1, "%") Colour(Highlight, "Outline (players/bots)", "V_chamsOutline", function() return VIS.chamsOutline end, function(Value) VIS.chamsOutline = Value end) Colour(Highlight, "Outline (dummies)", "V_chamsOutlineDummy", function() return VIS.chamsOutlineDummy end, function(Value) VIS.chamsOutlineDummy = Value end) Slider(Highlight, "Outline alpha", "V_chamsOutlineAlpha", 0, 100, function() return VIS.chamsOutlineAlpha end, function(Value) VIS.chamsOutlineAlpha = Value end, 1, "%") local Material = Section(Visuals, "Material chams", 2) Toggle(Material, "Chams on body parts", "V_matChams", function() return VIS.matChams end, function(Value) VIS.matChams = Value end) Toggle(Material, "Rainbow body", "V_matChamsRainbow", function() return VIS.matChamsRainbow end, function(Value) VIS.matChamsRainbow = Value end) Dropdown(Material, "Material", "V_matChamsMaterial", { "ForceField", "Neon", "Glass", "Plastic", "SmoothPlastic", "Metal", "Foil", "Ice", "Marble", "Granite", "Slate", "Concrete", "Wood", "Fabric", }, function() return VIS.matChamsMaterial end, function(Value) VIS.matChamsMaterial = Value end) Dropdown(Material, "Pattern", "V_matChamsPattern", { "Static", "Wave", "Pulse", "Noise", "Lightning", "Scanline", "Grid", }, function() return VIS.matChamsPattern end, function(Value) VIS.matChamsPattern = Value end) Colour(Material, "Body colour", "V_matChamsColor", function() return VIS.matChamsColor end, function(Value) VIS.matChamsColor = Value end) Slider(Material, "Body alpha", "V_matChamsAlpha", 0, 1, function() return VIS.matChamsAlpha end, function(Value) VIS.matChamsAlpha = Value end, 0.01) Slider(Material, "Pattern speed", "V_matChamsSpeed", 0.5, 12, function() return VIS.matChamsSpeed end, function(Value) VIS.matChamsSpeed = Value end, 0.1) -- -------------------------------- colours local Palette = Section(Visuals, "Colors", 1) Colour(Palette, "Enemy", "V_enemyColor", function() return VIS.enemyColor end, function(Value) VIS.enemyColor = Value end) Colour(Palette, "Own tribe", "V_teamColor", function() return VIS.teamColor end, function(Value) VIS.teamColor = Value end) Colour(Palette, "Training dummy", "V_dummyColor", function() return VIS.dummyColor end, function(Value) VIS.dummyColor = Value end) Colour(Palette, "Health (low)", "V_healthBad", function() return VIS.healthBad end, function(Value) VIS.healthBad = Value end) Colour(Palette, "Health (full)", "V_healthGood", function() return VIS.healthGood end, function(Value) VIS.healthGood = Value end) Colour(Palette, "Text", "V_textColor", function() return VIS.textColor end, function(Value) VIS.textColor = Value end) -- -------------------------------- real body local RealBody = Section(Visuals, "Real body visualization", 2) Toggle(RealBody, "Real body visualization", "C_realBody", function() return CONFIG.realBody end, function(Value) CONFIG.realBody = Value end) Colour(RealBody, "Marker colour", "V_realBodyColor", function() return VIS.realBodyColor end, function(Value) VIS.realBodyColor = Value end) Toggle(RealBody, "Colour by distance", "C_realBodyHeat", function() return CONFIG.realBodyHeat end, function(Value) CONFIG.realBodyHeat = Value end) Slider(RealBody, "Marker size", "C_realBodyScale", 0.5, 2.5, function() return CONFIG.realBodyScale end, function(Value) CONFIG.realBodyScale = Value end, 0.01, "x") Toggle(RealBody, "Distance label", "C_realBodyTag", function() return CONFIG.realBodyTag end, function(Value) CONFIG.realBodyTag = Value end) Toggle(RealBody, "Crosshair chevron", "C_realBodyChevron", function() return CONFIG.realBodyChevron end, function(Value) CONFIG.realBodyChevron = Value end) -- -------------------------------- misc: hvH local Hvh = Section(Misc, "Anti aim", 1) Toggle(Hvh, "Desync anti-aim", "C_desync", function() return CONFIG.desync end, function(Value) CONFIG.desync = Value end) Slider(Hvh, "Desync amount", "C_desyncAmount", 0, 12, function() return CONFIG.desyncAmount end, function(Value) CONFIG.desyncAmount = Value end, 0.1, " studs") Toggle(Hvh, "Step across their aim", "C_desyncAimAware", function() return CONFIG.desyncAimAware end, function(Value) CONFIG.desyncAimAware = Value end) Toggle(Hvh, "Desync only when walking", "C_desyncWalkOnly", function() return CONFIG.desyncWalkOnly end, function(Value) CONFIG.desyncWalkOnly = Value end) Slider(Hvh, "Desync interval", "C_desyncInterval", 0.05, 0.6, function() return CONFIG.desyncInterval end, function(Value) CONFIG.desyncInterval = Value end, 0.001, " s") Toggle(Hvh, "Step in height too", "C_desyncVertical", function() return CONFIG.desyncVertical end, function(Value) CONFIG.desyncVertical = Value end) Slider(Hvh, "Height step", "C_desyncVerticalAmount", 0, 6, function() return CONFIG.desyncVerticalAmount end, function(Value) CONFIG.desyncVerticalAmount = Value end, 0.1, " studs") Slider(Hvh, "Interval variation", "C_desyncJitter", 0, 0.6, function() return CONFIG.desyncJitter end, function(Value) CONFIG.desyncJitter = Value end, 0.01) Slider(Hvh, "Local view compensation", "C_desyncLocalSteady", 0, 100, function() return CONFIG.desyncLocalSteady end, function(Value) CONFIG.desyncLocalSteady = Value end, 1, "%") -- -------------------------------- misc: dodge local Dodge = Section(Misc, "Dodge", 2) Toggle(Dodge, "Dodge when attacked", "C_dodge", function() return CONFIG.dodge end, function(Value) CONFIG.dodge = Value end) Toggle(Dodge, "Jitter dodge (draw to landing)", "C_jitterDodge", function() return CONFIG.jitterDodge end, function(Value) CONFIG.jitterDodge = Value end) Slider(Dodge, "Jitter studs min", "C_jitterMin", 1, 30, function() return CONFIG.jitterMin end, function(Value) CONFIG.jitterMin = Value end, 0.1, " studs") Slider(Dodge, "Jitter studs max", "C_jitterMax", 1, 30, function() return CONFIG.jitterMax end, function(Value) CONFIG.jitterMax = Value end, 0.1, " studs") Slider(Dodge, "Jitter every", "C_jitterInterval", 0.003, 0.1, function() return CONFIG.jitterInterval end, function(Value) CONFIG.jitterInterval = Value end, 0.001, " s") Toggle(Dodge, "Smart dodge (their weapon and distance)", "C_dodgeSmart", function() return CONFIG.dodgeSmart end, function(Value) CONFIG.dodgeSmart = Value end) Slider(Dodge, "Dodge studs min", "C_dodgeDistanceMin", 2, 40, function() return CONFIG.dodgeDistanceMin end, function(Value) CONFIG.dodgeDistanceMin = Value end, 0.1, " studs") Slider(Dodge, "Dodge studs max", "C_dodgeDistanceMax", 2, 40, function() return CONFIG.dodgeDistanceMax end, function(Value) CONFIG.dodgeDistanceMax = Value end, 0.1, " studs") Slider(Dodge, "Dodge easing", "C_dodgeRamp", 0, 0.2, function() return CONFIG.dodgeRamp end, function(Value) CONFIG.dodgeRamp = Value end, 0.001, " s") Slider(Dodge, "Dodge hold", "C_dodgeHold", 0, 1.5, function() return CONFIG.dodgeHold end, function(Value) CONFIG.dodgeHold = Value end, 0.001, " s") Slider(Dodge, "Dodge reacts within", "C_dodgeRange", 10, 200, function() return CONFIG.dodgeRange end, function(Value) CONFIG.dodgeRange = Value end, 1, " studs") Slider(Dodge, "Incoming shot trim", "C_viewDelay", 0, 0.2, function() return CONFIG.viewDelay end, function(Value) CONFIG.viewDelay = Value end, 0.001, " s") Toggle(Dodge, "Face whoever we dodge", "C_dodgeFacing", function() return CONFIG.dodgeFacing end, function(Value) CONFIG.dodgeFacing = Value end) Toggle(Dodge, "Steady camera on dodge", "C_dodgeSteadyCamera", function() return CONFIG.dodgeSteadyCamera end, function(Value) CONFIG.dodgeSteadyCamera = Value end) Toggle(Dodge, "Re-step inside the flight", "C_dodgeDouble", function() return CONFIG.dodgeDouble end, function(Value) CONFIG.dodgeDouble = Value end) Toggle(Dodge, "Hop out of melee reach", "C_meleeHop", function() return CONFIG.meleeHop end, function(Value) CONFIG.meleeHop = Value end) Slider(Dodge, "Hop height", "C_meleeHopHeight", 4, 20, function() return CONFIG.meleeHopHeight end, function(Value) CONFIG.meleeHopHeight = Value end, 0.1, " studs") -- -------------------------------- misc: keys, menu, info local Keys = Section(Misc, "Keybinds", 2) Keys:Label("Panic (unload)"):Keybind({ Name = "Panic", Flag = "K_panic", Default = Enum.KeyCode.Delete, Mode = "Hold", Callback = function(State) if State then pcall(function() getgenv().TribesOverlay.unload() end) end end, }) Keys:Label("Show / hide menu"):Keybind({ Name = "Show / hide menu", Flag = "K_window", Default = Enum.KeyCode.RightControl, Mode = "Hold", Callback = function(State) if uiWindow then uiWindow:SetOpen(State) end end, }) Library:CreateSettingsPage(Window, KeybindList, Watermark) -- -------------------------------- status ticker local function RecentRate() local Cal = STATE.cal if not Cal then return 0, 0, 0 end local Hits = Cal.hitCount or 0 local Total = Hits + (Cal.missCount or 0) local Recent = Cal.recent or {} local Sum = 0 for _, Value in ipairs(Recent) do Sum += Value end local Percent = #Recent > 0 and math.floor(Sum / #Recent * 100) or 0 return Hits, Total, Percent end local Accum = 0 RunService.RenderStepped:Connect(function(Delta) if closed then return end Accum += Delta if Accum < 0.25 then return end Accum = 0 pcall(function() updateIndicator() Watermark:SetVisibility(VIS.watermark) if VIS.watermark then Watermark:SetText(string.format("trident aim %s fov %d %s pool %d", CONFIG.enabled and "on" or "off", math.floor(CONFIG.fov + 0.5), CONFIG.targets, STATE.pool or 0)) end TargetHud:SetVisibility(VIS.targetHud) local Target = HudTarget() local TargetPlayer = Target and Players:GetPlayerFromCharacter(Target) or nil if TargetPlayer then if STATE.hudPlayer ~= TargetPlayer then STATE.hudPlayer = TargetPlayer TargetHud:SetPlayer(TargetPlayer) end if HudAvatar then HudAvatar.Visible = true end else STATE.hudPlayer = nil if HudName then local TheirRoot = Target and Target:FindFirstChild("HumanoidRootPart") local MyRoot = LP.Character and LP.Character:FindFirstChild("HumanoidRootPart") local Gap = TheirRoot and MyRoot and math.floor((TheirRoot.Position - MyRoot.Position).Magnitude + 0.5) or 0 HudName.Text = Target and string.format("%s (%d studs)", Target.Name, Gap) or "no target" end if HudAvatar then HudAvatar.Visible = false end end local Health = 0 if Target then local Humanoid = Target:FindFirstChildOfClass("Humanoid") if Humanoid and Humanoid.MaxHealth > 0 then Health = math.clamp(Humanoid.Health / Humanoid.MaxHealth * 100, 0, 100) end end HealthBar:SetPercentage(Health) local Hits, Total, Percent = RecentRate() HitBar:SetPercentage(math.clamp(Percent, 0, 100)) local Locked = Target and STATE.lock and STATE.lock.model == Target and STATE.lock.solution TargetLine:SetText(Target and string.format("target %s %.0f studs %.1f deg off centre", Target.Name, Locked and (Locked.distance or 0) or 0, Locked and (Locked.angle or 0) or 0) or "target -") AimLine:SetText(string.format("aim %s shots %d hits %d/%d", CONFIG.enabled and "on" or "off", STATE.redirected or 0, STATE.ourHits or 0, STATE.hitEvents or 0)) PoolLine:SetText(string.format("pool %d mode %s tracers %d marks %d", STATE.pool or 0, CONFIG.mode, STATE.tracersDrawn or 0, STATE.markersDrawn or 0)) LeadLine:SetText(string.format("fov %.0f deg lead %.2fx ping %.3fs %s", CONFIG.fov, CONFIG.leadScale or 0, oneWayLatency(), CONFIG.gravityComp and "drop comp on" or "no drop comp")) HitLine:SetText(string.format("hit rate %d/%d recent %d%%", Hits, Total, Percent)) CalLine:SetText(string.format("gated %d shots at us %d last hit %s", (STATE.cal and STATE.cal.gated) or 0, STATE.shotsAtUs or 0, STATE.lastHit and tostring(STATE.lastHit.amount) or "-")) EnvLine:SetText(string.format("%s place %d esp %s", game.Name, game.PlaceId, VIS.enabled and "on" or "off")) ErrLine:SetText(STATE.lastError and ("error: " .. tostring(STATE.lastError)) or (STATE.espError and ("esp: " .. tostring(STATE.espError):sub(1, 70)) or "no errors")) end) end) uiWindow = Window uiRefresh() end -- ------------------------------------------------------------------ ESP (screen-space instances) -- Delta exposes no working Drawing API, so the whole overlay is pooled GuiObjects. -- Bounding boxes come from the mesh bounding box, edges are rotated Frames, and -- off-screen targets are hidden rather than rebuilt so the cost stays flat. local SKEL_R6 = { { "Head", "Torso" }, { "Torso", "Left Arm" }, { "Torso", "Right Arm" }, { "Torso", "Left Leg" }, { "Torso", "Right Leg" }, } local SKEL_R15 = { { "Head", "UpperTorso" }, { "UpperTorso", "LowerTorso" }, { "UpperTorso", "LeftUpperArm" }, { "LeftUpperArm", "LeftLowerArm" }, { "LeftLowerArm", "LeftHand" }, { "UpperTorso", "RightUpperArm" }, { "RightUpperArm", "RightLowerArm" }, { "RightLowerArm", "RightHand" }, { "LowerTorso", "LeftUpperLeg" }, { "LeftUpperLeg", "LeftLowerLeg" }, { "LeftLowerLeg", "LeftFoot" }, { "LowerTorso", "RightUpperLeg" }, { "RightUpperLeg", "RightLowerLeg" }, { "RightLowerLeg", "RightFoot" }, } local GuiService = game:GetService("GuiService") local espGui = mk("ScreenGui", { Name = "TribesESP", IgnoreGuiInset = true, ResetOnSpawn = false, DisplayOrder = 2147483000, ZIndexBehavior = Enum.ZIndexBehavior.Sibling, }, container()) local ESP = { pool = setmetatable({}, { __mode = "k" }), gui = espGui } -- ------------------------------------------------------------------ path indicator -- Answers the one question worth asking before pulling the trigger: with the crosshair where -- it is, is the line to whoever is under it clear, is there a launch point that clears it, or -- is the shot dead in front of a wall. It runs the same calls the shot path runs, so what it -- shows is what a shot would do. local indicator = mk("TextLabel", { Name = "PathIndicator", BackgroundColor3 = Color3.fromRGB(12, 14, 16), BackgroundTransparency = 0.25, BorderSizePixel = 0, Font = Enum.Font.GothamBold, TextSize = 13, TextColor3 = Color3.fromRGB(200, 200, 200), Text = "", AnchorPoint = Vector2.new(0.5, 0), Position = UDim2.new(0.5, 0, 0.5, 44), Size = UDim2.new(0, 240, 0, 22), Visible = false, ZIndex = 50, }, espGui) mk("UICorner", { Parent = indicator, CornerRadius = UDim.new(1, 0) }) local indicatorStroke = mk("UIStroke", { Parent = indicator, Thickness = 1, Color = Color3.fromRGB(120, 120, 120), Transparency = 0.4 }) local PATH_COLOUR = { open = Color3.fromRGB(60, 220, 130), path = Color3.fromRGB(245, 190, 60), blocked = Color3.fromRGB(240, 70, 70), none = Color3.fromRGB(150, 150, 150), } local function pathState() local char = LP.Character local tool = char and char:FindFirstChildWhichIsA("Tool") local origin = tool and aimOrigin(tool) if not origin then local root = char and char:FindFirstChild("HumanoidRootPart") if not root then return "no character", "none" end origin = root.Position + Vector3.new(0, 2, 0) end local target = selectTarget(origin, (lock and lock.model and lock.model.Parent) and lock.model or nil) if not target then return "no target in cone", "none" end local name = target.model.Name if hasLine(origin, target.part.Position, target.model) then -- a shot taken now reaches them return string.format("%s OPEN %.0f studs", name, target.distance), "open" end if not CONFIG.wallbang then -- cover, and moving the launch point is off: the shot goes into whatever is in front return string.format("%s BLOCKED (cover)", name), "blocked" end local candidate, moved = findWallbangOrigin(origin, target.part.Position, target.model, wallbangReach()) if candidate then -- a spot exists, but taking it moves the launch point, and the server fires the -- direction we send from where it thinks we are - which is what puts shots sideways return string.format("%s COVER spot %.1f studs (cannot be used)", name, moved), "path" end return string.format("%s NO SPOT", name), "blocked" end local lastIndicator = 0 updateIndicator = function() if not VIS.pathIndicator then indicator.Visible = false return end local now = os.clock() if now - lastIndicator < 0.35 then return end lastIndicator = now local ok, text, state = pcall(pathState) if not ok then indicator.Text = "indicator error" indicator.TextColor3 = PATH_COLOUR.blocked indicator.Visible = true return end indicator.Text = text indicator.TextColor3 = PATH_COLOUR[state] or PATH_COLOUR.none indicatorStroke.Color = PATH_COLOUR[state] or PATH_COLOUR.none indicator.Visible = true end STATE.updateIndicator = updateIndicator -- Cham support. The patterns are the reference set, one value in 0..1 per part that -- multiplies the colour, so the same code drives every mode. The part pass mirrors -- what the reference does: skip the root and accessory handles, keep the original -- material/colour/transparency so it can be put back exactly. ESP.patterns = { "Static", "Wave", "Pulse", "Noise", "Lightning", "Scanline", "Grid" } ESP.materials = { "ForceField", "Neon", "Glass", "Plastic", "SmoothPlastic", "Metal", "Foil", "Ice", "Marble", "Granite", "Slate", "Concrete", "Wood", "Fabric" } ESP.pattern = function(part, mode, speed) local t = os.clock() * (speed or 3) if mode == "Wave" then return (math.sin(t + part.Position.X * 0.2 + part.Position.Y * 0.2) + 1) * 0.5 elseif mode == "Pulse" then return (math.sin(t * 2) + 1) * 0.5 elseif mode == "Noise" then return (math.noise(part.Position.X * 0.1, part.Position.Y * 0.1, t * 0.5) + 1) * 0.5 elseif mode == "Lightning" then return math.abs(math.sin(t * 12 + part.Position.X)) * 0.8 + 0.2 elseif mode == "Scanline" then return math.clamp(math.sin(t + part.Position.Y * 0.5) * 1.2, 0, 1) elseif mode == "Grid" then return (math.abs(math.sin(part.Position.X * 0.3 + t * 1.5)) + math.abs(math.sin(part.Position.Y * 0.3 + t * 1.5))) * 0.5 end return 1 end ESP.shouldCham = function(part) if not part:IsA("BasePart") then return false end if part.Name == "HumanoidRootPart" then return false end local parent = part.Parent if parent and parent:IsA("Accessory") then return false end -- invisible volumes such as the damage hull are not worth recolouring if part.Transparency >= 1 then return false end return true end ESP.applyMaterial = function(model, store, color, material, pattern, alpha, speed) for _, part in ipairs(model:GetDescendants()) do if ESP.shouldCham(part) then if not store[part] then store[part] = { Material = part.Material, Color = part.Color, Transparency = part.Transparency } end local value = ESP.pattern(part, pattern, speed) pcall(function() if Enum.Material[material] then part.Material = Enum.Material[material] end part.Color = Color3.new(color.R * value, color.G * value, color.B * value) part.Transparency = alpha end) end end end -- Tracers: a thin neon part stretched along the flight path, faded out and recycled. -- Pooled because a fight can produce a lot of them and the phone should not be -- creating and destroying parts per shot. ESP.tracerPool = {} ESP.tracerIndex = 0 ESP.spawnTracer = function(from, to, color, life, thickness) if typeof(from) ~= "Vector3" or typeof(to) ~= "Vector3" then STATE.tracerBadArgs = (STATE.tracerBadArgs or 0) + 1 STATE.lastBadArgs = { from = typeof(from), to = typeof(to) } return end local delta = to - from local length = delta.Magnitude if length < 1 then STATE.tracerTooShort = (STATE.tracerTooShort or 0) + 1 STATE.lastShort = { from = from, to = to, length = length } return end ESP.tracerIndex = (ESP.tracerIndex % 32) + 1 local part = ESP.tracerPool[ESP.tracerIndex] if not part then part = Instance.new("Part") part.Anchored = true part.CanCollide = false part.CanQuery = false part.CanTouch = false part.CastShadow = false part.Material = Enum.Material.Neon part.Name = "Tracer" part.Parent = workspace ESP.tracerPool[ESP.tracerIndex] = part end local size = thickness or 0.07 -- start just past the muzzle so it does not draw out of our own chest, and -- measure the span from there rather than keeping the untrimmed length local start = from + delta.Unit * 1.2 local span = to - start part.Size = Vector3.new(size, size, span.Magnitude) part.CFrame = CFrame.lookAt(start + span * 0.5, to) part.Color = color or Color3.fromRGB(120, 200, 255) part.Transparency = 0.05 TweenService:Create(part, TweenInfo.new(life or 0.22), { Transparency = 1 }):Play() STATE.tracersDrawn = (STATE.tracersDrawn or 0) + 1 STATE.lastTracer = { from = from, to = to, at = os.clock() } logEvent("fx", string.format("tracer %.0f studs from %s to %s", span.Magnitude, string.format("%.0f,%.0f,%.0f", from.X, from.Y, from.Z), string.format("%.0f,%.0f,%.0f", to.X, to.Y, to.Z))) end -- Impact markers: a billboard cross pinned at the impact, on a pooled invisible part ESP.markerPool = {} ESP.markerIndex = 0 ESP.spawnMarker = function(position, color, size, life) if typeof(position) ~= "Vector3" then return end ESP.markerIndex = (ESP.markerIndex % 24) + 1 local entry = ESP.markerPool[ESP.markerIndex] if not entry then local part = Instance.new("Part") part.Anchored = true part.CanCollide = false part.CanQuery = false part.CanTouch = false part.CastShadow = false part.Transparency = 1 part.Size = Vector3.new(0.2, 0.2, 0.2) part.Name = "ImpactMark" part.Parent = workspace local gui = Instance.new("BillboardGui") gui.Name = "Mark" gui.AlwaysOnTop = true gui.LightInfluence = 0 gui.Adornee = part gui.Size = UDim2.fromOffset(22, 22) gui.Parent = part local arms = {} for i = 1, 2 do local arm = Instance.new("Frame") arm.BackgroundColor3 = Color3.new(1, 1, 1) arm.BorderSizePixel = 0 arm.AnchorPoint = Vector2.new(0.5, 0.5) arm.Position = UDim2.fromScale(0.5, 0.5) arm.Size = UDim2.new(0, 2, 0.82, 0) arm.Rotation = i == 1 and 45 or -45 arm.Parent = gui arms[i] = arm end entry = { part = part, gui = gui, arms = arms } ESP.markerPool[ESP.markerIndex] = entry end local size = size or 22 local life = life or 0.35 entry.part.Position = position entry.gui.Size = UDim2.fromOffset(size, size) for _, arm in ipairs(entry.arms) do arm.BackgroundColor3 = color or Color3.new(1, 1, 1) arm.BackgroundTransparency = 0 end TweenService:Create(entry.gui, TweenInfo.new(life), { Size = UDim2.fromOffset(size * 0.6, size * 0.6), }):Play() for _, arm in ipairs(entry.arms) do TweenService:Create(arm, TweenInfo.new(life), { BackgroundTransparency = 1 }):Play() end STATE.markersDrawn = (STATE.markersDrawn or 0) + 1 end -- The CombatFX handler is defined far above this block, so it cannot name the ESP -- table: up there a local that does not exist yet resolves to the global nil, and -- `pcall(ESP.spawnTracer, ...)` throws while evaluating its own arguments, which -- silently aborts the whole handler before it draws anything. These references are -- published on STATE, which does exist that early, and the handler uses them. STATE.fxTracer = ESP.spawnTracer STATE.fxMarker = ESP.spawnMarker ESP.restoreMaterial = function(store) for part, data in pairs(store) do if part and part.Parent then pcall(function() part.Material = data.Material part.Color = data.Color part.Transparency = data.Transparency end) end store[part] = nil end end STATE.screenInset = function() local ok, v = pcall(function() return GuiService:GetGuiInset() end) if ok and typeof(v) == "Vector2" then return v end return Vector2.new(0, 0) end -- ScreenGui space here is the viewport, so the world -> UI mapping is -- WorldToViewportPoint. WorldToScreenPoint on Delta returns viewport minus the -- inset (measured 58px on this device) and draws the whole overlay too high. local function screenCenter() local vs = Camera.ViewportSize return Vector2.new(vs.X * 0.5, vs.Y * 0.5) end local function project(v3) local sp, onScreen = Camera:WorldToViewportPoint(v3) if onScreen and sp.Z > 0 then return Vector2.new(sp.X, sp.Y) end return nil end local function projectBox(cf, size) local half = size * 0.5 local minX, minY, maxX, maxY = math.huge, math.huge, -math.huge, -math.huge local any = false for xi = -1, 1, 2 do for yi = -1, 1, 2 do for zi = -1, 1, 2 do local world = (cf * CFrame.new(half.X * xi, half.Y * yi, half.Z * zi)).Position local sp, on = Camera:WorldToViewportPoint(world) if on and sp.Z > 0 then any = true minX = math.min(minX, sp.X); maxX = math.max(maxX, sp.X) minY = math.min(minY, sp.Y); maxY = math.max(maxY, sp.Y) end end end end if not any then return nil end return minX, minY, maxX, maxY end local function espSquare(parent) return mk("Frame", { BorderSizePixel = 0, BackgroundColor3 = Color3.new(1, 1, 1), Visible = false, }, parent) end local function espLine(parent) local f = espSquare(parent) f.AnchorPoint = Vector2.new(0.5, 0.5) return f end local function espText(parent, size) return mk("TextLabel", { BackgroundTransparency = 1, BorderSizePixel = 0, Font = Enum.Font.Code, TextSize = size, TextColor3 = Color3.new(1, 1, 1), TextStrokeTransparency = 0.4, TextStrokeColor3 = Color3.fromRGB(0, 0, 0), TextXAlignment = Enum.TextXAlignment.Center, TextYAlignment = Enum.TextYAlignment.Center, AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(280, size + 4), Visible = false, }, parent) end local function buildState() local s = { full = {}, corners = {}, skel = {} } for i = 1, 4 do s.full[i] = espLine(espGui) end for i = 1, 8 do s.corners[i] = espLine(espGui) end s.skel = {} s.fill = espSquare(espGui) s.hbBg = espSquare(espGui) s.hbFill = espSquare(espGui) s.dot = espSquare(espGui) round(s.dot, 4) s.snap = espLine(espGui) s.name = espText(espGui, 13) s.dist = espText(espGui, 12) s.wpn = espText(espGui, 12) s.chams = mk("Highlight", { Name = "Chams", Enabled = false, FillTransparency = 1, OutlineTransparency = 0, DepthMode = Enum.HighlightDepthMode.AlwaysOnTop, }, espGui) s.matStore = {} return s end local function stateFor(model) local s = ESP.pool[model] if not s then s = buildState() ESP.pool[model] = s end return s end local function hideAll(s) for _, f in ipairs(s.full) do f.Visible = false end for _, f in ipairs(s.corners) do f.Visible = false end for _, f in ipairs(s.skel) do f.Visible = false end s.fill.Visible = false s.hbBg.Visible = false s.hbFill.Visible = false s.dot.Visible = false s.snap.Visible = false s.name.Visible = false s.dist.Visible = false s.wpn.Visible = false end local function hideState(model) local s = ESP.pool[model] if s then hideAll(s) -- the 2D parts are per frame, but a highlight and recoloured body persist, -- so they are cleared here, on the path that means "not shown this frame" if s.chams then s.chams.Enabled = false end if s.matStore and next(s.matStore) then ESP.restoreMaterial(s.matStore) end end end local function destroyState(s) if s.chams then s.chams:Destroy() end if s.matStore then ESP.restoreMaterial(s.matStore) end for _, f in ipairs(s.full) do f:Destroy() end for _, f in ipairs(s.corners) do f:Destroy() end for _, f in ipairs(s.skel) do f:Destroy() end for _, f in ipairs({ s.fill, s.hbBg, s.hbFill, s.dot, s.snap, s.name, s.dist, s.wpn }) do f:Destroy() end end -- A rotated Frame centred on the segment midpoint, so only one edge per line. local function setLine(f, a, b, color, thickness) local dx, dy = b.X - a.X, b.Y - a.Y local length = math.sqrt(dx * dx + dy * dy) if length < 0.5 then f.Visible = false return end f.Position = UDim2.fromOffset((a.X + b.X) * 0.5, (a.Y + b.Y) * 0.5) f.Size = UDim2.fromOffset(length, thickness or 1) f.Rotation = math.deg(math.atan2(dy, dx)) f.BackgroundColor3 = color f.Visible = true end local function healthColor(frac) return VIS.healthBad:Lerp(VIS.healthGood, math.clamp(frac, 0, 1)) end -- Angular FOV to viewport pixels: the ring then matches the aim cone exactly. local function fovPixelRadius() local vertical = math.rad(math.clamp(Camera.FieldOfView or 70, 1, 179)) local aim = math.rad(math.clamp(CONFIG.fov, 0, 179)) local half = math.tan(aim * 0.5) / math.tan(vertical * 0.5) return half * (Camera.ViewportSize.Y * 0.5) end local function renderSkeleton(s, char, color) local joints = char:FindFirstChild("UpperTorso") and SKEL_R15 or SKEL_R6 local idx = 0 for _, pair in ipairs(joints) do local a = char:FindFirstChild(pair[1]) local b = char:FindFirstChild(pair[2]) if a and b and a:IsA("BasePart") and b:IsA("BasePart") then local pa, pb = project(a.Position), project(b.Position) if pa and pb then idx += 1 local f = s.skel[idx] if not f then f = espLine(espGui) s.skel[idx] = f end setLine(f, pa, pb, color, 1) end end end return idx end local fovRing = mk("Frame", { BackgroundTransparency = 1, BorderSizePixel = 0, AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(2, 2), Visible = false, }, espGui) local ringCorner = Instance.new("UICorner") ringCorner.CornerRadius = UDim.new(1, 0) ringCorner.Parent = fovRing local ringStroke = Instance.new("UIStroke") ringStroke.Thickness = 1.5 ringStroke.Color = Color3.fromRGB(255, 255, 255) ringStroke.Transparency = 0.35 ringStroke.Parent = fovRing local lastShown = {} local cleanupAccum = 0 ESP.updateRealBody = function() local root = LP.Character and LP.Character:FindFirstChild("HumanoidRootPart") local rb = ESP.realBody if not CONFIG.realBody or closed or not root then if rb then rb.box.Transparency = 1 if rb.shell then rb.shell.Transparency = 1 end rb.tile.Transparency = 1 rb.beam.Transparency = NumberSequence.new(1) rb.gui.Enabled = false rb.ring.Visible = false rb.tag.Visible = false end return end if not rb then rb = {} local function anchor(name) local part = Instance.new("Part") part.Name = name part.Anchored = true part.CanCollide = false part.CanQuery = false part.CanTouch = false part.CastShadow = false part.Transparency = 1 part.Size = Vector3.new(0.2, 0.2, 0.2) part.Parent = workspace local attach = Instance.new("Attachment") attach.Parent = part return part, attach end rb.real, rb.realAtt = anchor("RealBodyAnchor") rb.shown, rb.shownAtt = anchor("ShownBodyAnchor") -- the body, as a wireframe box: crisp, always on top, and with no volume to it local box = Instance.new("BoxHandleAdornment") box.Name = "RealBodyBox" box.Adornee = rb.real box.AlwaysOnTop = true box.ZIndex = 4 box.Size = Vector3.new(2.5, 5.3, 1.3) box.Transparency = 0.55 box.Color3 = Color3.fromRGB(120, 220, 255) box.Parent = rb.real rb.box = box -- footprint on the floor: anchors the marker to the ground under the real body local tile = Instance.new("Part") tile.Name = "RealBodyFootprint" tile.Anchored = true tile.CanCollide = false tile.CanQuery = false tile.CanTouch = false tile.CastShadow = false tile.Material = Enum.Material.Neon tile.Color = Color3.fromRGB(120, 220, 255) tile.Transparency = 0.7 tile.Size = Vector3.new(3, 0.06, 3) tile.Parent = workspace rb.tile = tile -- tether: a beam, so it stays a hairline at any distance and cannot be tripped over local beam = Instance.new("Beam") beam.Name = "RealBodyTether" beam.Attachment0 = rb.realAtt beam.Attachment1 = rb.shownAtt beam.Width0, beam.Width1 = 0.075, 0.075 beam.FaceCamera = true beam.LightEmission = 0.55 beam.Segments = 1 beam.Color = ColorSequence.new(Color3.fromRGB(120, 220, 255)) beam.Transparency = NumberSequence.new(0.25) beam.Parent = rb.real rb.beam = beam local gui = Instance.new("BillboardGui") gui.Name = "RealBodyTag" gui.AlwaysOnTop = true gui.LightInfluence = 0 gui.Adornee = rb.real gui.Size = UDim2.fromOffset(120, 16) gui.StudsOffsetWorldSpace = Vector3.new(0, 3.3, 0) gui.Parent = rb.real local text = Instance.new("TextLabel") text.BackgroundTransparency = 1 text.Font = Enum.Font.SourceSansBold text.TextSize = 12 text.TextColor3 = Color3.fromRGB(120, 220, 255) text.TextStrokeTransparency = 0.55 text.TextStrokeColor3 = Color3.fromRGB(0, 0, 0) text.Text = "" text.Size = UDim2.fromScale(1, 1) text.Parent = gui rb.gui, rb.text = gui, text -- chevron orbiting the crosshair: direction to the real body without looking away local ring = mk("Frame", { Name = "RealBodyChevron", BackgroundTransparency = 1, AnchorPoint = Vector2.new(0.5, 0.5), Visible = false, }, espGui) local function arm(anchorPoint, rotation) return mk("Frame", { BackgroundColor3 = Color3.fromRGB(120, 220, 255), BorderSizePixel = 0, AnchorPoint = anchorPoint, Position = UDim2.fromScale(0.5, 0), Size = UDim2.fromOffset(1.6, 11), Rotation = rotation, }, ring) end rb.armA, rb.armB = arm(Vector2.new(1, 1), -32), arm(Vector2.new(0, 1), 32) rb.tag = mk("TextLabel", { Name = "RealBodyDistance", BackgroundTransparency = 1, Font = Enum.Font.SourceSansBold, TextSize = 13, TextColor3 = Color3.fromRGB(120, 220, 255), TextStrokeTransparency = 0.55, TextStrokeColor3 = Color3.fromRGB(0, 0, 0), Text = "", AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(90, 16), Visible = false, }, espGui) rb.ring = ring ESP.realBody = rb end local offset = STATE.appliedOffset or Vector3.new(0, 0, 0) local shown = offset.Magnitude -- Only while there is something to show. At rest the replicated body and the body on -- screen are the same position, so the marker would be drawn inside the fake body - -- an outline around nothing, exactly where the body already is. What the marker is for -- is saying "this is where you really are", which only means anything once that is -- somewhere else. So it appears on the flick and disappears when the body settles. local flicked = rb.last and (offset - rb.last).Magnitude > 1 rb.last = offset if shown <= 0.75 then rb.box.Transparency = 1 rb.tile.Transparency = 1 rb.beam.Transparency = NumberSequence.new(1) rb.gui.Enabled = false rb.ring.Visible = false rb.tag.Visible = false if rb.jitAnchor then rb.pip.Transparency = 1 rb.capA.Transparency = 1 rb.capB.Transparency = 1 rb.jitGui.Enabled = false end return end -- Jitter mode shows the envelope rather than the position. A shake changes the body's -- place every few milliseconds, so a marker that follows it thrashes and says nothing - -- there is no "where you are" to draw. What is worth seeing is the reach: a band for the -- ground the body is covering, end caps for its extremes, and one smoothed pip showing -- roughly where in that band it currently is. Nothing here moves at the shake's rate. local jittering = CONFIG.jitterDodge and STATE.jitterUntil and os.clock() < STATE.jitterUntil if jittering and STATE.jitterAcross then if not rb.jitAnchor then -- no line on the ground: the caps and the pip already say where the shake reaches -- and roughly where the body is, and a bar across the floor just draws a stripe -- over the map. This is an invisible anchor for the label, nothing else. local anchor = Instance.new("Part") anchor.Name = "JitterAnchor" anchor.Anchored = true anchor.CanCollide = false anchor.CanQuery = false anchor.CanTouch = false anchor.CastShadow = false anchor.Transparency = 1 anchor.Size = Vector3.new(0.2, 0.2, 0.2) anchor.Parent = workspace local pip = Instance.new("Part") pip.Name = "JitterPip" pip.Shape = Enum.PartType.Ball pip.Anchored = true pip.CanCollide = false pip.CanQuery = false pip.CanTouch = false pip.CastShadow = false pip.Material = Enum.Material.Neon pip.Size = Vector3.new(0.9, 0.9, 0.9) pip.Parent = workspace local function cap() local c = Instance.new("Part") c.Anchored = true c.CanCollide = false c.CanQuery = false c.CanTouch = false c.CastShadow = false c.Material = Enum.Material.Neon c.Size = Vector3.new(0.3, 0.3, 0.3) c.Parent = workspace return c end local gui = Instance.new("BillboardGui") gui.Name = "JitterTag" gui.AlwaysOnTop = true gui.LightInfluence = 0 gui.Adornee = anchor gui.Size = UDim2.fromOffset(120, 16) gui.StudsOffsetWorldSpace = Vector3.new(0, 2.2, 0) gui.Parent = anchor local text = Instance.new("TextLabel") text.BackgroundTransparency = 1 text.Font = Enum.Font.SourceSansBold text.TextSize = 12 text.TextStrokeTransparency = 0.55 text.TextStrokeColor3 = Color3.fromRGB(0, 0, 0) text.Size = UDim2.fromScale(1, 1) text.Parent = gui rb.jitAnchor, rb.pip, rb.capA, rb.capB, rb.jitGui, rb.jitText = anchor, pip, cap(), cap(), gui, text end -- the marker proper stands down: none of it can render a position that changes faster -- than the frame that draws it rb.box.Transparency = 1 rb.tile.Transparency = 1 rb.beam.Transparency = NumberSequence.new(1) rb.gui.Enabled = false rb.ring.Visible = false rb.tag.Visible = false local real = root.Position local base = real - offset local axis = STATE.jitterAcross local low = math.min(CONFIG.jitterMin or 3, CONFIG.jitterMax or 15) local reach = math.max(STATE.jitterReach or 15, low) local colour = VIS.realBodyColor or Color3.fromRGB(120, 220, 255) rb.jitAnchor.CFrame = CFrame.new(base) for _, cap in ipairs({ rb.capA, rb.capB }) do cap.Color = colour cap.Transparency = 0.2 end rb.capA.CFrame = CFrame.new(base + axis * reach) rb.capB.CFrame = CFrame.new(base - axis * reach) -- the pip follows the body, eased, so it reads as sliding inside the band rather than -- flickering between its ends rb.pipPos = rb.pipPos and rb.pipPos:Lerp(real, 0.22) or real rb.pip.CFrame = CFrame.new(rb.pipPos) rb.pip.Color = colour rb.pip.Transparency = 0.25 rb.jitGui.Enabled = true rb.jitText.Text = string.format("SHAKING %.0f-%.0f studs", low, reach) rb.jitText.TextColor3 = colour return end if rb.jitAnchor and rb.jitGui.Enabled then rb.pip.Transparency = 1 rb.capA.Transparency = 1 rb.capB.Transparency = 1 rb.jitGui.Enabled = false rb.pipPos = nil end if flicked then rb.flashAt = os.clock() end -- colour says how far off the body is: cyan for a step that barely matters, amber for -- one big enough to decide a shot on its own local heat = math.clamp(shown / 9, 0, 1) local base = VIS.realBodyColor or Color3.fromRGB(120, 220, 255) local color = CONFIG.realBodyHeat and base:Lerp(Color3.fromRGB(255, 140, 70), heat) or base local scale = CONFIG.realBodyScale or 1 local real = root.Position local visible = real - offset local flash = rb.flashAt and (1 - math.clamp((os.clock() - rb.flashAt) / 0.28, 0, 1)) or 0 rb.real.CFrame = root.CFrame rb.shown.CFrame = CFrame.new(visible) rb.box.Color3 = color rb.box.Size = Vector3.new(2.5, 5.3, 1.3) * scale rb.box.Transparency = 0.55 - 0.3 * heat - 0.2 * flash if rb.shell then rb.shell.Size = Vector3.new(2.5, 5.3, 1.3) * scale end rb.text.TextColor3 = color rb.text.Text = string.format("OFF %.1f", shown) rb.gui.Enabled = CONFIG.realBodyTag ~= false -- footprint: under the real body, snapped to whatever is beneath it local under = workspace:Raycast(real, Vector3.new(0, -8, 0)) local floorY = under and under.Position.Y or (real.Y - 2.9) rb.tile.Position = Vector3.new(real.X, floorY + 0.04, real.Z) rb.tile.Color = color rb.tile.Transparency = 0.7 - 0.35 * flash local spread = (3 + 1.1 * flash) * scale rb.tile.Size = Vector3.new(spread, 0.06, spread) rb.beam.Color = ColorSequence.new(color) rb.beam.Transparency = NumberSequence.new(0.3 - 0.2 * flash) -- the chevron: angle to the real body, from the projection when it is on screen and -- from the camera's own axes when it is not, so it stays right even behind us local centre = screenCenter() local dx, dy local sp = project(real) if sp then dx, dy = sp.X - centre.X, sp.Y - centre.Y else local rel = real - Camera.CFrame.Position dx, dy = rel:Dot(Camera.CFrame.RightVector), -rel:Dot(Camera.CFrame.UpVector) end if (dx * dx + dy * dy) < 4 or CONFIG.realBodyChevron == false then rb.ring.Visible = false rb.tag.Visible = false else local angle = math.deg(math.atan2(dy, dx)) + 90 local radius = math.min(Camera.ViewportSize.X, Camera.ViewportSize.Y) * 0.17 rb.ring.Size = UDim2.fromOffset(radius * 2, radius * 2) rb.ring.Position = UDim2.fromOffset(centre.X, centre.Y) rb.ring.Rotation = angle rb.ring.Visible = true for _, part in ipairs({ rb.armA, rb.armB }) do part.BackgroundColor3 = color end local len = math.sqrt(dx * dx + dy * dy) rb.tag.Position = UDim2.fromOffset(centre.X + (dx / len) * (radius + 17), centre.Y + (dy / len) * (radius + 17)) rb.tag.Text = string.format("%.1f", shown) rb.tag.TextColor3 = color rb.tag.Visible = CONFIG.realBodyTag ~= false end end local function ESPRender(dt) Camera = workspace.CurrentCamera or Camera local vs = Camera.ViewportSize local center = screenCenter() -- independent of the ESP toggle: this is about the anti-aim, not the overlay's boxes if ESP.updateRealBody then ESP.updateRealBody() end if VIS.fovRing and VIS.enabled and CONFIG.enabled then local radius = math.max(1, fovPixelRadius()) fovRing.Visible = true fovRing.Position = UDim2.fromOffset(center.X, center.Y) fovRing.Size = UDim2.fromOffset(radius * 2, radius * 2) else fovRing.Visible = false end if not VIS.enabled then for model in pairs(lastShown) do hideState(model) end table.clear(lastShown) return end local camPos = Camera.CFrame.Position local seen = {} local active = (lock.model and lock.solution and lock.model.Parent) and lock.model or (STATE.lastSolution and STATE.lastSolution.model) for _, model in ipairs(targetPool()) do local head, root, player = validTarget(model) if head then local dist = (root.Position - camPos).Magnitude if dist <= VIS.maxDistance then local s = stateFor(model) -- highlight chams: colours, alpha, thermal breathing, -- through-wall depth and a distance fade, as in the source if s.chams then if VIS.chams then local breathe = 1 if VIS.chamsThermal then breathe = (math.atan(math.sin(os.clock() * 2)) * 2 / math.pi + 1) * 0.5 breathe = 0.35 + 0.65 * breathe end local fade = 1 if VIS.chamsFade then fade = math.clamp(1 - dist / math.max(VIS.maxDistance, 1), 0.2, 1) end -- bots are people, so they follow the player -- outline; dummies have their own local fillColor = VIS.chamsFill local outlineColor = isDummy(model) and VIS.chamsOutlineDummy or VIS.chamsOutline if VIS.chamsRainbow then local hue = (os.clock() % 5) / 5 fillColor = Color3.fromHSV(hue, 1, 1) outlineColor = Color3.fromHSV((hue + 0.5) % 1, 1, 1) end s.chams.Adornee = model s.chams.FillColor = fillColor s.chams.OutlineColor = outlineColor s.chams.FillTransparency = math.clamp(1 - (VIS.chamsFillAlpha / 100) * breathe * fade, 0, 1) s.chams.OutlineTransparency = math.clamp(1 - (VIS.chamsOutlineAlpha / 100) * breathe * fade, 0, 1) s.chams.DepthMode = VIS.chamsThrough and Enum.HighlightDepthMode.AlwaysOnTop or Enum.HighlightDepthMode.Occluded s.chams.Enabled = true else s.chams.Enabled = false end end -- material chams: the actual body parts recoloured, with the -- pattern animation and a rainbow option from the source if VIS.matChams then local matColor = VIS.matChamsColor if VIS.matChamsRainbow then matColor = Color3.fromHSV((os.clock() % 5) / 5, 1, 1) end ESP.applyMaterial(model, s.matStore, matColor, VIS.matChamsMaterial, VIS.matChamsPattern, VIS.matChamsAlpha, VIS.matChamsSpeed) elseif s.matStore and next(s.matStore) then ESP.restoreMaterial(s.matStore) end local ok, cf, size = pcall(model.GetBoundingBox, model) if ok then local minX, minY, maxX, maxY = projectBox(cf, size) if minX then minX, minY, maxX, maxY = minX - 1, minY - 1, maxX + 1, maxY + 1 local w, h = maxX - minX, maxY - minY if w >= 2 and h >= 2 then seen[model] = true hideAll(s) local color, thick if model == active then color, thick = Color3.fromRGB(255, 220, 120), 2 elseif isDummy(model) then color = VIS.dummyColor thick = 1 else -- no Player instance does not make it a dummy: bots and -- anyone mid-spawn are people and get the enemy colour color = isTeammate(model) and VIS.teamColor or VIS.enemyColor thick = 1 end if VIS.box then if VIS.boxStyle == "Full" then setLine(s.full[1], Vector2.new(minX, minY), Vector2.new(maxX, minY), color, thick) setLine(s.full[2], Vector2.new(minX, maxY), Vector2.new(maxX, maxY), color, thick) setLine(s.full[3], Vector2.new(minX, minY), Vector2.new(minX, maxY), color, thick) setLine(s.full[4], Vector2.new(maxX, minY), Vector2.new(maxX, maxY), color, thick) else local len = math.min(w, h) * 0.25 setLine(s.corners[1], Vector2.new(minX, minY), Vector2.new(minX + len, minY), color, thick) setLine(s.corners[2], Vector2.new(minX, minY), Vector2.new(minX, minY + len), color, thick) setLine(s.corners[3], Vector2.new(maxX - len, minY), Vector2.new(maxX, minY), color, thick) setLine(s.corners[4], Vector2.new(maxX, minY), Vector2.new(maxX, minY + len), color, thick) setLine(s.corners[5], Vector2.new(maxX - len, maxY), Vector2.new(maxX, maxY), color, thick) setLine(s.corners[6], Vector2.new(maxX, maxY - len), Vector2.new(maxX, maxY), color, thick) setLine(s.corners[7], Vector2.new(minX, maxY), Vector2.new(minX + len, maxY), color, thick) setLine(s.corners[8], Vector2.new(minX, maxY - len), Vector2.new(minX, maxY), color, thick) end end if VIS.filledBox then s.fill.Visible = true s.fill.Position = UDim2.fromOffset(minX, minY) s.fill.Size = UDim2.fromOffset(w, h) s.fill.BackgroundColor3 = color s.fill.BackgroundTransparency = VIS.fillAlpha end if VIS.healthBar then local humanoid = model:FindFirstChildOfClass("Humanoid") local frac = humanoid and math.clamp(humanoid.Health / math.max(humanoid.MaxHealth, 1), 0, 1) or 1 s.hbBg.Visible = true s.hbBg.Position = UDim2.fromOffset(minX - 7, minY - 1) s.hbBg.Size = UDim2.fromOffset(5, h + 2) s.hbBg.BackgroundColor3 = Color3.fromRGB(0, 0, 0) s.hbBg.BackgroundTransparency = 0.55 local fh = h * frac s.hbFill.Visible = true s.hbFill.Position = UDim2.fromOffset(minX - 6, minY + (h - fh)) s.hbFill.Size = UDim2.fromOffset(3, math.max(fh, 0.5)) s.hbFill.BackgroundColor3 = healthColor(frac) s.hbFill.BackgroundTransparency = 0 end if VIS.headDot then local hp = project(head.Position) if hp then s.dot.Visible = true s.dot.Position = UDim2.fromOffset(hp.X, hp.Y) s.dot.Size = UDim2.fromOffset(6, 6) s.dot.AnchorPoint = Vector2.new(0.5, 0.5) s.dot.BackgroundColor3 = color s.dot.BackgroundTransparency = 0 end end if VIS.skeleton then renderSkeleton(s, model, color) end if VIS.snapline then setLine(s.snap, Vector2.new(center.X, vs.Y), Vector2.new(minX + w * 0.5, maxY), color, 1) end local cx = minX + w * 0.5 local ts = VIS.textSize if VIS.name then s.name.Visible = true local label if isDummy(model) then label = model.Name elseif player then label = player.Name else label = model:GetAttribute("DisplayName") or model.Name end s.name.Text = label s.name.TextSize = ts s.name.Position = UDim2.fromOffset(cx, minY - ts - 3) s.name.TextColor3 = color end local below = maxY + ts * 0.5 + 4 if VIS.distance then s.dist.Visible = true s.dist.Text = string.format("%d studs", math.floor(dist + 0.5)) s.dist.TextSize = ts - 1 s.dist.Position = UDim2.fromOffset(cx, below) s.dist.TextColor3 = VIS.textColor below += ts end if VIS.weapon then local tool = model:FindFirstChildWhichIsA("Tool") local tag = player and (player:GetAttribute("TribeTag") or "") or "" local label = tool and tool.Name or (tag ~= "" and tag or nil) if label then s.wpn.Visible = true s.wpn.Text = label s.wpn.TextSize = ts - 1 s.wpn.Position = UDim2.fromOffset(cx, below) s.wpn.TextColor3 = VIS.textColor end end end end end end end end for model in pairs(lastShown) do if not seen[model] then hideState(model) end end lastShown = seen -- Reclaim states whose character is gone. cleanupAccum += dt if cleanupAccum >= 1 then cleanupAccum = 0 for model, s in pairs(ESP.pool) do if model.Parent == nil then destroyState(s) ESP.pool[model] = nil lastShown[model] = nil end end end end -- ------------------------------------------------------------------ instant draw / fire -- Drawn weapons compute their power from Charge() = (now - drawStart)/drawTime, so -- reporting a full charge makes a tap shoot at full power with no wait. Guns are -- gated by their own reload/cooldown state, which is cleared before each Fire. local handlerHooks = {} do local combatFolder = LP.PlayerScripts:FindFirstChild("Combat") local handlers = combatFolder and combatFolder:FindFirstChild("Handlers") if handlers then local function patchCharge(moduleName) local module = handlers:FindFirstChild(moduleName) if not module or not module:IsA("ModuleScript") then return end local ok, table_ = pcall(require, module) if not ok or type(table_) ~= "table" or type(table_.Charge) ~= "function" then return end local original = table_.Charge table_.Charge = function(self, ...) if CONFIG.instantFire then local full = self.drawing or self.winding if full then local ignored = original(self, ...) return 1 end end return original(self, ...) end handlerHooks[#handlerHooks + 1] = { table = table_, key = "Charge", original = original } end patchCharge("Bow") patchCharge("Throw") end end -- ------------------------------------------------------------------ auto fire -- Everything a weapon sends is gated server side: gun magazines refill on the -- server's own reload clock, so shots sent around that clock are simply refused. -- This drives the game's real input dispatcher instead, firing the moment the -- server is ready, which removes all of the idle time a human adds. local Controller = require(LP.PlayerScripts.Combat.Controller) local lastAutoShot = 0 -- ------------------------------------------------------------------ melee kill aura -- Melee swings are built client-side: the handler detects its own targets inside -- the weapon's range and arc, then sends that list along with the direction it -- swung in. Both are therefore ours to correct. Every swing is re-aimed at the -- nearest enemy in range (so the arc contains them no matter where we are looking) -- and the hit list is extended to everyone in range, which is the kill aura. local MELEE_KINDS = { Sword = true, Axe = true, Spear = true, Dagger = true, Club = true, Shield = true } local function meleeTool() local char = LP.Character local tool = char and char:FindFirstChildWhichIsA("Tool") if not tool then return nil end local weaponId = tool:GetAttribute("WeaponId") local definition = weaponId and Weapons.Get(weaponId) local kind = definition and definition.kind if not kind or not MELEE_KINDS[kind] then return nil end return tool, definition.tuning or {}, kind end -- Range and reach follow the game's own detection rules: it accepts anything -- within range + 1.2 studs of the body centre and less than 8 studs above or below. local function killAuraRange(tuning) local configured = tonumber(CONFIG.killAuraRange) or 0 if configured > 0 then return configured end return tuning.range or 8 end -- A small margin inside the accepted range: the list is checked by the server a -- ping later, and a target that steps back out of range takes the swing with it. local function killAuraTargets(range) local char = LP.Character local root = char and char:FindFirstChild("HumanoidRootPart") if not root then return {} end local limit = math.max(1, range + 1.2 - 0.6) local found = {} for _, model in ipairs(targetPool()) do local head, theirRoot = validTarget(model) if head and theirRoot then local delta = theirRoot.Position - root.Position local distance = delta.Magnitude if distance <= limit and math.abs(delta.Y) < 8 then found[#found + 1] = { model = model, root = theirRoot, distance = distance } end end end table.sort(found, function(a, b) return a.distance < b.distance end) return found end -- The server takes the swing's hit list from the payload and checks it against the -- swing direction, so a swing is only ever aimed at one target and lists what that -- aim cone contains. The target steps through everyone in range, so a group all get -- hit over successive swings. Sending this payload directly is what makes the aura -- silent: the game's own handler never runs, so nothing swings on screen. killAuraAim = function(tool, tuning, origin, target) local vector = target.root.Position - origin if vector.Magnitude < 0.01 then return nil end local direction = vector.Unit -- the swing is checked against the direction the body faces, and the body is -- turned through the game's own simulation input, which is why the handler -- calls FaceCamera() before every swing local flat = Vector3.new(direction.X, 0, direction.Z) if flat.Magnitude > 0.01 then flat = flat.Unit local yaw = math.atan2(-flat.X, -flat.Z) pcall(function() Simulation.SetFacing(yaw) end) end local half = math.rad((tonumber(tuning.arc) or 60) * 0.5) local cosine = math.cos(half) local hits = { target.model } local inRange = STATE.killAuraList or {} for _, entry in ipairs(inRange) do if entry.model ~= target.model then local to = entry.root.Position - origin if to.Magnitude > 0.01 and direction:Dot(to.Unit) >= cosine then hits[#hits + 1] = entry.model end end end return { tool = tool, dir = direction, hits = hits } end local function killAuraTick() if closed or not CONFIG.enabled or not CONFIG.killAura then return end local tool, tuning = meleeTool() if not tool then return end local char = LP.Character local humanoid = char and char:FindFirstChildOfClass("Humanoid") if not humanoid or humanoid.Health <= 0 then return end local inRange = killAuraTargets(killAuraRange(tuning)) if #inRange == 0 then return end STATE.killAuraList = inRange -- the weapon's own cooldown, plus a margin: the server refuses swings that -- arrive inside its own window and a refused swing is a wasted target local now = os.clock() if now - (STATE.killAuraLast or 0) < math.max(0.2, (tuning.cooldown or 0.5) + 0.06) then return end STATE.killAuraLast = now STATE.killAuraStep = ((STATE.killAuraStep or 0) % #inRange) + 1 local target = inRange[STATE.killAuraStep] local head = char:FindFirstChild("Head") local origin = head and head.Position or char.HumanoidRootPart.Position local payload = killAuraAim(tool, tuning, origin, target) if not payload then return end STATE.killAuraSwings = (STATE.killAuraSwings or 0) + 1 STATE.killAuraPrimary = target.model.Name pcall(function() WeaponAction:FireServer("Swing", payload) end) end local autoPress = false local drawTool = nil local drawUntil = 0 local function releasePrimary() if not autoPress then return end autoPress = false pcall(function() Controller.Dispatch("Primary", false) end) end local function pressPrimary() if autoPress then return end autoPress = true pcall(function() Controller.Dispatch("Primary", true) end) end local function autoFireTick() if closed or not CONFIG.enabled or not CONFIG.autoFire then releasePrimary() drawTool = nil return end local char = LP.Character local tool = char and char:FindFirstChildWhichIsA("Tool") if not tool then releasePrimary() drawTool = nil return end local tuning = shotParams(tool) if not tuning then releasePrimary() drawTool = nil return end local now = os.clock() local origin = aimOrigin(tool) -- Weapons that ramp their power from a draw fire on RELEASE, with the shot's -- speed taken from how long the input was held. Tapping them gives a fraction -- of the charge: the string never comes back, the arrow leaves slow, and a -- weapon with a minimum charge refuses the throw outright. So start the draw, -- hold it for the weapon's own draw time, then release. local drawTime = tonumber(tuning.drawTime) if drawTime and drawTime > 0 then if not autoPress then -- the handler refuses to start a draw inside its own cooldown, so wait -- it out rather than pressing into a dead input if now - lastAutoShot < (tuning.cooldown or 0.25) + 0.02 then return end local speed = tuning.speedMax or tuning.speed or 250 if not planShot(origin, speed, tuning.gravity or 0) then return end pressPrimary() drawTool = tool drawUntil = now + drawTime + 0.06 return end if drawTool ~= tool then releasePrimary() drawTool = nil return end if now < drawUntil then return end -- the draw is complete. If the target just stepped out, keep winding a -- moment longer rather than throwing the shot away, but never hold on. local speed = tuning.speedMax or tuning.speed or 250 if not planShot(origin, speed, tuning.gravity or 0) and now < drawUntil + 0.4 then return end releasePrimary() drawTool = nil lastAutoShot = now return end -- Magazine weapons fire on press. if now - lastAutoShot < 0.08 then return end -- let the weapon finish its own reload first: pressing into the crank leaves -- it half drawn and burns a shot the server refuses if tool:GetAttribute("Reloading") == true then return end local ammo, magazine = tool:GetAttribute("Ammo"), tool:GetAttribute("MaxAmmo") if type(ammo) == "number" and type(magazine) == "number" and magazine > 0 and ammo <= 0 then return end local speed = tuning.speed or tuning.speedMax or tuning.speedMin or 250 if not planShot(origin, speed, tuning.gravity or 0) then return end lastAutoShot = now releasePrimary() pressPrimary() task.delay(0.05, function() if autoPress and not closed then releasePrimary() end end) end task.spawn(function() while not closed do local ok, err = pcall(autoFireTick) if not ok then STATE.lastError = "autofire: " .. tostring(err) end local auraOk, auraErr = pcall(killAuraTick) if not auraOk then STATE.lastError = "killaura: " .. tostring(auraErr) end task.wait(0.05) end end) -- ------------------------------------------------------------------ auto heal -- The server ignores "Use" for a tool that is not the active one: sending the -- payload for a bandage sitting in the backpack (with no eq, with the current -- EquipAck, and with EquipAck + 1) is silently dropped, and a hand-rolled Equip -- loses the race against the client's own equip sequence. So the heal has to make -- the item active first, which means a swap: equip the item, let the game take it, -- then put the weapon back. -- -- Both halves go through the game's own entry points so its state stays coherent. -- Controller.Equip assigns the equip sequence the server matches the payload -- against, and the item's own handler is what sends the Use remote, so no payload -- is hand-built and nothing in the client's bookkeeping is bypassed. local healState = { phase = "idle", untilAt = 0, item = nil, main = nil, last = 0, hp = 0, charges = nil } local function autoHealTick() if closed or not CONFIG.enabled or not CONFIG.autoHeal then healState.phase = "idle" return end local char = LP.Character local hum = char and char:FindFirstChildOfClass("Humanoid") if not hum or hum.Health <= 0 then healState.phase = "idle" return end local now = os.clock() local equippedTool = char:FindFirstChildWhichIsA("Tool") if healState.phase ~= "idle" then if not healState.item or not healState.item.Parent then healState.phase = "idle" return end if healState.phase == "equip" then if now < healState.untilAt then return end -- the game refused the equip (dead, mouse unlocked, item moved) if equippedTool ~= healState.item then healState.phase = "idle" healState.last = now logEvent("heal", "equip refused for " .. tostring(healState.item.Name)) return end -- exactly what the item's own input handler does on a press pcall(function() Controller.Dispatch("Primary", true) end) task.delay(0.05, function() if not closed then pcall(function() Controller.Dispatch("Primary", false) end) end end) healState.phase = "use" healState.untilAt = now + (healState.useTime or 0.7) + 0.35 return end if healState.phase == "use" then -- polled rather than waited out: the item is consumed the moment the -- server takes it, and the weapon goes straight back at that point local charges = healState.item:GetAttribute("Charges") local consumed = charges ~= healState.charges local healed = hum.Health > healState.hp if consumed or healed or now >= healState.untilAt then CAL.heals = (CAL.heals or 0) + 1 if consumed or healed then CAL.healsLanded = (CAL.healsLanded or 0) + 1 logEvent("heal", string.format("%s: hp %.0f -> %.0f, charges %s -> %s", tostring(healState.item.Name), healState.hp, hum.Health, tostring(healState.charges), tostring(charges))) else logEvent("heal", tostring(healState.item.Name) .. " was not consumed") end -- only take the weapon back out if we are still the ones holding -- the item; if the player swapped by hand, leave their choice alone if healState.main and healState.main.Parent and equippedTool == healState.item then pcall(function() Controller.Equip(healState.main) end) end healState.phase = "swapped" healState.untilAt = now + 0.3 end return end if now < healState.untilAt then return end healState.phase = "idle" healState.last = now return end if now - healState.last < (CONFIG.autoHealCooldown or 3) then return end if hum.Health > hum.MaxHealth * ((CONFIG.autoHealBelow or 70) / 100) then return end -- pick a healing item with something left in it, from the backpack or hands local item, useTime local sources = { LP.Backpack, char } for _, container in ipairs(sources) do for _, tool in ipairs(container:GetChildren()) do if not item and tool:IsA("Tool") then local weaponId = tool:GetAttribute("WeaponId") local definition = weaponId and Weapons.Get(weaponId) if definition and definition.kind == "Herbs" then local charges = tool:GetAttribute("Charges") if type(charges) ~= "number" or charges >= 1 then item = tool useTime = definition.tuning and definition.tuning.useTime end end end end end if not item then return end healState.item = item healState.useTime = useTime healState.main = (equippedTool and equippedTool ~= item) and equippedTool or nil healState.hp = hum.Health healState.charges = item:GetAttribute("Charges") healState.phase = "equip" healState.untilAt = now + 0.5 pcall(function() Controller.Equip(item) end) end -- ------------------------------------------------------------------ hvH countermeasures -- Anyone aiming at us only ever sees a delayed, interpolated copy of our -- position, and has to extrapolate that copy forward to place a shot. A smooth -- path makes that extrapolation exact, and per-frame noise is averaged away by -- their fit. What a fit cannot survive is a step: the body is held inside a -- wall-checked offset for a fraction of a second and then snapped to a fresh one -- several studs away. The apparent velocity across a single jump is far larger -- than anything a sampled window can track, so their lead is computed from a -- poisoned estimate and the projectile is thrown at a point we are not at, while -- the authoritative copy the server resolves damage against is the offset one. -- -- The offset is stripped before it is reapplied, so the copies never accumulate -- and the character still walks normally. Our own shots read the muzzle after -- the offset is applied, so they stay exact. local hvRandom = Random.new(os.clock() * 1e6 // 1) local lastOffset = Vector3.new(0, 0, 0) local desyncOffset = Vector3.new(0, 0, 0) local desyncNext = 0 -- Pick a destination that is open floor on every side of the step. Blinking into -- geometry or through a wall is visible to every client watching, and it also -- drops us inside a part where the humanoid cannot move. -- Nearest enemy, used to aim the step and to size it. Only consulted when a new -- offset is chosen, not per frame. local function nearestThreat(root) local best, bestDist, bestPos for _, model in ipairs(targetPool()) do local head, theirRoot = validTarget(model) if head and theirRoot then local delta = theirRoot.Position - root.Position local dist = delta.Magnitude if dist < 90 and (not bestDist or dist < bestDist) then best, bestDist, bestPos = model, dist, theirRoot.Position end end end return best, bestDist, bestPos end -- Jump out of the way when someone nearby attacks. This is deliberately separate from -- the desync: it runs with the desync switched off, it fires on the attack itself -- rather than on arrival, and it holds the displaced position for the whole flight so -- the target stays out of the way for every step of the projectile's path, not just -- at the instant it lands. STATE.armDodge = function(velocity, attackerPos) if not CONFIG.dodge then return end local char = LP.Character local root = char and char:FindFirstChild("HumanoidRootPart") if not root then return end local now = os.clock() -- a rapid weapon is firing again inside this dodge, so a step per shot is the only -- answer: the guard is skipped for those and each step takes a fresh side local forced = STATE.dodgeForce STATE.dodgeForce = nil -- do not restart a dodge that is already running: holding one position is what -- makes it work if not forced and STATE.dodgeUntil and now < STATE.dodgeUntil - 0.05 then -- worth saying out loud: a move that never happened looks exactly like a move that -- failed, and this is the one that decides whether a shot lands if not STATE.skipLogged or now - STATE.skipLogged > 1 then STATE.skipLogged = now logEvent("dodge", string.format("move skipped, body already displaced for another %.0fms", ((STATE.dodgeUntil or now) - now) * 1000)) end return end local low = math.min(CONFIG.dodgeDistanceMin or 6, CONFIG.dodgeDistanceMax or 10) local high = math.max(CONFIG.dodgeDistanceMin or 6, CONFIG.dodgeDistanceMax or 10) local base = root.Position local params = Util.WorldParams(Util.Teammates()) -- The axis is the SHOT'S line, not the shooter's. Whenever a projectile's velocity is -- in hand, that is the direction the arrow is travelling, and a step perpendicular to it -- cannot change how far along the flight the arrow is - it changes only how far off the -- path the body is, which is the whole of what a dodge is. Deriving the axis from the -- shooter's position instead put the step at an angle to the arrow whenever the two -- differed, which at close range and with any lead they do. local pathOrigin = typeof(attackerPos) == "Vector3" and attackerPos or nil local pathDir if typeof(velocity) == "Vector3" then local flat = Vector3.new(velocity.X, 0, velocity.Z) if flat.Magnitude > 0.5 then pathDir = flat.Unit end end if not pathDir and pathOrigin then local flat = Vector3.new(base.X - pathOrigin.X, 0, base.Z - pathOrigin.Z) if flat.Magnitude > 0.5 then pathDir = flat.Unit end end local across if pathDir then across = Vector3.new(-pathDir.Z, 0, pathDir.X) else local a = hvRandom:NextNumber(0, math.pi * 2) across = Vector3.new(math.cos(a), 0, math.sin(a)) end -- And the side is chosen to move AWAY from the line, never across it. This is the part -- that was landing the body on the shot: with the body already off to one side, a step -- to the same side arrives back on the line, and a step to the opposite side crosses it. -- The signed distance from the line says which way is out, and only when the body is -- effectively on the line does the choice fall back to alternating. local side if pathOrigin and pathDir then local rel = Vector3.new(base.X - pathOrigin.X, 0, base.Z - pathOrigin.Z) local signed = rel:Dot(across) if math.abs(signed) > 1 then side = signed > 0 and 1 or -1 end end if not side then side = STATE.dodgeSide and -STATE.dodgeSide or (hvRandom:NextNumber() < 0.5 and 1 or -1) end STATE.dodgeSide = side local dir = across * side -- The length is drawn from the configured range, so the displacement is a different -- size every time; the ends and the midpoint stay as fallbacks for when the drawn one -- lands in geometry. local lengths = { hvRandom:NextNumber(low, high), high, low, (low + high) * 0.5 } -- Where the body has just been. Alternating sides alone is not enough to keep it out of -- its own footprints: left, right, left arrives back at the same left, and with the -- length drawn from a range it can arrive back almost exactly on top of it. So the last -- few landings are remembered and a candidate within a few studs of any of them is -- rejected while anything else is still open - a body that returns to a place it has -- already stood is a body that can be pre-aimed at it. local recent = STATE.recentSteps if recent then local now2 = os.clock() for index = #recent, 1, -1 do if now2 - recent[index].at > 3 then table.remove(recent, index) end end else recent = {} STATE.recentSteps = recent end local function isFresh(step) for _, previous in ipairs(recent) do if (step - previous.step).Magnitude < 3 then return false end end return true end local function place(step) STATE.dodgeOffset = step STATE.dodgeSince = now -- smart dodge: the hold is what the attacker's own weapon and the arc of their shot -- call for, rather than a fixed number local hold = CONFIG.dodgeSmart and STATE.dodgeSmartHold or nil STATE.dodgeSmartHold = nil STATE.dodgeUntil = now + (hold or CONFIG.dodgeHold or 0.4) STATE.dodgeAcross = across recent[#recent + 1] = { step = step, at = now } while #recent > 3 do table.remove(recent, 1) end -- Never sit on one offset for long: a draw can be a full second, and a second parked -- in one place is a second to be re-aimed at, so a fresh side is drawn part way -- through whatever the hold turns out to be. if CONFIG.dodgeSmart and (STATE.dodgeUntil - now) > 0.45 then -- stage three: keep moving inside the hold, spaced off the flight so there are -- roughly two flicks per flight rather than one long park local interval = math.clamp((STATE.dodgeFlight or 0.35) * 0.5, 0.18, 0.6) STATE.dodgeStepAt = now + math.min(interval, (STATE.dodgeUntil - now) * 0.9) end STATE.dodges = (STATE.dodges or 0) + 1 -- who to face for the duration: turned toward the attacker, the step reads as a -- sidestep rather than a slide, and other clients rebuild the body's pose from the -- root they receive, so that is the copy the attacker sees pointed at them STATE.dodgeFace = CONFIG.dodgeFacing and typeof(attackerPos) == "Vector3" and attackerPos or nil -- second step, scheduled inside the flight: their lead was computed at release -- against the position they believed we held, so a fresh displacement while the -- projectile is still travelling is one their aim cannot correct for if CONFIG.dodgeDouble then local arrival if typeof(velocity) == "Vector3" and velocity.Magnitude > 1 and typeof(attackerPos) == "Vector3" then arrival = (base - attackerPos).Magnitude / velocity.Magnitude end if arrival then STATE.dodgeUntil = now + math.max(CONFIG.dodgeHold or 0.4, arrival * 0.55 + 0.12) end STATE.dodgeStepAt = arrival and (now + arrival * 0.55) or nil else STATE.dodgeStepAt = nil end logEvent("dodge", string.format("%.1f studs across, holding %.0fms%s", step.Magnitude, (STATE.dodgeUntil - now) * 1000, STATE.dodgeStepAt and string.format(", re-step in %.0fms", (STATE.dodgeStepAt - now) * 1000) or "")) return true end local function open(step) if workspace:Raycast(base, step, params) then return false end local landing = base + step local floor = workspace:Raycast(landing + Vector3.new(0, 3, 0), Vector3.new(0, -14, 0), params) local headroom = workspace:Raycast(landing + Vector3.new(0, 1, 0), Vector3.new(0, 3.5, 0), params) return floor ~= nil and headroom == nil end local fallback for _, heading in ipairs({ dir, -dir }) do for _, distance in ipairs(lengths) do local step = heading * distance if open(step) then if isFresh(step) then return place(step) end fallback = fallback or step end end end -- every open spot was one just left: take it rather than not move at all, but only after -- exhausting the alternatives if fallback then return place(fallback) end if not STATE.dodgeBlocked or now - STATE.dodgeBlocked > 1 then STATE.dodgeBlocked = now logEvent("dodge", string.format("nowhere lateral to step %.1f studs from here", high)) end return false end -- Real projectile data, read from the game's own weapon table rather than guessed. -- -- The numbers matter more than they look: launch speeds run from 110 studs per second to -- 900, so the same distance is up to an eight-fold difference in how long the shot is in -- the air, and the cadences run from a 150ms short bow to a 1.1 second longbow draw. A -- fixed dodge cannot be right against both ends of that - it either steps too early, which -- is a step they can re-aim at, or too late, which is a step that happens after the arrow -- has already passed. Indexed by projectile kind, which is what an incoming shot carries. STATE.weaponByProjectile = {} ;(function() local ok, list = pcall(function() return Weapons.All() end) if ok and type(list) == "table" then for _, def in pairs(list) do local tuning = type(def) == "table" and def.tuning or nil local kind = tuning and tuning.projectile local speed = tuning and tonumber(tuning.speed or tuning.speedMax) if kind and speed and speed > 0 then local cooldown = tonumber(tuning.cooldown) or 0 local before = STATE.weaponByProjectile[kind] -- Several weapons share a projectile kind - short bow and longbow are both -- "Arrow" - and a kind lookup cannot tell them apart. Keep the most rapid -- one, because the cadence is what decides whether the dodge has to step -- again mid-flight, and stepping again costs nothing while failing to step -- against a 150ms bow costs the exchange. if not before or (cooldown > 0 and (before.cooldown <= 0 or cooldown < before.cooldown)) then STATE.weaponByProjectile[kind] = { speed = speed, cooldown = cooldown, draw = tonumber(tuning.drawTime or tuning.windupTime) or 0, id = def.id or def.kind, } end end end end end)(); -- Ping, as a round trip, refreshed once a second. -- -- This is the number that decides whether a dodge can work at all. A step we apply now is -- only known to the server a round trip later, and the server resolves the hit when its own -- copy of the arrow reaches its own copy of us. So the time actually available to move is -- the travel time minus the round trip, and for anything fast at close range - a musket, a -- flintlock, a knife - that difference goes to zero: the hit is already decided before the -- broadcast that tells us a shot exists has finished arriving. STATE.netRtt = function() local ok, ping = pcall(function() return LP:GetNetworkPing() end) if ok and type(ping) == "number" and ping > 0 then return math.clamp(ping, 0, 0.4) end return 0.05 end -- Where an incoming shot will be, and whether it is coming for us. -- -- The broadcast carries the launch velocity and the gravity the server applies, so the -- trajectory is known rather than estimated: the same arc, evaluated forward against the -- position the body is actually holding. That answers the two questions a dodge needs - will -- it land at all, and if so how long is there - from the shot's own path instead of from a -- speed figure. Distance over speed ignores the arc entirely, and the arc is most of the -- answer at range: a sling stone hangs far past its speed, an arrow with elevation is still -- climbing when a straight-line estimate says it has arrived. -- -- `advance` is how far into the flight the shot already is when we hear about it, which is -- the one-way delay - the same number the ping costs us, spent before we can react. STATE.shotImpact = function(origin, velocity, gravity, advance, target, radius) local down = Vector3.new(0, -(gravity or 0), 0) local speed = velocity.Magnitude local horizon = math.clamp(advance + 1.6, 0.1, 3) -- step by distance along the path, not by clock: at these speeds a time step crosses the -- body entirely between samples local stride = math.max(0.15, math.min(0.45, (radius or 1.7) * 0.3)) local steps = math.clamp(math.ceil(speed * (horizon - advance) / stride), 12, 700) local closest, closestAt = math.huge, nil for i = 0, steps do local t = advance + (horizon - advance) * (i / steps) local point = origin + velocity * t + down * (0.5 * t * t) local distance = (point - target).Magnitude if distance < closest then closest, closestAt = distance, t end if distance <= (radius or 1.7) then return true, t, distance end end return false, closestAt, closest end -- what the game says the attacker is holding: from the weapon id when the windup carried -- one, otherwise from the projectile kind of the shot already in the air STATE.attackerWeapon = {} STATE.weaponFor = function(shooterChar, projectileKind, weaponId) if weaponId then local ok, def = pcall(function() return Weapons.Get(weaponId) end) local tuning = ok and type(def) == "table" and def.tuning or nil if tuning then local speed = tonumber(tuning.speed or tuning.speedMax) if speed and speed > 0 then local info = { speed = speed, cooldown = tonumber(tuning.cooldown) or 0, draw = tonumber(tuning.drawTime or tuning.windupTime) or 0, id = def.id or def.kind } if shooterChar then STATE.attackerWeapon[shooterChar] = info end return info end end end if shooterChar and STATE.attackerWeapon[shooterChar] then return STATE.attackerWeapon[shooterChar] end if projectileKind and STATE.weaponByProjectile[projectileKind] then return STATE.weaponByProjectile[projectileKind] end return nil end local function pickDesync(root) local amount = CONFIG.desyncAmount or 0 if amount <= 0.05 then return Vector3.new(0, 0, 0) end local params = Util.WorldParams(Util.Teammates()) local origin = root.Position -- Size the step from the threat: further away means their lead is longer and a -- larger step is affordable, and someone inside melee reach needs the widest -- step available, since melee is validated against a cone around their aim. local threat, threatDist, threatPos = nearestThreat(root) if threat then local scale = math.clamp((threatDist or 30) / 30, 0.7, 1.5) amount = amount * scale end if STATE.dodgeBoost then amount = amount * 1.35 end -- Aim across the line they are aiming along: a step perpendicular to it turns -- into the largest possible angular error, which is what beats both a lead and -- a melee cone. local preferred if CONFIG.desyncAimAware and threatPos then local line = Vector3.new(threatPos.X - origin.X, 0, threatPos.Z - origin.Z) if line.Magnitude > 0.5 then local across = Vector3.new(-line.Z, 0, line.X).Unit preferred = hvRandom:NextNumber() < 0.5 and across or -across -- a slight along-the-line component so the step is not perfectly regular preferred = (preferred + line.Unit * hvRandom:NextNumber(-0.18, 0.18)).Unit end end local directions = { 0, 45, 90, 135, 180, 225, 270, 315 } if preferred then -- Rank the compass by how perpendicular each bearing is to the line they are -- aiming along, then shuffle among the best few rather than taking the top one. -- Taking the top one made every step identical whenever the threat and the -- geometry held still, which pinned the body to one side instead of moving it -- around. Ties on the compass are a coin toss, so this is where the variation -- has to come from. local across = Vector3.new(-preferred.Z, 0, preferred.X) local acrossAngle = math.atan2(across.Z, across.X) table.sort(directions, function(a, b) local da = math.abs(math.cos(math.rad(a) - acrossAngle)) local db = math.abs(math.cos(math.rad(b) - acrossAngle)) return da < db end) -- Six of the eight bearings, not four: four kept every step inside the narrow -- perpendicular band, which reads as sliding sideways rather than moving around. local shortlist = { directions[1], directions[2], directions[3], directions[4], directions[5], directions[6] } for i = #shortlist, 2, -1 do local j = hvRandom:NextInteger(1, i) shortlist[i], shortlist[j] = shortlist[j], shortlist[i] end directions = shortlist end local start = 1 for index = 1, #directions do local degrees = directions[(start + index - 2) % #directions + 1] local radians = math.rad(degrees) local step = Vector3.new(math.cos(radians), 0, math.sin(radians)) * amount -- the line has to be clear, and the landing spot has to be supported if not workspace:Raycast(origin, step, params) then local landing = origin + step local floor = workspace:Raycast(landing + Vector3.new(0, 3, 0), Vector3.new(0, -12, 0), params) local headroom = workspace:Raycast(landing + Vector3.new(0, 1, 0), Vector3.new(0, 3.5, 0), params) if floor and not headroom then -- Height as well as distance. The body they are solving against is narrow -- vertically and they lead along the ground, so a few studs of height buys -- more miss than the same few studs sideways, and it stacks with them. if CONFIG.desyncVertical and (CONFIG.desyncVerticalAmount or 0) > 0.2 then local lift = (CONFIG.desyncVerticalAmount or 2.5) * hvRandom:NextNumber(0.75, 1.25) local raised = step + Vector3.new(0, lift, 0) if not workspace:Raycast(landing, Vector3.new(0, lift + 1, 0), params) then return raised end end return step end end end return Vector3.new(0, 0, 0) end local function hvhTick() if closed then return end local char = LP.Character local root = char and char:FindFirstChild("HumanoidRootPart") if not root then return end if STATE.pendingDodge and os.clock() >= STATE.pendingDodge.at then local pending = STATE.pendingDodge STATE.pendingDodge = nil -- restore what this move was planned with, so nothing that happened in between can -- have taken it away if pending.force then STATE.dodgeForce = true end if pending.hold then STATE.dodgeSmartHold = pending.hold end if STATE.armDodge then STATE.armDodge(nil, pending.position) end end local dodging = STATE.dodgeUntil and os.clock() < STATE.dodgeUntil local hopping = STATE.meleeOffset and STATE.meleeUntil and os.clock() < STATE.meleeUntil local jittering = CONFIG.jitterDodge and STATE.jitterUntil and os.clock() < STATE.jitterUntil local wanted = CONFIG.desync or dodging or hopping or jittering if not wanted and lastOffset == Vector3.new(0, 0, 0) then return end -- Strip the previous offset first: applying a fresh offset to the already -- offset position walks the character across the map and breaks movement. local clean = root.Position - lastOffset local offset = Vector3.new(0, 0, 0) local now = os.clock() if CONFIG.desync and not dodging then -- Walk-only: while standing still the character is left exactly where the game -- puts it, so a stationary player has no displacement at all. The moment they -- move, the next frame picks a step up straight away. local moving = true if CONFIG.desyncWalkOnly then local humanoid = char:FindFirstChildOfClass("Humanoid") local velocity = root.AssemblyLinearVelocity local flatSpeed = Vector3.new(velocity.X, 0, velocity.Z).Magnitude local input = humanoid and humanoid.MoveDirection.Magnitude or 0 moving = input > 0.1 or flatSpeed > 1 if not moving then desyncOffset = Vector3.new(0, 0, 0) desyncNext = 0 end end local dodge = false if CONFIG.desyncReactive and STATE.dodgeAt and now >= STATE.dodgeAt then dodge = true STATE.dodgeAt = nil STATE.dodgeBoost = true end if dodge or now >= desyncNext then -- Vary the interval. A held offset already poisons their lead, but a fixed -- cadence is the one part of it that stays fittable - measured across steps, -- a regular interval is a signature. Randomising it means the next jump has no -- time they can be ready for. local jitter = math.clamp(CONFIG.desyncJitter or 0, 0, 0.6) local interval = (CONFIG.desyncInterval or 0.18) * (1 + hvRandom:NextNumber(-jitter, jitter)) desyncNext = now + math.max(0.05, interval) desyncOffset = pickDesync(root) STATE.dodgeBoost = nil end if moving then offset = desyncOffset end elseif not dodging then desyncOffset = Vector3.new(0, 0, 0) end -- The dodge rides on top of the desync, and works with the desync switched off. -- It eases in and out instead of snapping: a hard jump would move the camera a -- frame before the correction catches up, and a single 14-stud teleport is exactly -- the shape of movement a server is likely to object to. if dodging and STATE.dodgeOffset then -- Second step, inside the flight. Their lead was fixed at release, so moving again -- now is movement their committed shot cannot follow - the first step only has to -- beat where they thought we were, and this one beats where the arrow is going. if (CONFIG.dodgeDouble or CONFIG.dodgeSmart) and STATE.dodgeStepAt and now >= STATE.dodgeStepAt then STATE.dodgeStepAt = nil local params = Util.WorldParams(Util.Teammates()) local flip if CONFIG.dodgeSmart and STATE.dodgeAcross then -- the opposite side to the one being held, always: never the same side twice, -- so the body crosses the line rather than drifting along one end of it local low, high = CONFIG.dodgeDistanceMin or 6, CONFIG.dodgeDistanceMax or 10 if low > high then low, high = high, low end local side = STATE.dodgeSide and -STATE.dodgeSide or (hvRandom:NextNumber() < 0.5 and 1 or -1) STATE.dodgeSide = side -- same rule as a fresh dodge: nothing within a few studs of the last few -- landings, drawn again until something is open for _ = 1, 4 do local attempt = STATE.dodgeAcross * side * hvRandom:NextNumber(low, high) local fresh = true for _, previous in ipairs(STATE.recentSteps or {}) do if (attempt - previous.step).Magnitude < 3 then fresh = false break end end flip = attempt if fresh then break end end else -- back across the first step: the body ends twice the displacement from -- where it started, on the far side of the point their lead was aimed at flip = -STATE.dodgeOffset end if not workspace:Raycast(clean, flip, params) then local landing = clean + flip local floor = workspace:Raycast(landing + Vector3.new(0, 3, 0), Vector3.new(0, -14, 0), params) local headroom = workspace:Raycast(landing + Vector3.new(0, 1, 0), Vector3.new(0, 3.5, 0), params) if floor and not headroom then STATE.dodgeOffset = flip STATE.dodgeSince = now STATE.dodgeSteps = (STATE.dodgeSteps or 1) + 1 STATE.recentSteps = STATE.recentSteps or {} STATE.recentSteps[#STATE.recentSteps + 1] = { step = flip, at = now } while #STATE.recentSteps > 3 do table.remove(STATE.recentSteps, 1) end -- still a long way to go: draw again rather than park here, spaced off the -- flight time so the body keeps moving for as long as the shot is flying if CONFIG.dodgeSmart and (STATE.dodgeUntil - now) > 0.45 then local interval = math.clamp((STATE.dodgeFlight or 0.35) * 0.5, 0.18, 0.6) STATE.dodgeStepAt = now + math.min(interval, (STATE.dodgeUntil - now) * 0.9) end logEvent("dodge", string.format("re-stepped %.1f studs, %.0fms left of the hold", flip.Magnitude, (STATE.dodgeUntil - now) * 1000)) end end end local ramp = STATE.dodgeRamp or CONFIG.dodgeRamp or 0 local factor = 1 if ramp > 0.001 then local rampIn = (now - (STATE.dodgeSince or now)) / ramp local rampOut = ((STATE.dodgeUntil or now) - now) / ramp factor = math.clamp(math.min(rampIn, rampOut), 0, 1) end offset = offset + STATE.dodgeOffset * factor STATE.dodgeBodyFactor = factor STATE.dodgeBodyOn = true else STATE.dodgeOffset = nil STATE.dodgeSince = nil end if STATE.meleeOffset and STATE.meleeUntil and now < STATE.meleeUntil then offset = offset + STATE.meleeOffset end -- Jitter dodge: a fresh side and a fresh length every interval, so no position survives -- long enough to be aimed at. It replaces what the desync and the dodge were doing rather -- than stacking with them - two offsets at once is still one position. if jittering and STATE.jitterAcross and now >= (STATE.jitterNext or 0) then STATE.jitterNext = now + math.max(0.003, CONFIG.jitterInterval or 0.01) local low = math.min(CONFIG.jitterMin or 3, CONFIG.jitterMax or 15) local high = math.min(math.max(CONFIG.jitterMin or 3, CONFIG.jitterMax or 15), STATE.jitterReach or 15) if high < low then high = low end -- alternate: consecutive positions are on opposite sides and cannot coincide local side = STATE.jitterSide and -STATE.jitterSide or (hvRandom:NextNumber() < 0.5 and 1 or -1) STATE.jitterSide = side STATE.jitterOffset = STATE.jitterAcross * side * hvRandom:NextNumber(low, high) STATE.jitterTicks = (STATE.jitterTicks or 0) + 1 end if jittering and STATE.jitterOffset then offset = STATE.jitterOffset end -- the dodge rides on the desync, but the two must not stack into a jump nobody -- would mistake for moving: cap the total at a bit over the larger of the two local cap = math.max((CONFIG.desyncAmount or 0) + (CONFIG.desyncVertical and (CONFIG.desyncVerticalAmount or 0) or 0), CONFIG.dodgeDistanceMax or 10, CONFIG.meleeHop and (CONFIG.meleeHopHeight or 0) or 0, CONFIG.jitterMax or 15) * 1.2 if offset.Magnitude > cap then offset = offset.Unit * cap end STATE.appliedOffset = offset if offset == Vector3.new(0, 0, 0) and lastOffset == Vector3.new(0, 0, 0) then return end local frame = root.CFrame local target = clean + offset -- While dodging, face whoever we are dodging: turned toward them, level, position -- displaced. Other clients rebuild the body's pose from the root they receive, so -- that is the copy the attacker and the server see. local face = STATE.dodgeFace local faced = false if dodging and face and CONFIG.dodgeFacing then local flat = Vector3.new(face.X - target.X, 0, face.Z - target.Z) if flat.Magnitude > 0.5 then root.CFrame = CFrame.lookAt(target, target + flat.Unit) faced = true end end if not faced then -- otherwise keep the body's own rotation: only the position is displaced root.CFrame = CFrame.new(target) * (frame - frame.Position) end lastOffset = offset end -- The camera is placed off the character, so it inherits the body's displacement -- and the whole view jumps with it. Pull the camera back by the same vector and our -- screen sits exactly where it would with the desync off, while the displaced body -- is still the one replicated to everyone else and resolved against by the server. -- -- The camera script binds at Camera + 1 and re-derives the camera from the body -- every frame, so this has to be the next thing after it. It reads the offset the -- body is currently carrying, which is the same value that camera was built from. local CAMERA_STEP = "TribesOverlayDesyncCamera" RunService:BindToRenderStep(CAMERA_STEP, Enum.RenderPriority.Camera.Value + 2, function() if closed then return end if STATE.cameraStepOff then return end local blend = math.clamp((CONFIG.desyncLocalSteady or 0) / 100, 0, 1) -- The desync follows the local view compensation setting. A dodge does not: "steady -- camera on dodge" means the view is held completely still through the step, and with it -- off the view travels with the body so the step reads as a sidestep. Leaving a dodge on -- the desync's setting is what let 85% of an 18-stud flick land in the view. if STATE.dodgeUntil and os.clock() < STATE.dodgeUntil then blend = CONFIG.dodgeSteadyCamera and 1 or 0 end -- Through a jitter the camera is corrected at FULL strength, whatever the local view -- compensation is set to. A shake is tens of studs, every few milliseconds: leaving any of -- that uncorrected is a view that thrashes, and the desync's compensation setting is about -- whether the desync is visible, not about whether a shake should be. This step runs after -- the game's own camera script re-derives the view from the body, so it is the correction -- that actually lands. if STATE.jitterUntil and os.clock() < STATE.jitterUntil then blend = 1 end if blend <= 0 or lastOffset == Vector3.new(0, 0, 0) then return end local camera = workspace.CurrentCamera if not camera then return end local correction = lastOffset * blend local ok = pcall(function() camera.CFrame = camera.CFrame - correction end) if not ok then STATE.cameraStepFails = (STATE.cameraStepFails or 0) + 1 if STATE.cameraStepFails >= 5 then STATE.cameraStepOff = true logEvent("render", "camera step lacks instance access, disabled") end return end -- The body we are actually shown is not the character at all: the game hides -- the character's parts from its own client and draws a separate R15 clone -- under the camera, positioned from the character each frame. That clone moves -- with the desync, so it is the one thing that would still visibly jump. It is -- rebuilt from the character every frame, so a per-frame correction neither -- accumulates nor fights anything. local drawn = camera:FindFirstChild("TribesDrawnBody") if drawn then for _, item in ipairs(drawn:GetDescendants()) do if item:IsA("BasePart") then item.CFrame = item.CFrame - correction end end end end) -- ------------------------------------------------------------------ render loop -- One frame of everything the overlay draws. Kept as a function so it can be called -- either straight from the render step or, if the environment refuses instance access -- from render callbacks, from a coroutine created here at load time (which carries the -- loading thread's capability instead). local function renderFrame(dt) -- Some executors run callbacks (render steps, coroutines) at a lower capability -- than the console thread, which makes instance writes inside them fail with -- "lacking capability Plugin" even though the same write works from the console. -- Raise it back every frame; harmless where the functions do not exist. pcall(setthreadidentity, 8) pcall(setthreadcontext, 8) -- one-off check: can this thread still create and parent after the load finished? if not STATE.renderSelfTest then STATE.renderSelfTest = true local steps = {} local function attempt(name, fn) local ok, err = pcall(fn) steps[#steps + 1] = string.format("%s:%s", name, ok and "ok" or ("FAIL " .. tostring(err):gsub("%s+", " "):sub(1, 40))) end attempt("frame", function() local f = Instance.new("Frame") f.BorderSizePixel = 0 f.BackgroundColor3 = Color3.new(1, 1, 1) f.Visible = false f.Parent = espGui f:Destroy() end) attempt("anchor", function() local f = Instance.new("Frame") f.AnchorPoint = Vector2.new(0.5, 0.5) f.Parent = espGui f:Destroy() end) attempt("uicorner", function() local f = Instance.new("Frame") local c = Instance.new("UICorner") c.CornerRadius = UDim.new(0, 4) c.Parent = f f.Parent = espGui f:Destroy() end) attempt("uistroke", function() local f = Instance.new("Frame") local st = Instance.new("UIStroke") st.Color = Color3.new(1, 1, 1) st.Parent = f f.Parent = espGui f:Destroy() end) attempt("textlabel", function() local t = Instance.new("TextLabel") t.BackgroundTransparency = 1 t.Font = Enum.Font.Code t.TextSize = 13 t.TextColor3 = Color3.new(1, 1, 1) t.TextStrokeTransparency = 0.4 t.TextStrokeColor3 = Color3.fromRGB(0, 0, 0) t.TextXAlignment = Enum.TextXAlignment.Center t.TextYAlignment = Enum.TextYAlignment.Center t.AnchorPoint = Vector2.new(0.5, 0.5) t.Size = UDim2.fromOffset(280, 17) t.Visible = false t.Parent = espGui t:Destroy() end) attempt("highlight", function() local h = Instance.new("Highlight") h.Enabled = false h.FillTransparency = 1 h.OutlineTransparency = 0 h.DepthMode = Enum.HighlightDepthMode.AlwaysOnTop h.Parent = espGui h:Destroy() end) attempt("ugradient", function() local f = Instance.new("Frame") local g = Instance.new("UIGradient") g.Color = ColorSequence.new(Color3.new(1,1,1), Color3.new(0,0,0)) g.Parent = f f.Parent = espGui f:Destroy() end) STATE.renderSelfTestResult = table.concat(steps, " ") end local tickOk, tickErr = pcall(hvhTick, dt) if not tickOk then STATE.tickError = tostring(tickErr) else STATE.tickError = nil end local healOk, healErr = pcall(autoHealTick) if not healOk then STATE.lastError = "autoheal: " .. tostring(healErr) end local ok, err = xpcall(ESPRender, function(message) local trace = debug and debug.traceback and debug.traceback("", 2) or "" return tostring(message) .. " @ " .. tostring(trace):gsub("%s+", " "):sub(1, 220) end, dt) if not ok then local text = tostring(err) STATE.espError = text -- "lacking capability" means this thread may not touch instances at all: it is -- not a bug in the drawing, it is the thread. Hand the work to the loader thread. if not STATE.renderProxy and (text:find("capability") or text:find("cannot access")) then STATE.renderProxy = true logEvent("render", "render callback lacks instance access, moving to the loader thread") end local now = os.clock() if not STATE.espErrorAt or now - STATE.espErrorAt > 1 then STATE.espErrorAt = now end else STATE.espError = nil end end local renderConn = RunService.RenderStepped:Connect(function(dt) if STATE.renderProxy then -- just a heartbeat: the drawing happens on the loader thread below STATE.proxyTick = (STATE.proxyTick or 0) + 1 STATE.proxyDt = dt return end renderFrame(dt) end) -- Created during the load, so it runs with the loading thread's capability rather than -- the render callback's. Idle unless the proxy above asked for it. task.spawn(function() local seen = -1 while not closed do if STATE.renderProxy and STATE.proxyTick ~= seen then seen = STATE.proxyTick local ok, err = pcall(renderFrame, STATE.proxyDt or 0.016) if not ok then STATE.proxyError = tostring(err) end end task.wait() end end) -- ------------------------------------------------------------------ api / unload local api local function unload() if closed then return end closed = true CONFIG.enabled = false VIS.enabled = false -- never leave the trigger held or a draw winding releasePrimary() drawTool = nil pcall(function() RunService:UnbindFromRenderStep(CAMERA_STEP) end) -- put the head back and unhide it renderConn:Disconnect() denialConnection:Disconnect() if STATE.unreliableFx then pcall(function() STATE.unreliableFx:Disconnect() end) end hitConnection:Disconnect() if Effects.Projectile == projectileHook then Effects.Projectile = originalProjectile end if originalPellets and Effects.Pellets == pelletsHook then Effects.Pellets = originalPellets end if getrawmetatable(game).__namecall == namecallHook then hookmetamethod(game, "__namecall", originalNamecall) end pending = nil for _, hook in ipairs(handlerHooks) do hook.table[hook.key] = hook.original end for model, s in pairs(ESP.pool) do destroyState(s) ESP.pool[model] = nil end table.clear(ESP.pool) if ESP.realBody then local rb = ESP.realBody for _, key in ipairs({ "real", "shown", "tile", "jitAnchor", "pip", "capA", "capB" }) do pcall(function() rb[key]:Destroy() end) end pcall(function() rb.ring:Destroy() end) pcall(function() rb.tag:Destroy() end) ESP.realBody = nil end for _, part in ipairs(ESP.tracerPool or {}) do pcall(function() part:Destroy() end) end for _, entry in ipairs(ESP.markerPool or {}) do pcall(function() entry.part:Destroy() end) end pcall(function() espGui:Destroy() end) pcall(function() local lib = getgenv().Library if lib and lib.Unload then lib:Unload() end end) pcall(function() sg:Destroy() end) if getgenv().TribesOverlay == api then getgenv().TribesOverlay = nil end if getgenv().SilentAim == api then getgenv().SilentAim = nil end end api = { CONFIG = CONFIG, VIS = VIS, STATE = STATE, UI = UI, ESP = ESP, solve = aimDirection, hasLine = hasLine, -- the whole shot plan, so the aim can be checked without firing plan = function(origin, speed, gravity) return planShot(origin, speed, gravity) end, wallStats = function() return WALL end, -- run the wallbang decision by hand against a solution table, the same call the shot -- path makes, so the origin it picks can be checked without a shot in the air wallCheck = function(origin, solution, speed, gravity) return applyWallbang(origin, solution, speed, gravity) end, armOriginProbe = function(studs, shots) CONFIG.originProbe = tonumber(studs) or 6 STATE.probeLeft = tonumber(shots) or 3 return CONFIG.originProbe, STATE.probeLeft end, disarmOriginProbe = function() CONFIG.originProbe = 0 return 0 end, wallOrigin = function(origin, aimPoint, model, maxOffset) return findWallbangOrigin(origin, aimPoint, model, maxOffset or CONFIG.wallbangMax) end, viewLag = viewLag, worldClear = worldClear, selectTarget = function(origin) return selectTarget(origin, nil) end, saveSettings = function() local ok, message = saveConfig() return ok, message end, loadSettings = function() local ok, message = loadConfig() uiRefresh() return ok, message end, -- exposed so the drawing can be run by hand from the console thread, which is how -- a build that draws in the render callback can be told apart from one that draws renderFrame = function(dt) return renderFrame(dt or 0.016) end, -- Runs the drawing one stage at a time, so an environment that refuses a -- specific operation can be pinned to it. Defined here as a closure because the -- main chunk is at its local limit. -- arms a reactive dodge by hand, the same state the incoming-attack handlers set, -- so the consumer side can be checked without somebody shooting at us -- delegates to the real thing: an earlier stub here only set a legacy field, so -- anything calling it (including my own tests) did nothing armDodge = function(velocity, attackerPos) return STATE.armDodge(velocity, attackerPos) end, tickError = function() return STATE.tickError end, -- feed a synthetic Swing through the real handler: exercises the trigger, the -- reach gate, the placement and the jump, which is the whole chain espBisect = function() local out = {} local function stage(name, fn) local ok, err = xpcall(fn, function(message) local trace = debug and debug.traceback and debug.traceback("", 2) or "" return tostring(message) .. " @ " .. tostring(trace):gsub("%s+", " "):sub(1, 160) end) out[#out + 1] = string.format("%-16s %s%s", name, ok and "ok" or "FAIL", ok and "" or (" " .. tostring(err):sub(1, 200))) end local model = targetPool()[1] stage("viewport", function() local _ = Camera.ViewportSize end) stage("camera cframe", function() local _ = Camera.CFrame end) stage("project point", function() local _ = Camera:WorldToViewportPoint(Vector3.new(0, 0, 0)) end) stage("fov ring write", function() fovRing.Visible = true fovRing.Position = UDim2.fromOffset(1, 1) fovRing.Size = UDim2.fromOffset(2, 2) fovRing.Visible = VIS.fovRing end) stage("target pool", function() local _ = #targetPool() end) if model then stage("valid target", function() local _ = validTarget(model) end) stage("bounding box", function() local ok, cf, size = pcall(model.GetBoundingBox, model) if not ok then error(cf) end local _ = projectBox(cf, size) end) stage("build state", function() local _ = stateFor(model) end) local state = ESP.pool[model] if state then stage("hide all", function() hideAll(state) end) stage("line write", function() state.full[1].Visible = true state.full[1].Position = UDim2.fromOffset(1, 1) state.full[1].Visible = false end) stage("name write", function() state.name.Text = "x" state.name.Visible = false end) stage("chams write", function() state.chams.Adornee = model state.chams.FillColor = Color3.new(1, 0, 0) state.chams.Enabled = false end) end end return out end, espRender = function(dt) return ESPRender(dt or 0.016) end, solveSpeed = function(origin, direction, impact, gravity) return solveLaunchSpeed(origin, direction, impact, gravity) end, -- Walk the operations the interface actually performs and report which one the -- environment refuses. For a build where the ESP draws nothing, this names the -- blocked access instead of leaving it to guesswork from a message alone. probe = function() local out = {} local function step(name, fn) local ok, err = pcall(fn) out[#out + 1] = string.format("%-18s %s%s", name, ok and "ok" or "FAIL", ok and "" or (" " .. tostring(err):gsub("%s+", " "):sub(1, 120))) end step("viewport", function() local _ = Camera.ViewportSize end) step("gui frame", function() local f = Instance.new("Frame") f.Parent = espGui f:Destroy() end) step("ui stroke", function() local f = Instance.new("Frame") local u = Instance.new("UIStroke") u.Parent = f f.Parent = espGui f:Destroy() end) step("text label", function() local t = Instance.new("TextLabel") t.Text = "x" t.Parent = espGui t:Destroy() end) step("highlight", function() local h = Instance.new("Highlight") h.Parent = espGui h:Destroy() end) step("billboard gui", function() local b = Instance.new("BillboardGui") b.Parent = espGui b:Destroy() end) step("world part", function() local x = Instance.new("Part") x.Parent = workspace x:Destroy() end) step("collection tag", function() local _ = CollectionService:HasTag(workspace, "Combatant") end) step("read target", function() local m = targetPool()[1] if m then local h = m:FindFirstChild("Head") if h then local _ = h.Position end local hum = m:FindFirstChildOfClass("Humanoid") if hum then local _ = hum.Health end end end) step("write target part", function() local m = targetPool()[1] local part = m and (m:FindFirstChild("UpperTorso") or m:FindFirstChild("Head")) if part then local was = part.Material part.Material = Enum.Material.Plastic part.Material = was end end) step("write highlight", function() local m = targetPool()[1] if m then local h = Instance.new("Highlight") h.Parent = espGui h.Adornee = m h.FillColor = Color3.new(1, 0, 0) h.DepthMode = Enum.HighlightDepthMode.AlwaysOnTop h:Destroy() end end) return out end, -- Test hooks that cannot leak. A value set here is remembered, restored by testEnd -- whatever happens to the caller, and excluded from any save in the meantime, so -- changing what the overlay aims at during a test cannot end up in the saved -- settings. Anything toggling team filtering or the aim cone goes through this. testSet = function(values) STATE.testPins = STATE.testPins or {} for key, value in pairs(values) do if STATE.testPins[key] == nil then STATE.testPins[key] = CONFIG[key] end CONFIG[key] = value end return true end, testEnd = function() local pins = STATE.testPins STATE.testPins = nil if not pins then return false end for key, value in pairs(pins) do CONFIG[key] = value end saveConfig() return true end, -- shots taken at us, and how many of them landed, per shooter dodgeScore = function() local shots, taken = CAL.shotsFor or {}, CAL.takenFor or {} local out = {} for name, count in pairs(shots) do local landed = taken[name] or 0 out[#out + 1] = string.format("%s: %d shots at us, %d landed (%.0f%%)", name, count, landed, count > 0 and 100 * landed / count or 0) end table.sort(out) return out, { shots = STATE.shotsAtUs or 0, landed = CAL.taken or 0, predictedHit = CAL.takenPredictedHit or 0, predictedMiss = CAL.takenPredictedMiss or 0 } end, solvedSpeeds = function() local out = {} for kind, list in pairs(CAL.solvedSpeed or {}) do local sum, n = 0, 0 local copy = table.clone(list) table.sort(copy) for _, v in ipairs(copy) do sum += v n += 1 end out[kind] = { median = copy[math.floor(n / 2) + 1] or 0, mean = n > 0 and sum / n or 0, n = n } end return out end, speedReport = function() local out = {} for kind, list in pairs(CAL.speedSamples or {}) do local sum, n = 0, 0 for _, v in ipairs(list) do sum += v n += 1 end out[kind] = { mean = n > 0 and sum / n or 0, n = n } end return out end, isAirborne = function(model) return model and targetIsAirborne(model) or false end, targetState = function(model) local root = model and model:FindFirstChild("HumanoidRootPart") if not root then return nil end return targetState(model, root) end, checkPath = pathCheck, checkPath = hasLine, planMortar = function(model) local char = LP.Character local tool = char and char:FindFirstChildWhichIsA("Tool") local tuning = shotParams(tool) if not tuning then return nil, "No equipped bow" end local origin = Util.ShotOrigin(tool, "Muzzle") local target if model then local head, root, player, dummy, hitbox = validTarget(model) if head then target = { model = model, head = head, part = aimPartFor(head, hitbox, player, dummy) } end end target = target or selectTarget(origin) if not target then return nil, "No target in FOV" end local speed = tuning.speedMax or tuning.speed or 250 local gravity = tuning.gravity or 60 local point = target.part.Position if (CONFIG.leadScale or 0) > 0 then local flight = (point - origin).Magnitude / speed point = point + target.velocity * flight * (CONFIG.leadScale or 0) end local direction = aimDirection(origin, point, speed, gravity, "high") if not direction then return nil, "No direction" end return { target = target.model, direction = direction, speed = speed, gravity = gravity, charge = 1, drawSeconds = tuning.drawTime or 0, distance = (point - origin).Magnitude } end, resolve = function(charge) local char = LP.Character local tool = char and char:FindFirstChildWhichIsA("Tool") local tuning = shotParams(tool) if not tuning then return nil end local _, speed = Combat.DrawnShot(tuning, charge or 1) local origin = Util.ShotOrigin(tool, "Muzzle") return planShot(origin, speed, tuning.gravity or 60) end, stats = function() local tracked, drawn, objects = 0, 0, 0 for _, s in pairs(ESP.pool) do tracked += 1 local any = false for _, list in ipairs({ s.full, s.corners, s.skel }) do for _, f in ipairs(list) do objects += 1 if f.Visible then any = true end end end for _, f in ipairs({ s.fill, s.hbBg, s.hbFill, s.dot, s.snap, s.name, s.dist, s.wpn }) do objects += 1 if f.Visible then any = true end end if any then drawn += 1 end end return { tracked = tracked, drawn = drawn, objects = objects, pool = STATE.pool, ring = fovRing.Visible, radius = fovRing.Visible and fovPixelRadius() or 0, redirected = STATE.redirected } end, hideMenu = function() if uiWindow then uiWindow:SetOpen(false) end end, showMenu = function() if uiWindow then uiWindow:SetOpen(true) end end, unload = unload, } getgenv().TribesOverlay = api getgenv().SilentAim = api return { installed = true, gui = sg, place = game.PlaceId }