--!nocheck -- Tribes 3v3 - CS:GO style overlay. -- One file: ballistic silent-aim engine + screen-space ESP + touch-friendly CS:GO menu. -- Aim solves the real projectile (outgoing speed/gravity/charge), not a draw-time guess. -- Fast polynomial root isolation finds the interception time; every candidate is then -- re-validated by marching the curved path so visual, namecall and server direction agree. -- API: getgenv().TribesOverlay (CONFIG, VIS, STATE, unload). getgenv().SilentAim aliases it. 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 -- ------------------------------------------------------------------ teardown old ;(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)(); -- ------------------------------------------------------------------ game modules local Shared = ReplicatedStorage:WaitForChild("Shared") local Combat = require(Shared.Combat) local Weapons = require(Shared.Weapons) -- the game's own movement simulation, the same table its camera module turns the -- body through (Shared.Camera requires Shared.Simulation) 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 -- ------------------------------------------------------------------ config 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 velocityWindow = 0.12, leadSeconds = 0, acquireDelay = 0.12, -- require a stable line of sight before shooting maxFlight = 0.35, -- do not take shots that cannot realistically land hitGate = true, -- auto-fire only when the solve is a likely hit viewDelay = 0, -- extra lead beyond the ping (constant time) leadScale = 0.5, -- half the target's predicted travel; pinned in code delayComp = 0, -- extra constant trim on top of the above 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, -- integrate the shot before spending the arrow on it 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" predictMode = "simple", -- "simple" | "off" } local VIS = { enabled = true, 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 TRACK = setmetatable({}, { __mode = "k" }) 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 -- ------------------------------------------------------------------ velocity fit -- Subtract a nearby reference before accumulating: Vector3 uses float32 and -- fitting absolute positions around X=5000 loses the sub-stud motion we need. local function velocityFit(samples, window) local latest = samples[#samples] if not latest then return nil end local count, st, stt, sx, sy, sz, stx, sty, stz = 0, 0, 0, 0, 0, 0, 0, 0, 0 local firstTime for _, sample in ipairs(samples) do local dt = sample.t - latest.t if dt >= -window then firstTime = firstTime or sample.t local x = sample.p.X - latest.p.X local y = sample.p.Y - latest.p.Y local z = sample.p.Z - latest.p.Z count += 1 st += dt; stt += dt * dt sx += x; sy += y; sz += z stx += dt * x; sty += dt * y; stz += dt * z end end if count < 2 or latest.t - firstTime < 0.02 then return nil end local denominator = count * stt - st * st if denominator <= 1e-12 then return nil end return Vector3.new((count * stx - st * sx) / denominator, (count * sty - st * sy) / denominator, (count * stz - st * sz) / denominator) end -- Least-squares quadratic through the sample window, in a frame centred on the -- newest sample (numerically stable at large world coordinates). Gives position, -- velocity and acceleration so a target that reverses or curves mid-flight is -- predicted instead of assumed straight. local function stateFit(samples, window) local newest = samples[#samples] if not newest then return nil end local n, st, st2, st3, st4 = 0, 0, 0, 0, 0 local sy, sty, st2y = 0, 0, 0 -- x local sy2, sty2, st2y2 = 0, 0, 0 -- y local sy3, sty3, st2y3 = 0, 0, 0 -- z for _, sample in ipairs(samples) do local dt = sample.t - newest.t if dt >= -window then local t2, t3, t4 = dt * dt, dt * dt * dt, dt * dt * dt * dt local x, y, z = sample.p.X - newest.p.X, sample.p.Y - newest.p.Y, sample.p.Z - newest.p.Z n += 1 st += dt; st2 += t2; st3 += t3; st4 += t4 sy += x; sty += dt * x; st2y += t2 * x sy2 += y; sty2 += dt * y; st2y2 += t2 * y sy3 += z; sty3 += dt * z; st2y3 += t2 * z end end if n < 6 then return nil end -- determinant of three column vectors local function det3(a, b, c) return a[1] * (b[2] * c[3] - b[3] * c[2]) - b[1] * (a[2] * c[3] - a[3] * c[2]) + c[1] * (a[2] * b[3] - a[3] * b[2]) end local col1 = { n, st, st2 } local col2 = { st, st2, st3 } local col3 = { st2, st3, st4 } local base = det3(col1, col2, col3) if math.abs(base) < 1e-9 then return nil end -- Cramer's rule: replace one column with the right-hand side vector local function solveAxis(rhs) local c0 = det3(rhs, col2, col3) / base local c1 = det3(col1, rhs, col3) / base local c2 = det3(col1, col2, rhs) / base return c0, c1, c2 end local c0x, c1x, c2x = solveAxis({ sy, sty, st2y }) local c0y, c1y, c2y = solveAxis({ sy2, sty2, st2y2 }) local c0z, c1z, c2z = solveAxis({ sy3, sty3, st2y3 }) local velocity = Vector3.new(c1x, c1y, c1z) local accel = Vector3.new(2 * c2x, 2 * c2y, 2 * c2z) if velocity.Magnitude > 250 or accel.Magnitude > 600 then return nil end -- RMS error of the curve against the samples: a low value means the target -- really is moving smoothly and the acceleration is trustworthy. local rms, count = 0, 0 for _, sample in ipairs(samples) do local dt = sample.t - newest.t if dt >= -window then local fitted = Vector3.new( newest.p.X + c0x + c1x * dt + c2x * dt * dt, newest.p.Y + c0y + c1y * dt + c2y * dt * dt, newest.p.Z + c0z + c1z * dt + c2z * dt * dt) rms += (fitted - sample.p).Magnitude ^ 2 count += 1 end end rms = count > 0 and math.sqrt(rms / count) or 99 return newest.p, velocity, accel, Vector3.new(c0x, c0y, c0z), rms end -- Alpha-beta tracking filter per target. A fixed-window slope is unbiased but -- noisy, and that noise is what makes the lead alternate between too far and too -- short. The filter predicts forward every sample and corrects with the new -- measurement, so it keeps a clean velocity and still reacts quickly to a turn -- (the gains open up when the residual is large). local function filterUpdate(rec, z, now) if not rec.fp then rec.fp, rec.fv, rec.fpT = z, Vector3.new(0, 0, 0), now return end local dt = now - rec.fpT if dt <= 0 then return end if dt > 0.4 then -- respawn or teleport: restart cleanly rec.fp, rec.fv, rec.fpT = z, Vector3.new(0, 0, 0), now return end local predicted = rec.fp + rec.fv * dt local residual = z - predicted local err = residual.Magnitude -- smoothed so one bad frame cannot mark a target unstable rec.fResidual = (rec.fResidual or 0) * 0.9 + err * 0.1 -- Velocity is corrected as beta/dt, so beta must stay well under 1 at 60Hz or -- the estimate rings. A residual too large for one frame is a respawn, not -- motion, and resets the track instead of yanking the velocity. if err > 3 then rec.fp, rec.fv, rec.fpT = z, Vector3.new(0, 0, 0), now return end -- Humanoids accelerate hard and often. Low gains lag the truth, which -- overshoots decelerating targets and undershoots accelerating ones, so the -- gains open as soon as the residual shows real motion. local alpha, beta = 0.45, 0.14 if err > 0.6 then alpha, beta = 0.7, 0.30 end rec.fp = predicted + residual * alpha rec.fv = rec.fv + residual * (beta / dt) -- a humanoid cannot exceed this; anything more is a bad sample if rec.fv.Magnitude > 60 then rec.fv = rec.fv.Unit * 60 end rec.fpT = now end -- state advanced to now, from the filter -- How well the filter's prediction matches the incoming measurements: low means -- the target is moving predictably, high means it is juking and no lead will hold. STATE.trackStability = function(model) local rec = TRACK[model] if not rec or not rec.fpT then return 1 end return rec.fResidual or 0 end local function filteredState(model, root) local rec = TRACK[model] if rec and rec.fp and rec.fpT then local dt = math.max(0, os.clock() - rec.fpT) if dt < 0.3 then return rec.fp + rec.fv * dt, rec.fv end end return root.Position, root.AssemblyLinearVelocity end local function targetVelocity(model, root) -- the filter is the primary estimate; the window fit is only a fallback local rec = TRACK[model] if rec and rec.fp and rec.fpT and os.clock() - rec.fpT < 0.2 then return rec.fv end local record = TRACK[model] if record and record.velocity and os.clock() - record.at < 0.1 then return record.velocity end return root.AssemblyLinearVelocity end -- What the client renders is one round-trip behind the server, so at impact the -- server's copy of the target is further along than the client-predicted one. -- Feed half the measured ping in as extra lead on top of the slider value. local measuredGravity, measuredLead, oneWayLatency local killAuraAim local function leadTime() local lead = math.max(0, CONFIG.leadSeconds) if STATE.aimWeaponId then lead += measuredLead(STATE.aimWeaponId) end lead += oneWayLatency() lead += CONFIG.viewDelay or 0 lead += CONFIG.delayComp or 0 return math.clamp(lead, 0, 0.45) end local heartbeat = RunService.Heartbeat:Connect(function() if closed then return end local now = os.clock() local pool = targetPool() STATE.pool = #pool for _, model in ipairs(pool) do local head, root = validTarget(model) if head then local record = TRACK[model] if not record or record.root ~= root then record = { root = root, samples = {} } TRACK[model] = record end local samples = record.samples local last = samples[#samples] if last and ((root.Position - last.p).Magnitude > 12 or now - last.t > 0.25) then table.clear(samples) end samples[#samples + 1] = { t = now, p = root.Position } while #samples > 1 and (now - samples[1].t > CONFIG.velocityWindow or #samples > 32) do table.remove(samples, 1) end filterUpdate(record, root.Position, now) local humanoid = model:FindFirstChildOfClass("Humanoid") local inAir = humanoid and humanoid.FloorMaterial == Enum.Material.Air and 1 or 0 record.air = (record.air or 0) * 0.82 + inAir * 0.18 record.velocity = velocityFit(samples, CONFIG.velocityWindow) local pos, vel, acc, offset, rms = stateFit(samples, math.max(CONFIG.velocityWindow, 0.22)) if pos and vel then record.origin, record.velocity2, record.accel, record.fitRms = pos, vel, acc, rms else record.velocity2, record.accel = nil, nil end record.at = now end end for model in pairs(TRACK) do if not validTarget(model) then TRACK[model] = nil end end end) -- ------------------------------------------------------------------ ballistics STATE.valueAt = function(coefficients, x) local value = coefficients[#coefficients] for i = #coefficients - 1, 1, -1 do value = value * x + coefficients[i] end return value end -- Derivative roots partition the interval into monotonic pieces. Unlike -- fixed-point iteration or a coarse sign scan this also finds narrow/tangent -- roots near maximum range. Coefficients are ascending powers. STATE.rootsIn = function(coefficients, low, high) while #coefficients > 1 and math.abs(coefficients[#coefficients]) < 1e-12 do table.remove(coefficients) end local degree = #coefficients - 1 if degree == 0 then return {} end if degree == 1 then local root = -coefficients[1] / coefficients[2] return root >= low and root <= high and { root } or {} end local derivative = {} for i = 2, #coefficients do derivative[i - 1] = (i - 1) * coefficients[i] end local boundaries = STATE.rootsIn(derivative, low, high) table.insert(boundaries, 1, low) boundaries[#boundaries + 1] = high local roots = {} local function add(root) if #roots == 0 or math.abs(root - roots[#roots]) > 1e-7 then roots[#roots + 1] = root end end for i, a in ipairs(boundaries) do local fa = STATE.valueAt(coefficients, a) local scale = 0 for j, c in ipairs(coefficients) do scale += math.abs(c) * math.abs(a) ^ (j - 1) end if math.abs(fa) <= math.max(1e-9, scale * 1e-11) then add(a) end local b = boundaries[i + 1] if b then local fb = STATE.valueAt(coefficients, b) if (fa < 0 and fb > 0) or (fa > 0 and fb < 0) then local left, right = a, b for _ = 1, 48 do local middle = (left + right) * 0.5 local fm = STATE.valueAt(coefficients, middle) if (fa < 0 and fm <= 0) or (fa > 0 and fm >= 0) then left = middle else right = middle end end add((left + right) * 0.5) end end end return roots end local function solveIntercept(origin, point, velocity, speed, gravity, maxTime, arc) -- gravity here is the RELATIVE gravity and may be negative when a target is -- falling faster than the arrow drops; only absurd values are rejected. if speed <= 0 or speed ~= speed or gravity < -400 then return nil end local d = point - origin local coefficients = { d.X * d.X + d.Y * d.Y + d.Z * d.Z, 2 * (d.X * velocity.X + d.Y * velocity.Y + d.Z * velocity.Z), velocity.X ^ 2 + velocity.Y ^ 2 + velocity.Z ^ 2 + gravity * d.Y - speed ^ 2, gravity * velocity.Y, 0.25 * gravity ^ 2, } local limit = maxTime or Ballistics.MAX_LIFETIME local searchLimit = limit if arc == "high" then if gravity <= 0 then return nil end local leading = coefficients[5] local bound = 0 for i = 1, 4 do bound = math.max(bound, math.abs(coefficients[i] / leading)) end searchLimit = math.max(limit, 1 + bound) end local roots = STATE.rootsIn(coefficients, 0.0001, searchLimit) local startIndex, endIndex, increment = 1, #roots, 1 if arc == "high" then startIndex, endIndex, increment = #roots, #roots, 1 end for index = startIndex, endIndex, increment do local time = roots[index] if not time or time > limit then return nil end local displacement = d + velocity * time + Vector3.new(0, 0.5 * gravity * time * time, 0) local length = displacement.Magnitude if length > 0.001 and math.abs(length - speed * time) <= 0.001 then local direction = displacement.Unit local arrow = origin + direction * (speed * time) - Vector3.new(0, 0.5 * gravity * time * time, 0) local future = point + velocity * time local residual = (arrow - future).Magnitude local arrivalY = direction.Y * speed - gravity * time if residual <= 0.005 and (arc ~= "high" or arrivalY < 0) then return { direction = direction, time = time, future = future, residual = residual, arrivalY = arrivalY } end end end return nil end local function minimumLaunch(origin, point, velocity, gravity, maxTime) local d = point - origin local lengthSquared = d.X ^ 2 + d.Y ^ 2 + d.Z ^ 2 if lengthSquared < 1e-6 or gravity <= 0 then return nil end local dot = d.X * velocity.X + d.Y * velocity.Y + d.Z * velocity.Z local times = STATE.rootsIn({ -2 * lengthSquared, -2 * dot, 0, gravity * velocity.Y, 0.5 * gravity ^ 2 }, 0.0001, maxTime) times[#times + 1] = maxTime local bestSpeed, bestTime for _, time in ipairs(times) do local launch = d / time + velocity + Vector3.new(0, gravity * time * 0.5, 0) local speed = launch.Magnitude if not bestSpeed or speed < bestSpeed then bestSpeed, bestTime = speed, time end end return bestSpeed, bestTime end -- 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 -- March the real curved path against the target translated at constant velocity, -- so mesh collision geometry is respected instead of a straight-line guess. -- A hit anywhere on the model counts: the head is the aim point, but an arm, -- accessory or torso along the way is still a landed shot. local function pathCheck(origin, solution, model, head, velocity, speed, gravity, lead, strictHead, targetGravity) local targetParams = RaycastParams.new() targetParams.FilterType = Enum.RaycastFilterType.Include targetParams.FilterDescendantsInstances = { model } local worldParams = Util.WorldParams(Util.Teammates()) local excluded = worldParams.FilterDescendantsInstances excluded[#excluded + 1] = model worldParams.FilterDescendantsInstances = excluded local shot = Ballistics.new(origin, solution.direction * speed, gravity, 0) local time = 0 local finish = math.min(solution.time + 0.05, Ballistics.MAX_LIFETIME) while time < finish do local dt = math.min(CONFIG.castStep, finish - time) local start, segment = Ballistics.Advance(shot, dt) local wall = workspace:Raycast(start, segment, worldParams) local advance = lead + time local drop = (targetGravity or 0) * 0.5 * advance * advance local relativeStart = start - (velocity * advance - Vector3.new(0, drop, 0)) local relativeSegment = segment - velocity * dt + Vector3.new(0, (targetGravity or 0) * 0.5 * ((advance + dt) ^ 2 - advance ^ 2), 0) local target = workspace:Raycast(relativeStart, relativeSegment, targetParams) if target then local fraction = relativeSegment.Magnitude > 0 and target.Distance / relativeSegment.Magnitude or 0 if wall and wall.Distance < segment.Magnitude * fraction then return false, "obstacle" end local hit = target.Instance local accepted if strictHead and head then accepted = hit == head or hit:IsDescendantOf(head) else accepted = hit == model or hit:IsDescendantOf(model) end if accepted then return true, hit.Name end return false, "hit " .. hit.Name end if wall then return false, "obstacle" end if shot.traveled > Ballistics.MAX_DISTANCE then return false, "range" end time += dt end return false, "no intersection" end -- Head first (best damage), then the torso as a second aim point when the head -- line is blocked by an accessory or a raised weapon. local function aimPoints(model, head, hitbox, player, dummy) local points = {} local mode = player and CONFIG.playerHitPart or (dummy and CONFIG.dummyHitPart or "auto") if hitbox then points[#points + 1] = hitbox.Position end if mode ~= "head" then if not hitbox or hitbox.Position ~= head.Position then points[#points + 1] = head.Position end local torso = model:FindFirstChild("UpperTorso") or model:FindFirstChild("Torso") if torso and torso:IsA("BasePart") and torso.Position ~= head.Position then points[#points + 1] = torso.Position end end if #points == 0 then points[#points + 1] = head.Position end return points end -- Predict position at time T, using the fitted acceleration when it is small -- enough to be real (a target spinning in place would otherwise fake 100s of g). -- Characters fall under the world's gravity while airborne. Solving against the -- difference between the arrow's gravity and the target's makes a jumping target -- exact instead of leading it straight up off the screen. STATE.mediumGravity = function() local ok, g = pcall(function() return workspace.Gravity end) if ok and type(g) == "number" and g > 0 then return g end return 196.2 end local function targetIsAirborne(model) -- FloorMaterial is computed by the owner's physics and does not replicate -- reliably for remote characters (measured: it reads Air for over a second on -- players standing on wood, which would aim the shot high). Vertical velocity -- DOES replicate, so use that: a grounded humanoid has ~0, a jumping one tens. local record = TRACK[model] local root = model:FindFirstChild("HumanoidRootPart") local vy if record and record.fv then vy = record.fv.Y elseif root then vy = root.AssemblyLinearVelocity.Y end if not vy then return false end return math.abs(vy) > 8 end -- Simulate a shot before taking it. -- -- The solver returns a trajectory aimed at where the target is believed to be, but -- "aimed at a point" is not the same as "passes through the body": the solve is exact -- about the geometry it was given and helpless about everything it was not, and what it -- was not given is where the target actually ends up during the flight. So the trajectory -- is integrated forward at the gravity the game applies and tested at every step against -- the volume damage is resolved against - their hull, carried forward on its own measured -- motion, position, velocity and acceleration. A trajectory that never enters that volume -- cannot land, no matter how good the aim looked on paper. -- -- Nothing is fired while this runs. It is arithmetic on a copy of the trajectory; the -- only shot taken is the one whose simulation reaches the body. local function simulateShot(origin, direction, speed, gravity, model, hull, flight) if not direction or not speed or speed <= 1 then return nil end local rec = model and TRACK[model] local root = model and model:FindFirstChild("HumanoidRootPart") local halfXZ, halfY = 1.5, 2.2 local hullOffset = Vector3.new(0, 0, 0) if hull and hull.Parent and root then local size = hull.Size halfXZ = math.max(size.X, size.Z) * 0.5 halfY = size.Y * 0.5 hullOffset = hull.Position - root.Position elseif root then halfXZ, halfY = 1.2, 2.0 end if not root then return nil end local down = Vector3.new(0, -(gravity or 0), 0) local horizon = math.clamp((flight or 0.3) * 1.35 + 0.03, 0.05, 1.2) -- Step by distance travelled, not by clock: at these speeds a time-stepped test -- covers more than a stud between samples and steps straight over the body without -- ever being inside it. local stride = math.max(0.2, math.min(0.5, halfXZ * 0.35)) local steps = math.clamp(math.ceil((speed * horizon) / stride), 16, 600) local closest, closestAt = nil, nil for step = 1, steps do local t = horizon * step / steps local point = origin + direction * (speed * t) + down * (0.5 * t * t) -- where they will be at that moment: their fitted motion continued forward, which -- is the same shape the solver used but measured from the whole window local centre if rec and rec.origin then centre = rec.origin + (rec.velocity2 or Vector3.new(0, 0, 0)) * t + (rec.accel or Vector3.new(0, 0, 0)) * (0.5 * t * t) else centre = root.Position + (root.AssemblyLinearVelocity or Vector3.new(0, 0, 0)) * t end centre = centre + hullOffset local delta = point - centre local outX = math.max(0, math.abs(delta.X) - halfXZ) local outZ = math.max(0, math.abs(delta.Z) - halfXZ) local outY = math.max(0, math.abs(delta.Y) - halfY) local miss = math.sqrt(outX * outX + outZ * outZ + outY * outY) if miss <= 0 then return true, t, 0 end if not closest or miss < closest then closest, closestAt = miss, t end end return false, closestAt, closest end local function selectSolution(origin, speed, gravity, tuning, 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 delta = head.Position - origin local distance = delta.Magnitude if distance > 0.01 and distance <= (tuning.range or Ballistics.MAX_DISTANCE) then local angle = math.deg(math.acos(math.clamp(look:Dot(delta.Unit), -1, 1))) if angle <= CONFIG.fov then candidates[#candidates + 1] = { model = model, head = head, root = root, hitbox = hitbox, player = player, dummy = dummy, angle = angle } end end end end table.sort(candidates, function(a, b) return a.angle < b.angle end) -- keep the current lock: a target already held stays selected while it is -- still inside the cone, instead of swapping to whoever is nearest the cross if prefer then for index, candidate in ipairs(candidates) do if candidate.model == prefer then table.remove(candidates, index) table.insert(candidates, 1, candidate) break end end end local attempts, lastReason = 0, "no target in FOV" local solutions = {} for _, candidate in ipairs(candidates) do local velocity = targetVelocity(candidate.model, candidate.root) * (CONFIG.leadScale or 1) local lead = leadTime() local arc = CONFIG.mode == "mortar" and "high" or "low" -- a target in the air falls too: solve against the gravity difference local airborne = targetIsAirborne(candidate.model) local targetG = airborne and STATE.mediumGravity() or 0 local relGravity = gravity - targetG -- prediction off: aim at where the target is right now, no velocity, no lead if CONFIG.predictMode == "off" then velocity = Vector3.new(0, 0, 0) lead = 0 targetG = 0 relGravity = gravity end for _, point in ipairs(aimPoints(candidate.model, candidate.head, candidate.hitbox, candidate.player, candidate.dummy)) do attempts += 1 local tries = {} tries[#tries + 1] = solveIntercept(origin, point + velocity * lead, velocity, speed, relGravity, nil, arc) for _, solution in ipairs(tries) do if solution then local clear, firstPart = pathCheck(origin, solution, candidate.model, candidate.hitbox or candidate.head, velocity, speed, gravity, lead, false, targetG) if clear then solution.model = candidate.model solution.head = candidate.head solution.hitbox = candidate.hitbox solution.aimPoint = point solution.velocity = velocity solution.firstPart = firstPart solution.angle = candidate.angle solution.attempts = attempts -- Cost is dominated by flight time because prediction error -- grows with it (measured: 0.42s flights miss by ~10 studs, -- 0.04s flights by ~0.5). Angle is a light tiebreak, and the -- locked target keeps a bonus so the selection stays sticky. solution.cost = (solution.time or 0) + (candidate.angle or 0) * 0.002 if prefer and candidate.model == prefer then solution.cost = solution.cost * 0.75 end solutions[#solutions + 1] = solution break end lastReason = "path " .. tostring(firstPart) else lastReason = "no trajectory" end end end end if #solutions > 0 then table.sort(solutions, function(a, b) return (a.cost or 0) < (b.cost or 0) end) if not CONFIG.simulate then local best = solutions[1] best.options = #solutions return best end -- Simulate shots until one of them is a hit, best first: the preferred, most -- certain trajectory is tried, then the next, and the one whose simulation -- reaches the body is the shot that gets taken. Nothing leaves the client while -- this runs - it is the same trajectory arithmetic the solver already does, -- evaluated forward - and a shot with no hit to take is never taken at all. local closest, closestMiss, tried = nil, nil, 0 for _, solution in ipairs(solutions) do tried += 1 local hit, hitAt, miss = simulateShot(origin, solution.direction, speed, gravity, solution.model, solution.hitbox or solution.head, solution.time) if hit then solution.simHit = true solution.simTime = hitAt solution.simMiss = 0 solution.options = #solutions STATE.simDebug = { model = solution.model and solution.model.Name, flight = solution.time, hitAt = hitAt, tried = tried, candidates = #candidates } STATE.simShots = (STATE.simShots or 0) + 1 return solution end solution.simHit = false solution.simTime = hitAt solution.simMiss = miss if not closestMiss or (miss or 99) < closestMiss then closest, closestMiss = solution, miss or 99 end end -- every trajectory missed, so the arrow stays in the quiver: taking one of these -- would have been spending it on a miss that was already known STATE.simRejected = (STATE.simRejected or 0) + 1 STATE.simDebug = { model = closest and closest.model and closest.model.Name, miss = closestMiss, flight = closest and closest.time, tried = tried, candidates = #candidates, attempts = attempts } if closest then local now = os.clock() if not STATE.simLogged or now - STATE.simLogged > 2 then STATE.simLogged = now logEvent("sim", string.format( "no simulated hit: %s would pass %.1f studs wide, closest at %.3fs (shot held)", tostring(closest.model and closest.model.Name), closestMiss or -1, closest.time or 0)) end end STATE.selectDebug = { candidates = #candidates, attempts = attempts, reason = "simulated miss" } return nil end STATE.selectDebug = { candidates = #candidates, attempts = attempts, reason = lastReason } return nil end -- Target model used for highlight bookkeeping. local function solutionModel() return STATE.lastSolution and STATE.lastSolution.model or nil 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, } 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 -- Only override the tuning when the measured value genuinely disagrees with it: -- if the data agrees (it does for arrows: 54.98 solved vs 55.00 claimed) the -- table stays authoritative and measurement noise never perturbs the aim. measuredGravity = function(kind, tuned) if not kind then return nil end local g = CAL.gByKind[kind] if g == nil or (CAL.gCount[kind] or 0) < 15 then return nil end if (CAL.gCount[kind] or 0) < 20 then return nil end local spread = (CAL.gSpread and CAL.gSpread[kind]) or 1 if spread > 0.15 then return nil end if tuned and math.abs(g - tuned) <= math.max(1.5, tuned * 0.03) then return nil end return g 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 measuredLead = function(weaponId) if not weaponId then return 0 end local n = CAL.leadCount[weaponId] or 0 if n < 10 then return 0 end return math.clamp(CAL.leadByWeapon[weaponId] or 0, -0.06, 0.06) end STATE.measuredGravity = measuredGravity STATE.measuredLead = measuredLead -- ------------------------------------------------------------------ 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) local fp, fv = filteredState(model, root) return fp, fv, nil end -- ------------------------------------------------------------------ view-delay measurement -- The game's own hit test is a per-step thin ray against replicated character -- positions, so the only unknown left in the prediction is how far our view of -- other characters trails the server. We can measure it exactly on ourselves: -- our own position is local and undelayed, so when the server reports an impact -- somewhere on our body, the moment in our position history that matches that -- point IS the time the hit happened. The gap to now is the total delay. local SELF_HISTORY = {} local SELF_HISTORY_MAX = 150 RunService.Heartbeat:Connect(function() if closed then return end local char = LP.Character local root = char and char:FindFirstChild("HumanoidRootPart") if root then SELF_HISTORY[#SELF_HISTORY + 1] = { t = os.clock(), p = root.Position } while #SELF_HISTORY > SELF_HISTORY_MAX do table.remove(SELF_HISTORY, 1) end else table.clear(SELF_HISTORY) end end) local function sampleViewDelay(impactPosition) if typeof(impactPosition) ~= "Vector3" then return nil end local now = os.clock() local best, bestDist for _, sample in ipairs(SELF_HISTORY) do local d = (sample.p - impactPosition).Magnitude if not bestDist or d < bestDist then best, bestDist = sample, d end end if not best then return nil end -- the impact sits on our body surface, so the match is inside one body radius; -- the history must also be old enough to contain the real event if now - best.t > 1.2 then return nil end return now - best.t 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 local tool, tuning = toolTuning() if not tool or not CONFIG.enabled then lock.model, lock.solution = nil, nil return end local origin = aimOrigin(tool) local speed = tuning.speed or tuning.speedMax or tuning.speedMin or 250 local gravity = measuredGravity(tuning.projectile or "Arrow", tuning.gravity or 0) or (tuning.gravity or 0) local keep = lock.model and lock.model.Parent and lock.model or nil local solution = selectSolution(origin, speed, gravity, tuning, 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" -- prefer the gravity the server was measured to apply gravity = measuredGravity(kind, gravity) or gravity STATE.aimWeaponId = tool:GetAttribute("WeaponId") local locked = (lock.model and lock.model.Parent) and lock.model or nil local solution = selectSolution(stableOrigin, speed, gravity, tuning, locked) 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 rec.flight = solution.time 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.predictAt = solution.future rec.sentDir = solution.direction rec.sentOrigin = stableOrigin rec.leadUsed = leadTime() rec.assumedGravity = gravity rec.predictMode = CONFIG.predictMode local v = solution.velocity rec.targetSpeed = v and v.Magnitude or 0 if rec.targetSpeed and rec.targetSpeed > 1 and v then rec.velDir = v.Unit end 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 flight=%.3f speed=%.0f vsRoot=%.1f box=%s path=%s sim=%s", solution.model.Name, solution.angle or 0, solution.time or 0, speed, vsRoot, tostring(solution.hitbox and solution.hitbox.Name), tostring(solution.firstPart), solution.simHit and string.format("hit at %.3fs", solution.simTime or 0) or "not simulated")) 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 = selectSolution(origin, speed, gravity, tuning) 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 flight=%.3f speed=%.0f", solution.model.Name, solution.angle or 0, solution.time 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 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, velocity = sol.velocity, flightTime = sol.time, residual = sol.residual, direction = payload.dir, mode = CONFIG.mode, arrivalY = sol.arrivalY, lead = CONFIG.leadSeconds } else STATE.aimActive = false 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. 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)) 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 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 rec.predictAt and rec.model and rec.model.Parent 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 function planMortar(model) local head, root, _, _, hitbox if model then head, root, _, _, hitbox = validTarget(model) end if not head then local best, lastReason for _, candidate in ipairs(targetPool()) do local plan, reason = planMortar(candidate) if plan and plan.angle <= CONFIG.fov and (not best or plan.angle < best.angle) then best = plan end lastReason = reason or lastReason end if best then return best end return nil, lastReason or "No reachable target in FOV" end 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 velocity = targetVelocity(model, root) local lead = leadTime() local aimPart = hitbox or head local point = aimPart.Position + velocity * lead if (point - origin).Magnitude > (tuning.range or Ballistics.MAX_DISTANCE) then return nil, "Beyond weapon range" end local gravity = tuning.gravity or 60 local required = minimumLaunch(origin, point, velocity, gravity, Ballistics.MAX_LIFETIME) if not required then return nil, "No gravity-based mortar solution" end local minimum = tuning.speedMin local maximum = tuning.speedMax local speed = math.max(minimum, required + 0.5) if speed > maximum then return nil, "Insufficient bow speed" end local charge = maximum > minimum and (speed - minimum) / (maximum - minimum) or 0 local solution = solveIntercept(origin, point, velocity, speed, gravity, nil, "high") if not solution then return nil, "No descending intercept before arrow expires" end local clear, reason = pathCheck(origin, solution, model, aimPart, velocity, speed, gravity, lead, true, targetIsAirborne(model) and STATE.mediumGravity() or 0) if not clear then return nil, "Mortar path hits " .. tostring(reason) end local look = Util.GetAimRay().Direction.Unit local angle = math.deg(math.acos(math.clamp(look:Dot((point - origin).Unit), -1, 1))) return { target = model, charge = charge, drawSeconds = charge * tuning.drawTime, speed = speed, minimumSpeed = required, flightTime = solution.time, direction = solution.direction, residual = solution.residual, arrivalY = solution.arrivalY, angle = angle } end -- ------------------------------------------------------------------ CS:GO menu -- Cool neutral greys on frosted surfaces. Every panel is slightly translucent and -- every edge is a near-white hairline, so the layers read as glass over the game -- instead of flat black rectangles. One accent only, a desaturated steel, for -- anything that is on, selected or active. -- ------------------------------------------------------------------ interface -- -- Laid out like the gamesense menu: a square window with a 1px black edge and an -- inner highlight, a title strip, one row of main tabs, a row of group keys, and -- a body of two columns of bordered group boxes. Controls are the same primitives -- that menu uses: small outlined check squares, thin sliders with the value on the -- right, a bordered select box that cycles, a swatch for colours, and flat -- bordered buttons. No rounded corners, no glow, no large type. 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), } } -- Where the interface is parented. gethui() is the right home when the executor -- provides it; otherwise CoreGui is the usual choice but not every environment -- allows writes there (that is what raises "lacking plugin permission: cannot -- access instance"), so it is probed with a throwaway child first and the player's -- own PlayerGui is the last resort. -- Which container the interface will actually go in. It is not enough for a -- container to exist: some environments refuse writes inside gethui() and CoreGui -- (children added there fail with "lacking capability Plugin"), which leaves the -- menu built but every later element homeless. So each candidate is tested by -- adding a child to it, and the first container that accepts children wins. 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 frame(obj) outline(obj, UI.theme.border, 1, 0) local inner = Instance.new("UIStroke") inner.Color = UI.theme.borderLight inner.Thickness = 1 inner.Transparency = 0.35 inner.Parent = obj return obj end 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 -- clear anything left behind by a previous session that crashed before it could -- unload itself, so a stale half-built menu cannot sit on top of this one ;(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) local watermark = mk("TextLabel", { Name = "Watermark", BackgroundTransparency = 1, Font = Enum.Font.SourceSansBold, TextSize = 11, TextColor3 = Color3.fromRGB(205, 208, 212), TextStrokeTransparency = 0.6, TextStrokeColor3 = Color3.fromRGB(0, 0, 0), TextXAlignment = Enum.TextXAlignment.Left, TextYAlignment = Enum.TextYAlignment.Top, Position = UDim2.new(0, 10, 0, 8), Size = UDim2.new(0, 340, 0, 40), Text = "tribes", }, sg) local FONT = Enum.Font.SourceSans local FONT_BOLD = Enum.Font.SourceSansBold -- window local panelW = math.clamp(Camera.ViewportSize.X * 0.86, 320, 640) local panelH = math.clamp(Camera.ViewportSize.Y * 0.84, 250, 470) local panel = mk("Frame", { Name = "Menu", BackgroundColor3 = UI.theme.bg, BorderSizePixel = 0, Position = UDim2.new(0, 14, 0, 46), Size = UDim2.fromOffset(panelW, panelH), Visible = true, }, sg) frame(panel) -- title strip local header = mk("TextButton", { Name = "Header", BackgroundColor3 = UI.theme.panelHeader, BorderSizePixel = 0, AutoButtonColor = false, Text = "", Size = UDim2.new(1, 0, 0, 18), Active = true, }, panel) mk("TextLabel", { BackgroundTransparency = 1, Font = FONT_BOLD, TextSize = 12, TextColor3 = UI.theme.bright, TextXAlignment = Enum.TextXAlignment.Left, Position = UDim2.new(0, 6, 0, 0), Size = UDim2.new(0, 200, 1, 0), Text = "tribes", }, header) local statusLamp = mk("Frame", { BackgroundColor3 = UI.theme.accent, BorderSizePixel = 0, AnchorPoint = Vector2.new(1, 0.5), Position = UDim2.new(1, -26, 0.5, 0), Size = UDim2.fromOffset(6, 6), }, header) outline(statusLamp, UI.theme.border, 1, 0) local closeBtn = mk("TextButton", { BackgroundTransparency = 1, Font = FONT_BOLD, TextSize = 12, TextColor3 = UI.theme.dim, Text = "x", AnchorPoint = Vector2.new(1, 0.5), Position = UDim2.new(1, -6, 0.5, 0), Size = UDim2.fromOffset(18, 18), Active = true, }, header) -- main tabs local tabBar = mk("Frame", { BackgroundColor3 = UI.theme.bg, BorderSizePixel = 0, Position = UDim2.new(0, 0, 0, 18), Size = UDim2.new(1, 0, 0, 20), }, panel) ;(function() local layout = Instance.new("UIListLayout") layout.FillDirection = Enum.FillDirection.Horizontal layout.SortOrder = Enum.SortOrder.LayoutOrder layout.Parent = tabBar end)(); -- group keys local keyBar = mk("Frame", { BackgroundColor3 = UI.theme.bg, BorderSizePixel = 0, Position = UDim2.new(0, 0, 0, 38), Size = UDim2.new(1, 0, 0, 16), }, panel) padding(keyBar, 0, 0, 5, 5) -- band separators live on the panel: any full-width child inside a strip that has -- a UIListLayout becomes a laid-out item and shoves that strip's contents aside for _, bandY in ipairs({ 37, 53 }) do mk("Frame", { Name = "BandRule", BackgroundColor3 = UI.theme.border, BorderSizePixel = 0, Position = UDim2.new(0, 0, 0, bandY), Size = UDim2.new(1, 0, 0, 1), }, panel) end -- A hairline of blue under the title strip with a white blob travelling along it. -- -- Sliding a UIGradient over a fixed frame does not work here: the gradient is mapped -- to the frame's own 0..1 space, so whatever slides outside that space clamps to the -- endpoint colour, which floods the bar white and snaps at the loop point. Instead the -- bar is a static blue and the blob is a real frame that moves along it. The blob's -- travel starts and ends fully off the bar, so the loop is a fade in from the left and -- out to the right with nothing to jump. ;(function() local line = mk("Frame", { Name = "TitleGlow", BackgroundColor3 = Color3.fromRGB(34, 100, 226), BorderSizePixel = 0, ClipsDescendants = true, Position = UDim2.new(0, 0, 0, 16), Size = UDim2.new(1, 0, 0, 2), ZIndex = 2, }, panel) local base = Instance.new("UIGradient") base.Color = ColorSequence.new({ ColorSequenceKeypoint.new(0, Color3.fromRGB(24, 78, 190)), ColorSequenceKeypoint.new(0.5, Color3.fromRGB(46, 118, 246)), ColorSequenceKeypoint.new(1, Color3.fromRGB(24, 78, 190)), }) base.Parent = line local blob = mk("Frame", { Name = "Blob", BackgroundColor3 = Color3.fromRGB(255, 255, 255), BorderSizePixel = 0, Position = UDim2.new(-0.34, 0, 0, 0), Size = UDim2.new(0.34, 0, 1, 0), ZIndex = 3, }, line) local fade = Instance.new("UIGradient") -- white core, dissolving into the blue at both ends of the blob fade.Color = ColorSequence.new({ ColorSequenceKeypoint.new(0.00, Color3.fromRGB(96, 150, 255)), ColorSequenceKeypoint.new(0.50, Color3.fromRGB(255, 255, 255)), ColorSequenceKeypoint.new(1.00, Color3.fromRGB(96, 150, 255)), }) fade.Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0.00, 1.00), NumberSequenceKeypoint.new(0.30, 0.30), NumberSequenceKeypoint.new(0.50, 0.00), NumberSequenceKeypoint.new(0.70, 0.30), NumberSequenceKeypoint.new(1.00, 1.00), }) fade.Parent = blob UI.titleBlob = blob end)(); local tabHolder = mk("Frame", { BackgroundTransparency = 1, Position = UDim2.new(0, 0, 0, 54), Size = UDim2.new(1, 0, 1, -71), }, panel) -- status strip local statusStrip = mk("Frame", { BackgroundColor3 = UI.theme.panelHeader, BorderSizePixel = 0, Position = UDim2.new(0, 0, 1, -17), Size = UDim2.new(1, 0, 0, 17), }, panel) mk("Frame", { BackgroundColor3 = UI.theme.border, BorderSizePixel = 0, Position = UDim2.new(0, 0, 0, 0), Size = UDim2.new(1, 0, 0, 1), }, statusStrip) local statusText = mk("TextLabel", { BackgroundTransparency = 1, Font = FONT, TextSize = 11, TextColor3 = UI.theme.dim, TextXAlignment = Enum.TextXAlignment.Left, Position = UDim2.new(0, 5, 0, 0), Size = UDim2.new(1, -10, 1, 0), Text = "ready", }, statusStrip) local pages = {} local tabButtons = {} local pageCols = {} local refreshers = {} local function refreshAll() for _, f in ipairs(refreshers) do pcall(f) end end -- -------------------------------- group boxes local function columnFor(page) local state = pageCols[page] if not state then state = { cols = {}, count = 0, groups = {}, current = nil, keys = {} } local pageW = page.AbsoluteSize.X > 0 and page.AbsoluteSize.X or (panelW - 8) local colW = math.floor((pageW - 8) / 2) for i = 1, 2 do local col = mk("Frame", { Name = "Column" .. i, BackgroundTransparency = 1, Position = UDim2.new(0, 4 + (i - 1) * (colW + 4), 0, 4), Size = UDim2.new(0, colW, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, BorderSizePixel = 0, }, page) local layout = Instance.new("UIListLayout") layout.Padding = UDim.new(0, 4) layout.SortOrder = Enum.SortOrder.LayoutOrder layout.Parent = col state.cols[i] = col end state.colW = colW pageCols[page] = state end return state end -- rows go into the group that is currently open on that page local function rowParent(page) local state = columnFor(page) return (state.current and state.current.body) or page, state end local function nextOrder(state) state.order = (state.order or 0) + 1 return state.order end local function sectionHeader(page, text) local state = columnFor(page) state.count += 1 state.order = 0 local col = state.cols[(state.count % 2 == 0) and 2 or 1] local group = mk("Frame", { Name = "Group", BackgroundColor3 = UI.theme.panel, BorderSizePixel = 0, Size = UDim2.new(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, ClipsDescendants = true, }, col) frame(group) state.colOrder = (state.colOrder or 0) + 1 group.LayoutOrder = state.colOrder local head = mk("Frame", { Name = "Title", BackgroundColor3 = UI.theme.panelHeader, BorderSizePixel = 0, Size = UDim2.new(1, 0, 0, 16), }, group) local headText = mk("TextLabel", { BackgroundTransparency = 1, Font = FONT_BOLD, TextSize = 11, TextColor3 = UI.theme.text, TextXAlignment = Enum.TextXAlignment.Left, Position = UDim2.new(0, 5, 0, 0), Size = UDim2.new(1, -10, 1, 0), Text = text, }, head) mk("Frame", { BackgroundColor3 = UI.theme.border, BorderSizePixel = 0, Position = UDim2.new(0, 0, 1, -1), Size = UDim2.new(1, 0, 0, 1), }, head) local body = mk("Frame", { Name = "Body", BackgroundTransparency = 1, BorderSizePixel = 0, Position = UDim2.new(0, 0, 0, 16), Size = UDim2.new(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, }, group) local layout = Instance.new("UIListLayout") layout.Padding = UDim.new(0, 2) layout.SortOrder = Enum.SortOrder.LayoutOrder layout.Parent = body padding(body, 3, 3, 4, 4) state.current = { group = group, body = body, head = head } state.groups[#state.groups + 1] = { group = group, name = text } return group 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 key ~= "leadScale" and 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 or name == "leadScale" 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.leadScale = 0.5 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() -- -------------------------------- controls -- a label and, to its right, whatever the row controls local function rowShell(page, height, interactive) local state = columnFor(page) local props = { BackgroundColor3 = UI.theme.panel, BackgroundTransparency = 1, BorderSizePixel = 0, Size = UDim2.new(1, 0, 0, height), LayoutOrder = nextOrder(state), Active = interactive and true or false, } if interactive then props.AutoButtonColor = false props.Text = "" end local parent = (state.current and state.current.body) or page return mk(interactive and "TextButton" or "Frame", props, parent), state end -- check square, the way that menu draws them: an outlined box, filled when on local function toggleRow(page, label, get, set) local row = rowShell(page, 16, true) local box = mk("Frame", { BackgroundColor3 = UI.theme.track, BorderSizePixel = 0, Position = UDim2.new(0, 0, 0.5, -5), Size = UDim2.fromOffset(10, 10), }, row) outline(box, UI.theme.border, 1, 0) local fill = mk("Frame", { BackgroundColor3 = UI.theme.accent, BorderSizePixel = 0, AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.new(0.5, 0, 0.5, 0), Size = UDim2.fromOffset(6, 6), Visible = false, }, box) local text = mk("TextLabel", { BackgroundTransparency = 1, Font = FONT, TextSize = 12, TextColor3 = UI.theme.text, TextXAlignment = Enum.TextXAlignment.Left, Position = UDim2.new(0, 16, 0, 0), Size = UDim2.new(1, -16, 1, 0), Text = label, }, row) local function refresh() local on = get() and true or false fill.Visible = on text.TextColor3 = on and UI.theme.bright or UI.theme.dim end row.MouseButton1Click:Connect(function() set(not get()) refresh() saveSoon() end) refresh() refreshers[#refreshers + 1] = refresh return row end local function sliderRow(page, label, min, max, get, set, fmt) local row = rowShell(page, 26, false) local text = mk("TextLabel", { BackgroundTransparency = 1, Font = FONT, TextSize = 12, TextColor3 = UI.theme.text, TextXAlignment = Enum.TextXAlignment.Left, Position = UDim2.new(0, 0, 0, 0), Size = UDim2.new(0.6, 0, 0, 14), Text = label, }, row) local value = mk("TextLabel", { BackgroundTransparency = 1, Font = FONT, TextSize = 12, TextColor3 = UI.theme.dim, TextXAlignment = Enum.TextXAlignment.Right, Position = UDim2.new(0.4, 0, 0, 0), Size = UDim2.new(0.6, 0, 0, 14), Text = "", }, row) local track = mk("TextButton", { BackgroundColor3 = UI.theme.track, BorderSizePixel = 0, AutoButtonColor = false, Text = "", Position = UDim2.new(0, 0, 0, 17), Size = UDim2.new(1, 0, 0, 5), Active = true, }, row) outline(track, UI.theme.border, 1, 0) local fill = mk("Frame", { BackgroundColor3 = UI.theme.fill, BorderSizePixel = 0, Size = UDim2.new(0, 0, 1, 0), }, track) local knob = mk("Frame", { BackgroundColor3 = UI.theme.accent, BorderSizePixel = 0, AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.new(0, 0, 0.5, 0), Size = UDim2.fromOffset(3, 9), }, track) outline(knob, UI.theme.border, 1, 0) local function refresh() local span = math.max(max - min, 1e-6) local a = math.clamp((get() - min) / span, 0, 1) fill.Size = UDim2.new(a, 0, 1, 0) knob.Position = UDim2.new(a, 0, 0.5, 0) value.Text = fmt and fmt(get()) or string.format("%.2f", get()) end local dragging = false local function apply(input) local rel = (input.Position.X - track.AbsolutePosition.X) / math.max(track.AbsoluteSize.X, 1) set(min + (max - min) * math.clamp(rel, 0, 1)) refresh() saveSoon() end track.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then dragging = true apply(input) end end) UserInputService.InputChanged:Connect(function(input) if dragging and (input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch) then apply(input) end end) UserInputService.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then dragging = false end end) refresh() refreshers[#refreshers + 1] = refresh return row end -- a select box: outlined, current choice on the left, arrow on the right; tapping -- steps to the next option local function segmentedRow(page, label, options, get, set) local row = rowShell(page, 16, true) local nameLabel = mk("TextLabel", { BackgroundTransparency = 1, Font = FONT, TextSize = 12, TextColor3 = UI.theme.text, TextXAlignment = Enum.TextXAlignment.Left, Position = UDim2.new(0, 0, 0, 1), Size = UDim2.new(0.45, 0, 0, 14), Text = label, }, row) local box = mk("Frame", { BackgroundColor3 = UI.theme.track, BorderSizePixel = 0, Position = UDim2.new(0.45, 0, 0, 1), Size = UDim2.new(0.55, 0, 0, 14), }, row) outline(box, UI.theme.border, 1, 0) local chosen = mk("TextLabel", { BackgroundTransparency = 1, Font = FONT, TextSize = 12, TextColor3 = UI.theme.bright, TextXAlignment = Enum.TextXAlignment.Left, Position = UDim2.new(0, 4, 0, 0), Size = UDim2.new(1, -16, 1, 0), Text = "", }, box) mk("TextLabel", { BackgroundTransparency = 1, Font = FONT, TextSize = 11, TextColor3 = UI.theme.dim, TextXAlignment = Enum.TextXAlignment.Right, Position = UDim2.new(1, -4, 0, -1), Size = UDim2.new(0, 12, 1, 0), Text = "v", }, box) local function labelFor(value) for _, opt in ipairs(options) do if opt.value == value then return opt.label end end return tostring(value) end local function refresh() chosen.Text = labelFor(get()) end row.MouseButton1Click:Connect(function() local cur, index = get(), 1 for i, opt in ipairs(options) do if opt.value == cur then index = i break end end set(options[index % #options + 1].value) refresh() refreshAll() saveSoon() end) refresh() refreshers[#refreshers + 1] = refresh return row end -- A real picker rather than a palette cycle: a saturation/value square built from -- two gradient overlays (white to clear across, clear to black down) over the pure -- hue, plus a hue bar. Both are dragged directly, in the same way the sliders are. UI.openPicker = function(get, set, title) if UI.picker then UI.picker:Destroy() UI.picker = nil end local current = get() local hue, sat, val = current:ToHSV() -- a TextButton, not a Frame: only buttons absorb input, so this is what stops a -- tap landing on a row behind the picker. It sits above the panel's own children -- and below the picker, and tapping it closes the picker. local shade = mk("TextButton", { Name = "PickerModal", BackgroundColor3 = Color3.fromRGB(0, 0, 0), BackgroundTransparency = 0.45, BorderSizePixel = 0, AutoButtonColor = false, Text = "", Size = UDim2.new(1, 0, 1, 0), ZIndex = 10, Active = true, }, panel) local holder = mk("Frame", { Name = "Picker", BackgroundColor3 = UI.theme.panel, BorderSizePixel = 0, Position = UDim2.new(0.5, -88, 0.5, -92), Size = UDim2.fromOffset(176, 184), ZIndex = 20, }, panel) frame(holder) UI.picker = holder local function closePicker() holder:Destroy() shade:Destroy() if UI.picker == holder then UI.picker = nil end end shade.MouseButton1Click:Connect(closePicker) local bar = mk("Frame", { BackgroundColor3 = UI.theme.panelHeader, BorderSizePixel = 0, Size = UDim2.new(1, 0, 0, 18), ZIndex = 21, }, holder) mk("TextLabel", { BackgroundTransparency = 1, Font = FONT_BOLD, TextSize = 11, TextColor3 = UI.theme.text, TextXAlignment = Enum.TextXAlignment.Left, Position = UDim2.new(0, 6, 0, 0), Size = UDim2.new(1, -30, 1, 0), Text = title or "colour", ZIndex = 22, }, bar) local close = mk("TextButton", { BackgroundTransparency = 1, Font = FONT_BOLD, TextSize = 11, TextColor3 = UI.theme.dim, Text = "x", AnchorPoint = Vector2.new(1, 0.5), Position = UDim2.new(1, -6, 0.5, 0), Size = UDim2.fromOffset(16, 16), Active = true, ZIndex = 22, }, bar) close.MouseButton1Click:Connect(closePicker) local sv = mk("TextButton", { Name = "SV", BackgroundColor3 = Color3.fromHSV(hue, 1, 1), BorderSizePixel = 0, AutoButtonColor = false, Text = "", TextTransparency = 1, Position = UDim2.new(0, 9, 0, 27), Size = UDim2.fromOffset(158, 92), ZIndex = 21, Active = true, }, holder) outline(sv, UI.theme.border, 1, 0) local white = mk("Frame", { BackgroundColor3 = Color3.new(1, 1, 1), BorderSizePixel = 0, Size = UDim2.fromScale(1, 1), ZIndex = 22, Active = false, }, sv) local satGrad = Instance.new("UIGradient") satGrad.Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 0), NumberSequenceKeypoint.new(1, 1), }) satGrad.Parent = white local black = mk("Frame", { BackgroundColor3 = Color3.new(0, 0, 0), BorderSizePixel = 0, Size = UDim2.fromScale(1, 1), ZIndex = 23, Active = false, }, sv) local valGrad = Instance.new("UIGradient") valGrad.Rotation = 90 valGrad.Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 1), NumberSequenceKeypoint.new(1, 0), }) valGrad.Parent = black local knob = mk("Frame", { BackgroundColor3 = Color3.new(1, 1, 1), BorderSizePixel = 0, AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(9, 9), ZIndex = 24, }, sv) outline(knob, UI.theme.border, 1, 0) local hueBar = mk("TextButton", { Name = "Hue", BorderSizePixel = 0, AutoButtonColor = false, Text = "", TextTransparency = 1, Position = UDim2.new(0, 9, 0, 127), Size = UDim2.fromOffset(158, 12), ZIndex = 21, Active = true, }, holder) outline(hueBar, UI.theme.border, 1, 0) local hueGrad = Instance.new("UIGradient") local stops = {} for i = 0, 6 do stops[#stops + 1] = ColorSequenceKeypoint.new(i / 6, Color3.fromHSV(i / 6, 1, 1)) end hueGrad.Color = ColorSequence.new(stops) hueGrad.Parent = hueBar local hueKnob = mk("Frame", { BackgroundColor3 = Color3.new(1, 1, 1), BorderSizePixel = 0, AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.new(hue, 0, 0.5, 0), Size = UDim2.fromOffset(5, 16), ZIndex = 24, }, hueBar) outline(hueKnob, UI.theme.border, 1, 0) local preview = mk("Frame", { BackgroundColor3 = current, BorderSizePixel = 0, Position = UDim2.new(0, 9, 0, 146), Size = UDim2.fromOffset(44, 26), ZIndex = 21, }, holder) outline(preview, UI.theme.border, 1, 0) local readout = mk("TextLabel", { BackgroundTransparency = 1, Font = FONT, TextSize = 11, TextColor3 = UI.theme.dim, TextXAlignment = Enum.TextXAlignment.Left, TextYAlignment = Enum.TextYAlignment.Top, Text = "", Position = UDim2.new(0, 60, 0, 148), Size = UDim2.new(1, -68, 0, 30), ZIndex = 21, }, holder) local function apply(live) local color = Color3.fromHSV(hue, sat, val) sv.BackgroundColor3 = Color3.fromHSV(hue, 1, 1) knob.Position = UDim2.new(sat, 0, 1 - val, 0) hueKnob.Position = UDim2.new(hue, 0, 0.5, 0) preview.BackgroundColor3 = color readout.Text = string.format("%d, %d, %d", color.R * 255, color.G * 255, color.B * 255) if live ~= false then set(color) saveSoon() end end apply(false) local dragging = nil local function pick(input) if dragging == "sv" then local rel = input.Position - sv.AbsolutePosition sat = math.clamp(rel.X / math.max(sv.AbsoluteSize.X, 1), 0, 1) val = 1 - math.clamp(rel.Y / math.max(sv.AbsoluteSize.Y, 1), 0, 1) apply() elseif dragging == "hue" then local rel = input.Position.X - hueBar.AbsolutePosition.X hue = math.clamp(rel / math.max(hueBar.AbsoluteSize.X, 1), 0, 1) apply() end end sv.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then dragging = "sv" pick(input) end end) hueBar.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then dragging = "hue" pick(input) end end) UserInputService.InputChanged:Connect(function(input) if not dragging then return end if not holder.Parent then dragging = nil return end if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then pick(input) end end) UserInputService.InputEnded:Connect(function() dragging = nil end) return holder end local function colorRow(page, label, get, set) local row = rowShell(page, 16, true) local swatch = mk("Frame", { BackgroundColor3 = get(), BorderSizePixel = 0, Position = UDim2.new(0, 0, 0.5, -5), Size = UDim2.fromOffset(10, 10), }, row) outline(swatch, UI.theme.border, 1, 0) local text = mk("TextLabel", { BackgroundTransparency = 1, Font = FONT, TextSize = 12, TextColor3 = UI.theme.text, TextXAlignment = Enum.TextXAlignment.Left, Position = UDim2.new(0, 16, 0, 0), Size = UDim2.new(1, -16, 1, 0), Text = label, TextTruncate = Enum.TextTruncate.AtEnd, }, row) local function refresh() local cur = get() swatch.BackgroundColor3 = cur text.Text = label .. string.format(" %d,%d,%d", cur.R * 255, cur.G * 255, cur.B * 255) end row.MouseButton1Click:Connect(function() UI.openPicker(get, function(color) set(color) refresh() end, label) end) refresh() refreshers[#refreshers + 1] = refresh return row end local function buttonRow(page, label, callback) local row = rowShell(page, 17, true) row.BackgroundColor3 = UI.theme.select row.BackgroundTransparency = 0 outline(row, UI.theme.border, 1, 0) mk("TextLabel", { BackgroundTransparency = 1, Font = FONT, TextSize = 12, TextColor3 = UI.theme.text, Text = label, Size = UDim2.new(1, 0, 1, 0), }, row) row.MouseButton1Click:Connect(function() row.BackgroundColor3 = UI.theme.hover task.delay(0.12, function() if row.Parent then row.BackgroundColor3 = UI.theme.select end end) pcall(callback) end) return row end -- Returns the label, not the row: the callers keep writing .Text on the value this -- hands back, and a Frame has no Text property, which threw every frame. local function infoRow(page, text) local row = rowShell(page, 30, false) return mk("TextLabel", { BackgroundTransparency = 1, Font = FONT, TextSize = 11, TextColor3 = UI.theme.dim, TextXAlignment = Enum.TextXAlignment.Left, TextYAlignment = Enum.TextYAlignment.Top, TextWrapped = true, Text = text, Size = UDim2.new(1, 0, 0, 30), }, row) end -- -------------------------------- tabs and keys local function keyRowFor(page) for _, c in ipairs(keyBar:GetChildren()) do if c:IsA("TextButton") then c:Destroy() end end local layout = keyBar:FindFirstChildOfClass("UIListLayout") if not layout then layout = Instance.new("UIListLayout") layout.FillDirection = Enum.FillDirection.Horizontal layout.Padding = UDim.new(0, 8) layout.SortOrder = Enum.SortOrder.LayoutOrder layout.Parent = keyBar end local state = pageCols[page] if not state then return end for i, entry in ipairs(state.groups) do local key = mk("TextButton", { BackgroundTransparency = 1, Font = FONT, TextSize = 11, TextColor3 = UI.theme.dim, Text = entry.name, Size = UDim2.new(0, 0, 1, 0), AutomaticSize = Enum.AutomaticSize.X, LayoutOrder = i, Active = true, AutoButtonColor = false, }, keyBar) key.MouseButton1Click:Connect(function() local y = entry.group.AbsolutePosition.Y - page.AbsolutePosition.Y page.CanvasPosition = Vector2.new(0, math.max(0, y)) end) end end local function makePage(name, order) local btn = mk("TextButton", { BackgroundColor3 = UI.theme.bg, BackgroundTransparency = 1, Font = FONT_BOLD, TextSize = 12, TextColor3 = UI.theme.dim, Text = name, Size = UDim2.new(1 / 4, 0, 1, 0), LayoutOrder = order, Active = true, AutoButtonColor = false, }, tabBar) local page = mk("ScrollingFrame", { BackgroundTransparency = 1, BorderSizePixel = 0, Size = UDim2.new(1, 0, 1, 0), CanvasSize = UDim2.new(0, 0, 0, 0), AutomaticCanvasSize = Enum.AutomaticSize.Y, ScrollBarThickness = 2, ScrollBarImageColor3 = UI.theme.borderLight, ScrollingDirection = Enum.ScrollingDirection.Y, Visible = false, }, tabHolder) pages[name] = page tabButtons[name] = btn btn.MouseButton1Click:Connect(function() for n, other in pairs(tabButtons) do local on = n == name pages[n].Visible = on other.BackgroundTransparency = on and 0 or 1 other.BackgroundColor3 = on and UI.theme.select or UI.theme.bg other.TextColor3 = on and UI.theme.bright or UI.theme.dim end keyRowFor(page) end) return page end -- -------------------------------- drag / tap local function makeMovable(handle, target, onTap) local dragging, started, origin, moved = false, nil, nil, false handle.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then dragging = true moved = false started = input.Position origin = target.Position end end) UserInputService.InputChanged:Connect(function(input) if not dragging then return end if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then local d = input.Position - started if math.abs(d.X) > 6 or math.abs(d.Y) > 6 then moved = true end if moved then target.Position = UDim2.new(origin.X.Scale, origin.X.Offset + d.X, origin.Y.Scale, origin.Y.Offset + d.Y) end end end) UserInputService.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then if dragging and not moved and onTap then onTap() end dragging = false end end) end -- -------------------------------- FAB local fab = mk("TextButton", { Name = "Toggle", BackgroundColor3 = UI.theme.accent, BackgroundTransparency = 0.1, BorderSizePixel = 0, AutoButtonColor = false, Font = FONT, TextSize = 14, TextColor3 = Color3.fromRGB(20, 20, 20), Text = "CS", Size = UDim2.fromOffset(52, 52), Position = UDim2.new(0, Camera.ViewportSize.X - 68, 0, Camera.ViewportSize.Y - 68), Active = true, ZIndex = 5, }, sg) round(fab, 26) outline(fab, Color3.fromRGB(0, 0, 0), 1, 0.4) makeMovable(fab, fab, function() panel.Visible = not panel.Visible end) -- panel drag makeMovable(header, panel, nil) closeBtn.MouseButton1Click:Connect(function() panel.Visible = false end) -- -------------------------------- pages local aimPage = makePage("AIM", 1) local espPage = makePage("ESP", 2) local colorPage = makePage("COLOR", 3) local miscPage = makePage("MISC", 4) -- AIM sectionHeader(aimPage, "silent aim") toggleRow(aimPage, "Enable aim assist", function() return CONFIG.enabled end, function(v) CONFIG.enabled = v end) toggleRow(aimPage, "Simulate shot before firing", function() return CONFIG.simulate end, function(v) CONFIG.simulate = v end) segmentedRow(aimPage, "Targets", { { label = "Dummies", value = "dummies" }, { label = "Players", value = "players" }, { label = "Both", value = "both" }, }, function() return CONFIG.targets end, function(v) CONFIG.targets = v end) segmentedRow(aimPage, "Player hitbox", { { label = "Auto", value = "auto" }, { label = "Head", value = "head" }, { label = "Body", value = "body" }, }, function() return CONFIG.playerHitPart end, function(v) CONFIG.playerHitPart = v end) segmentedRow(aimPage, "Dummy hitbox", { { label = "Auto", value = "auto" }, { label = "Head", value = "head" }, { label = "Body", value = "body" }, }, function() return CONFIG.dummyHitPart end, function(v) CONFIG.dummyHitPart = v end) segmentedRow(aimPage, "Trajectory", { { label = "Direct", value = "direct" }, { label = "Mortar", value = "mortar" }, }, function() return CONFIG.mode end, function(v) CONFIG.mode = v end) segmentedRow(aimPage, "Prediction", { { label = "Simple", value = "simple" }, { label = "Off", value = "off" }, }, function() return CONFIG.predictMode end, function(v) CONFIG.predictMode = v end) sliderRow(aimPage, "FOV / aim cone", 5, 180, function() return CONFIG.fov end, function(v) CONFIG.fov = v end, function(v) return string.format("%d deg", math.floor(v + 0.5)) end) infoRow(aimPage, "lead: 50% of predicted travel (pinned)") sliderRow(aimPage, "Server view delay", 0, 0.2, function() return CONFIG.viewDelay end, function(v) CONFIG.viewDelay = v end, function(v) return string.format("%d ms", math.floor(v * 1000 + 0.5)) end) toggleRow(aimPage, "Hold fire unless it will land", function() return CONFIG.hitGate end, function(v) CONFIG.hitGate = v end) sliderRow(aimPage, "Acquire delay", 0, 0.6, function() return CONFIG.acquireDelay end, function(v) CONFIG.acquireDelay = v end, function(v) return string.format("%d ms of clear line", math.floor(v * 1000 + 0.5)) end) sliderRow(aimPage, "Max flight time", 0.05, 0.6, function() return CONFIG.maxFlight end, function(v) CONFIG.maxFlight = v end, function(v) return string.format("%d ms", math.floor(v * 1000 + 0.5)) end) sliderRow(aimPage, "Prediction lead", 0, 0.25, function() return CONFIG.leadSeconds end, function(v) CONFIG.leadSeconds = v end, function(v) return string.format("%.2f s", v) end) sliderRow(aimPage, "Velocity window", 0.02, 0.2, function() return CONFIG.velocityWindow end, function(v) CONFIG.velocityWindow = v end, function(v) return string.format("%.3f s", v) end) toggleRow(aimPage, "Instant draw (full charge)", function() return CONFIG.instantFire end, function(v) CONFIG.instantFire = v end) toggleRow(aimPage, "Auto-fire when server ready", function() return CONFIG.autoFire end, function(v) CONFIG.autoFire = v end) toggleRow(aimPage, "Melee kill aura", function() return CONFIG.killAura end, function(v) CONFIG.killAura = v end) sliderRow(aimPage, "Kill aura range", 0, 20, function() return CONFIG.killAuraRange end, function(v) CONFIG.killAuraRange = v end, function(v) return v < 0.5 and "weapon's own range" or string.format("%.1f studs", v) end) toggleRow(aimPage, "Auto-heal with bandages", function() return CONFIG.autoHeal end, function(v) CONFIG.autoHeal = v end) sliderRow(aimPage, "Heal below", 5, 95, function() return CONFIG.autoHealBelow end, function(v) CONFIG.autoHealBelow = v end, function(v) return string.format("%d%% health", math.floor(v + 0.5)) end) sliderRow(aimPage, "Heal cooldown", 1, 15, function() return CONFIG.autoHealCooldown end, function(v) CONFIG.autoHealCooldown = v end, function(v) return string.format("%.1f s", v) end) toggleRow(aimPage, "Team check (skip own tribe)", function() return CONFIG.teamCheck end, function(v) CONFIG.teamCheck = v end) toggleRow(aimPage, "Draw FOV ring", function() return VIS.fovRing end, function(v) VIS.fovRing = v end) local aimStat = infoRow(aimPage, "status") -- ESP sectionHeader(espPage, "esp") toggleRow(espPage, "Enable ESP", function() return VIS.enabled end, function(v) VIS.enabled = v end) toggleRow(espPage, "Box", function() return VIS.box end, function(v) VIS.box = v end) segmentedRow(espPage, "Box style", { { label = "Corners", value = "Corners" }, { label = "Full", value = "Full" }, }, function() return VIS.boxStyle end, function(v) VIS.boxStyle = v end) toggleRow(espPage, "Filled box", function() return VIS.filledBox end, function(v) VIS.filledBox = v end) sliderRow(espPage, "Fill opacity", 0.5, 0.98, function() return VIS.fillAlpha end, function(v) VIS.fillAlpha = v end, function(v) return string.format("%d%%", math.floor((1 - v) * 100 + 0.5)) end) toggleRow(espPage, "Health bar", function() return VIS.healthBar end, function(v) VIS.healthBar = v end) toggleRow(espPage, "Name", function() return VIS.name end, function(v) VIS.name = v end) toggleRow(espPage, "Distance", function() return VIS.distance end, function(v) VIS.distance = v end) toggleRow(espPage, "Weapon / tag", function() return VIS.weapon end, function(v) VIS.weapon = v end) toggleRow(espPage, "Head dot", function() return VIS.headDot end, function(v) VIS.headDot = v end) toggleRow(espPage, "Skeleton", function() return VIS.skeleton end, function(v) VIS.skeleton = v end) toggleRow(espPage, "Snapline", function() return VIS.snapline end, function(v) VIS.snapline = v end) sliderRow(espPage, "Max distance", 100, 2000, function() return VIS.maxDistance end, function(v) VIS.maxDistance = v end, function(v) return string.format("%d studs", math.floor(v + 0.5)) end) sliderRow(espPage, "Text size", 8, 20, function() return VIS.textSize end, function(v) VIS.textSize = v end, function(v) return string.format("%d px", math.floor(v + 0.5)) end) toggleRow(espPage, "Watermark", function() return VIS.watermark end, function(v) VIS.watermark = v end) -- COLOR -- EFFECTS sectionHeader(espPage, "effects") toggleRow(espPage, "Tracers", function() return VIS.tracers end, function(v) VIS.tracers = v end) toggleRow(espPage, "Tracers on enemy shots", function() return VIS.tracersEnemy end, function(v) VIS.tracersEnemy = v end) colorRow(espPage, "Tracer colour", function() return VIS.tracerColor end, function(v) VIS.tracerColor = v end) colorRow(espPage, "Enemy tracer colour", function() return VIS.tracerColorEnemy end, function(v) VIS.tracerColorEnemy = v end) sliderRow(espPage, "Tracer life", 0.05, 1, function() return VIS.tracerLife end, function(v) VIS.tracerLife = v end, function(v) return string.format("%d ms", math.floor(v * 1000 + 0.5)) end) sliderRow(espPage, "Tracer thickness", 0.1, 0.8, function() return VIS.tracerThickness end, function(v) VIS.tracerThickness = v end, function(v) return string.format("%.2f", v) end) toggleRow(espPage, "Impact markers", function() return VIS.markers end, function(v) VIS.markers = v end) colorRow(espPage, "Marker colour (world)", function() return VIS.markerColorWorld end, function(v) VIS.markerColorWorld = v end) colorRow(espPage, "Marker colour (player)", function() return VIS.markerColorPlayer end, function(v) VIS.markerColorPlayer = v end) sliderRow(espPage, "Marker size", 8, 48, function() return VIS.markerSize end, function(v) VIS.markerSize = v end, function(v) return string.format("%d px", math.floor(v + 0.5)) end) sliderRow(espPage, "Marker life", 0.1, 1.5, function() return VIS.markerLife end, function(v) VIS.markerLife = v end, function(v) return string.format("%d ms", math.floor(v * 1000 + 0.5)) end) toggleRow(espPage, "Hitsounds", function() return VIS.hitsounds end, function(v) VIS.hitsounds = v end) sliderRow(espPage, "Hitsound volume", 0, 100, function() return VIS.hitsoundVolume end, function(v) VIS.hitsoundVolume = v end, function(v) return string.format("%d%%", math.floor(v + 0.5)) end) -- CHAMS sectionHeader(espPage, "chams") toggleRow(espPage, "Chams (highlight)", function() return VIS.chams end, function(v) VIS.chams = v end) toggleRow(espPage, "Through walls", function() return VIS.chamsThrough end, function(v) VIS.chamsThrough = v end) toggleRow(espPage, "Thermal breathing", function() return VIS.chamsThermal end, function(v) VIS.chamsThermal = v end) toggleRow(espPage, "Rainbow fill", function() return VIS.chamsRainbow end, function(v) VIS.chamsRainbow = v end) toggleRow(espPage, "Fade with distance", function() return VIS.chamsFade end, function(v) VIS.chamsFade = v end) colorRow(espPage, "Fill colour", function() return VIS.chamsFill end, function(v) VIS.chamsFill = v end) sliderRow(espPage, "Fill alpha", 0, 100, function() return VIS.chamsFillAlpha end, function(v) VIS.chamsFillAlpha = v end, function(v) return string.format("%d%%", math.floor(v + 0.5)) end) colorRow(espPage, "Outline (players/bots)", function() return VIS.chamsOutline end, function(v) VIS.chamsOutline = v end) colorRow(espPage, "Outline (dummies)", function() return VIS.chamsOutlineDummy end, function(v) VIS.chamsOutlineDummy = v end) sliderRow(espPage, "Outline alpha", 0, 100, function() return VIS.chamsOutlineAlpha end, function(v) VIS.chamsOutlineAlpha = v end, function(v) return string.format("%d%%", math.floor(v + 0.5)) end) sectionHeader(espPage, "material chams") toggleRow(espPage, "Chams on body parts", function() return VIS.matChams end, function(v) VIS.matChams = v end) toggleRow(espPage, "Rainbow body", function() return VIS.matChamsRainbow end, function(v) VIS.matChamsRainbow = v end) segmentedRow(espPage, "Material", { { label = "ForceField", value = "ForceField" }, { label = "Neon", value = "Neon" }, { label = "Glass", value = "Glass" }, { label = "Plastic", value = "Plastic" }, { label = "SmoothPlastic", value = "SmoothPlastic" }, { label = "Metal", value = "Metal" }, { label = "Foil", value = "Foil" }, { label = "Ice", value = "Ice" }, { label = "Marble", value = "Marble" }, { label = "Granite", value = "Granite" }, { label = "Slate", value = "Slate" }, { label = "Concrete", value = "Concrete" }, { label = "Wood", value = "Wood" }, { label = "Fabric", value = "Fabric" }, }, function() return VIS.matChamsMaterial end, function(v) VIS.matChamsMaterial = v end) segmentedRow(espPage, "Pattern", { { label = "Static", value = "Static" }, { label = "Wave", value = "Wave" }, { label = "Pulse", value = "Pulse" }, { label = "Noise", value = "Noise" }, { label = "Lightning", value = "Lightning" }, { label = "Scanline", value = "Scanline" }, { label = "Grid", value = "Grid" }, }, function() return VIS.matChamsPattern end, function(v) VIS.matChamsPattern = v end) colorRow(espPage, "Body colour", function() return VIS.matChamsColor end, function(v) VIS.matChamsColor = v end) sliderRow(espPage, "Body alpha", 0, 1, function() return VIS.matChamsAlpha end, function(v) VIS.matChamsAlpha = v end, function(v) return string.format("%.2f", v) end) sliderRow(espPage, "Pattern speed", 0.5, 12, function() return VIS.matChamsSpeed end, function(v) VIS.matChamsSpeed = v end, function(v) return string.format("%.1f", v) end) sectionHeader(colorPage, "colors") colorRow(colorPage, "Enemy", function() return VIS.enemyColor end, function(v) VIS.enemyColor = v end) colorRow(colorPage, "Own tribe", function() return VIS.teamColor end, function(v) VIS.teamColor = v end) colorRow(colorPage, "Training dummy", function() return VIS.dummyColor end, function(v) VIS.dummyColor = v end) colorRow(colorPage, "Health (low)", function() return VIS.healthBad end, function(v) VIS.healthBad = v end) colorRow(colorPage, "Health (full)", function() return VIS.healthGood end, function(v) VIS.healthGood = v end) colorRow(colorPage, "Text", function() return VIS.textColor end, function(v) VIS.textColor = v end) -- MISC sectionHeader(miscPage, "hvH") toggleRow(miscPage, "Desync anti-aim", function() return CONFIG.desync end, function(v) CONFIG.desync = v end) sliderRow(miscPage, "Desync amount", 0, 12, function() return CONFIG.desyncAmount end, function(v) CONFIG.desyncAmount = v end, function(v) return string.format("%.1f studs", v) end) toggleRow(miscPage, "Step across their aim", function() return CONFIG.desyncAimAware end, function(v) CONFIG.desyncAimAware = v end) toggleRow(miscPage, "Desync only when walking", function() return CONFIG.desyncWalkOnly end, function(v) CONFIG.desyncWalkOnly = v end) sliderRow(miscPage, "Desync interval", 0.05, 0.6, function() return CONFIG.desyncInterval end, function(v) CONFIG.desyncInterval = v end, function(v) return string.format("%d ms", math.floor(v * 1000 + 0.5)) end) toggleRow(miscPage, "Step in height too", function() return CONFIG.desyncVertical end, function(v) CONFIG.desyncVertical = v end) sliderRow(miscPage, "Height step", 0, 6, function() return CONFIG.desyncVerticalAmount end, function(v) CONFIG.desyncVerticalAmount = v end, function(v) return string.format("%.1f studs up", v) end) sliderRow(miscPage, "Interval variation", 0, 0.6, function() return CONFIG.desyncJitter end, function(v) CONFIG.desyncJitter = v end, function(v) return string.format("+/- %d%%", math.floor(v * 100 + 0.5)) end) sliderRow(miscPage, "Local view compensation", 0, 100, function() return CONFIG.desyncLocalSteady end, function(v) CONFIG.desyncLocalSteady = v end, function(v) return v >= 100 and "screen stays still" or string.format("%d%%", math.floor(v + 0.5)) end) sectionHeader(miscPage, "dodge") toggleRow(miscPage, "Dodge when attacked", function() return CONFIG.dodge end, function(v) CONFIG.dodge = v end) toggleRow(miscPage, "Jitter dodge (draw to landing)", function() return CONFIG.jitterDodge end, function(v) CONFIG.jitterDodge = v end) sliderRow(miscPage, "Jitter studs min", 1, 30, function() return CONFIG.jitterMin end, function(v) CONFIG.jitterMin = v end, function(v) return string.format("%.0f studs", v) end) sliderRow(miscPage, "Jitter studs max", 1, 30, function() return CONFIG.jitterMax end, function(v) CONFIG.jitterMax = v end, function(v) return string.format("%.0f studs", v) end) sliderRow(miscPage, "Jitter every", 0.003, 0.1, function() return CONFIG.jitterInterval end, function(v) CONFIG.jitterInterval = v end, function(v) return string.format("%.0f ms", v * 1000) end) toggleRow(miscPage, "Smart dodge (their weapon and distance)", function() return CONFIG.dodgeSmart end, function(v) CONFIG.dodgeSmart = v end) sliderRow(miscPage, "Dodge studs min", 2, 40, function() return CONFIG.dodgeDistanceMin end, function(v) CONFIG.dodgeDistanceMin = v end, function(v) return string.format("%.1f studs", v) end) sliderRow(miscPage, "Dodge studs max", 2, 40, function() return CONFIG.dodgeDistanceMax end, function(v) CONFIG.dodgeDistanceMax = v end, function(v) return string.format("%.1f studs", v) end) sliderRow(miscPage, "Dodge easing", 0, 0.2, function() return CONFIG.dodgeRamp end, function(v) CONFIG.dodgeRamp = v end, function(v) return v < 0.005 and "instant, like the desync" or string.format("%d ms", math.floor(v * 1000 + 0.5)) end) sliderRow(miscPage, "Dodge hold", 0, 1.5, function() return CONFIG.dodgeHold end, function(v) CONFIG.dodgeHold = v end, function(v) return v < 0.005 and "no hold, just the flick" or string.format("%d ms", math.floor(v * 1000 + 0.5)) end) sliderRow(miscPage, "Dodge reacts within", 10, 200, function() return CONFIG.dodgeRange end, function(v) CONFIG.dodgeRange = v end, function(v) return string.format("%.0f studs", v) end) toggleRow(miscPage, "Face whoever we dodge", function() return CONFIG.dodgeFacing end, function(v) CONFIG.dodgeFacing = v end) toggleRow(miscPage, "Steady camera on dodge", function() return CONFIG.dodgeSteadyCamera end, function(v) CONFIG.dodgeSteadyCamera = v end) toggleRow(miscPage, "Re-step inside the flight", function() return CONFIG.dodgeDouble end, function(v) CONFIG.dodgeDouble = v end) toggleRow(miscPage, "Hop out of melee reach", function() return CONFIG.meleeHop end, function(v) CONFIG.meleeHop = v end) sliderRow(miscPage, "Hop height", 4, 20, function() return CONFIG.meleeHopHeight end, function(v) CONFIG.meleeHopHeight = v end, function(v) return string.format("%.0f studs up", v) end) sectionHeader(miscPage, "dodge visualization") toggleRow(miscPage, "Real body visualization", function() return CONFIG.realBody end, function(v) CONFIG.realBody = v end) colorRow(miscPage, "Marker colour", function() return VIS.realBodyColor end, function(v) VIS.realBodyColor = v end) toggleRow(miscPage, "Colour by distance", function() return CONFIG.realBodyHeat end, function(v) CONFIG.realBodyHeat = v end) sliderRow(miscPage, "Marker size", 0.5, 2.5, function() return CONFIG.realBodyScale end, function(v) CONFIG.realBodyScale = v end, function(v) return string.format("%.2fx", v) end) toggleRow(miscPage, "Distance label", function() return CONFIG.realBodyTag end, function(v) CONFIG.realBodyTag = v end) toggleRow(miscPage, "Crosshair chevron", function() return CONFIG.realBodyChevron end, function(v) CONFIG.realBodyChevron = v end) sectionHeader(miscPage, "menu") toggleRow(miscPage, "Show menu on launch", function() return panel.Visible end, function(v) panel.Visible = v end) buttonRow(miscPage, "Save config", function() local ok, message = saveConfig() logEvent("config", (ok and "save: " or "save failed: ") .. tostring(message)) end) buttonRow(miscPage, "Load config", function() local ok, message = loadConfig() refreshAll() logEvent("config", (ok and "load: " or "load failed: ") .. tostring(message)) end) buttonRow(miscPage, "Reset all settings", function() for k, v in pairs(CONFIG) do local d = DEFAULTS["c_" .. k]; if d ~= nil then CONFIG[k] = d end end for k, v in pairs(VIS) do local d = DEFAULTS["v_" .. k]; if d ~= nil then VIS[k] = d end end CONFIG.leadScale = 0.5 refreshAll() saveSoon() end) buttonRow(miscPage, "Unload script", function() pcall(function() getgenv().TribesOverlay.unload() end) end) sectionHeader(miscPage, "info") local envStat = infoRow(miscPage, "loading...") for name, b in pairs(tabButtons) do local on = name == "AIM" pages[name].Visible = on b.BackgroundTransparency = on and 0 or 1 b.BackgroundColor3 = on and UI.theme.select or UI.theme.bg b.TextColor3 = on and UI.theme.bright or UI.theme.dim end keyRowFor(aimPage) -- keep panel on screen after rotation / viewport change local function clampPanel() local vs = Camera.ViewportSize local x = math.clamp(panel.Position.X.Offset, -panelW + 60, vs.X - 60) local y = math.clamp(panel.Position.Y.Offset, 8, math.max(8, vs.Y - 44)) panel.Position = UDim2.new(0, x, 0, y) local fx = math.clamp(fab.Position.X.Offset, 0, math.max(0, vs.X - 52)) local fy = math.clamp(fab.Position.Y.Offset, 0, math.max(0, vs.Y - 52)) fab.Position = UDim2.new(0, fx, 0, fy) end pcall(function() Camera:GetPropertyChangedSignal("ViewportSize"):Connect(clampPanel) end) clampPanel() -- status ticker local statAccum = 0 RunService.RenderStepped:Connect(function(dt) statAccum += dt if statAccum < 0.25 then return end statAccum = 0 local aiming = CONFIG.enabled statusLamp.BackgroundColor3 = aiming and UI.theme.accent or UI.theme.off if statusText then statusText.Text = string.format("%s shots %d hits %d/%d tracers %d marks %d%s%s", CONFIG.enabled and "aim on" or "aim off", STATE.redirected or 0, STATE.ourHits or 0, STATE.hitEvents or 0, STATE.tracersDrawn or 0, STATE.markersDrawn or 0, (STATE.tracerNoOrigin or 0) > 0 and (" noOrigin " .. STATE.tracerNoOrigin) or "", (STATE.hitsNoRecord or 0) > 0 and (" unrecorded " .. STATE.hitsNoRecord) or "") end aimStat.Text = string.format( "targets in pool: %d\nredirected shots: %d\nmode: %s lead: %.3fs%s%s%s", STATE.pool, STATE.redirected, CONFIG.mode, leadTime(), STATE.lastDenial and ("\nlast denial: " .. tostring(STATE.lastDenial.reason)) or "", STATE.lastError and ("\nerror: " .. tostring(STATE.lastError)) or "", string.format("\nlead %.3fs (ping %.3f + view %.3f + trim %+.3f)\nhit rate: %d/%d recent %d%%", leadTime(), oneWayLatency(), CONFIG.viewDelay or 0, CONFIG.delayComp or 0, (STATE.cal and STATE.cal.hitCount) or 0, ((STATE.cal and STATE.cal.hitCount) or 0) + ((STATE.cal and STATE.cal.missCount) or 0), (function() local r = STATE.cal and STATE.cal.recent or {} if #r == 0 then return 0 end local n = 0 for _, x in ipairs(r) do n += x end return math.floor(n / #r * 100) end)()) .. string.format("\nview delay measured: %d samples gated: %d", (STATE.cal and STATE.cal.delayCount) or 0, (STATE.cal and STATE.cal.gated) or 0) .. (function() local o = STATE.cal and STATE.cal.onUs if not o then return "\ndesync: no incoming fire yet" end return string.format("\nshots taken on us: desync on %d (%d dmg) | off %d (%d dmg)", o.on, math.floor(o.onDmg), o.off, math.floor(o.offDmg)) end)()) envStat.Text = string.format("%s place %d\nview %s aim %s", game.Name, game.PlaceId, VIS.enabled and "on" or "off", CONFIG.enabled and "on" or "off") watermark.Visible = VIS.watermark if VIS.watermark then watermark.Text = string.format("TRIBES - CS:GO aim %s fov %d %s targets %d", CONFIG.enabled and "ON" or "off", math.floor(CONFIG.fov + 0.5), CONFIG.targets, STATE.pool) end 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 } -- 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 -- Real body visualization. -- -- The camera is pulled back by the displacement, so the screen shows the body we would -- have with the anti-aim off, while every other client - and the server, when it resolves -- a hit - is looking at the displaced root. The body that decides whether we get hit is -- therefore the one we cannot see, and a held offset is invisible precisely when it matters -- most, standing still. -- -- Drawn as a marker rather than as a body: a wireframe in the selection style, a footprint -- on the floor, a beam back to the body on screen, and a chevron orbiting the crosshair -- that carries the direction and the distance. The chevron is the part that earns its -- place - a world marker tells you where the body is only once you have looked for it, -- while a chevron on the crosshair tells you which way it is off without leaving the fight. -- -- Nothing here collides, queries or touches, so it cannot disturb the aim's raycasts or -- the physics, and the whole thing reads at a glance: blink-fast when the offset changes, -- dim and calm when the body is where it looks. 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 solutionModel() 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 -- The acquire gate: only shoot when the solve is a shot that lands. local function shotClears(started) local held = started and (os.clock() - started) or 0 return held >= (CONFIG.acquireDelay or 0.3) 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 local solution = selectSolution(origin, speed, tuning.gravity or 0, tuning) if not solution then return end if CONFIG.hitGate then local flight = solution.time or 1 local stability = STATE.trackStability(solution.model) if flight > (CONFIG.maxFlight or 0.35) or stability > 2.5 or not shotClears(STATE.lockSince) then CAL.gated = (CAL.gated or 0) + 1 return end 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 selectSolution(origin, speed, tuning.gravity or 0, tuning) 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 local solution = selectSolution(origin, speed, tuning.gravity or 0, tuning) if not solution then return end if CONFIG.hitGate then local flight = solution.time or 1 local stability = STATE.trackStability(solution.model) -- A target that has only just become visible is usually peeking past cover -- and will be back behind it by the time the arrow arrives, so require the -- same target to have been continuously shootable for a moment first. if flight > (CONFIG.maxFlight or 0.35) or stability > 2.5 or not shotClears(STATE.lockSince) then CAL.gated = (CAL.gated or 0) + 1 return end 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 if UI.titleBlob then -- one position write per frame, phase taken from the clock so it cannot drift. -- Travel runs -0.34 .. 1.0, i.e. completely off the left edge to completely -- off the right, so neither end of the loop is visible. local phase = (os.clock() * 0.28) % 1 UI.titleBlob.Position = UDim2.new(-0.34 + 1.34 * phase, 0, 0, 0) 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 warn("[csgo] esp render error: " .. text) 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 heartbeat:Disconnect() 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() 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 = solveIntercept, fitState = function(samples, window) return stateFit(samples, window) end, filterProbe = function(list) local rec = {} for _, m in ipairs(list) do filterUpdate(rec, m.p, m.t) end return rec.fp, rec.fv end, saveSettings = function() local ok, message = saveConfig() return ok, message end, loadSettings = function() local ok, message = loadConfig() refreshAll() 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, fitVelocity = velocityFit, checkPath = pathCheck, minimumLaunch = minimumLaunch, planMortar = function(model) return planMortar(model) 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 selectSolution(origin, speed, tuning.gravity or 60, tuning) 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() panel.Visible = false end, showMenu = function() panel.Visible = true end, unload = unload, } getgenv().TribesOverlay = api getgenv().SilentAim = api print("[csgo] loaded. game=" .. game.Name .. " targets=" .. CONFIG.targets .. " aim=" .. tostring(CONFIG.enabled) .. " esp=" .. tostring(VIS.enabled)) return { installed = true, gui = sg, place = game.PlaceId }