-- R1V4LZ (Rivals) hub - readable build -- Same program as the compact build: indented and formatted, decompiler placeholders renamed (arg / v115 / tbl17 / fn35 / index2 ...), -- section banners and module notes added. Search for a title below to jump to a section. -- Lifted ragebot modules are keyed by letters (`do -- cF FighterRegistry: ...`); their accessors are modules.(). -- -- Sections, in file order: -- Boot hooks: analytics remote, Player:Kick, ScriptContext.Error -- Startup: environment and single-instance guard -- Executor capability stubs -- Supported experiences and shared module registry -- UI library: embedded Lucide icon subset -- UI library: icons -- UI library (Obsidian): windows, tabs, widgets, notifications -- UI library: config save / load (SaveManager) -- UI library: themes (ThemeManager) -- Script paths and storage folders -- Connection registry -- Menu settings (UI Settings tab) -- Game bootstrap -- Ragebot (ZHub): lifted modules and standalone boot -- Ranked charm spoofer -- UI kit (ZkxUIKit): polish for Obsidian windows -- UI extras: font, fades, scale, cursor, splash -- Teleport rage (Automation > Ragebot) -- Error reporter -- Safe executor wrappers -- Game section (Rivals) -- Services, player refs and game modules -- Aimbot state: silent aim, camera aim, triggerbot -- Weapon mods: attack speed, reload, no spread, recoil, melee reach -- ESP (classic): player cards and reactive bars -- ESP (classic): lines and corner boxes -- Pickup drops (health / ammo) tracking -- Weapon mods: hooks, input prototypes, knife extender -- Aimbot bridge: pointer, targeting helpers -- Silent aim and triggerbot runtime -- Flickbot -- World visuals: lighting, sky, weather, lightning, post effects -- World presets -- Viewmodel visuals: chams, highlight, wireframe -- Crosshair presets -- Native removals: flashbang, scope, muzzle flash, hitmarker, ... -- Movement: speed, flight, noclip, auto strafe, long jump -- Device spoof -- Player spoofer -- Animation player -- Movement recorder -- Hit / kill feedback and bullet tracers -- Aimbot diagnostics -- Silent aim: hook target resolution -- Cursor triggerbot -- Aimbot targeting rules (priority, delays, lock) -- Combat Bot -- ESP render loop and cheater detector -- ESP text style extras -- Local ready state -- Hub shell (tower v3): event tab, tower kit, features, menus -- Kill-seat rage (RivalsRagebot) -- Auto loadout -- Auto ban / map vote -- Subspace tripmine automation -- Auto queue -- Cosmetics changer (Unlock All) -- World ESP: items, drops and sounds -- World ESP: sound visualizer -- ESP preview (the live preview in the ESP tab) -- Custom sounds / skyboxes dialogs -- Menu: ESP tab -- Anti-aim logic (RivalsAntihit) -- Menu: anti-aim -- Menu: Combat tab -- Menu: weapon mods -- Menu: Client Effects, Crosshair and World tabs -- Menu: ragebot (ZHub) -- Menu: classic ragebot -- Menu: cosmetics -- Menu: Automation tab -- Menu: Movement and Misc tabs -- Staff detector -- Menu: assembly -- Runtime lifecycle: teardown, loops, startup -- Startup: config profiles, menu settings, loops -- ====================================================================== -- Boot hooks: analytics remote, Player:Kick, ScriptContext.Error -- ====================================================================== local success, err = pcall(function() local Players = cloneref(game:GetService('Players')) local ReplicatedFirst = cloneref(game:GetService('ReplicatedFirst')) local ReplicatedStorage = cloneref(game:GetService('ReplicatedStorage')) local ScriptContext = cloneref(game:GetService('ScriptContext')) local LocalPlayer = Players.LocalPlayer local ac_script = ReplicatedFirst:WaitForChild('LocalScript3', 10) local remotes_folder = ReplicatedStorage:WaitForChild('Remotes', 10) local analytics_folder = remotes_folder:WaitForChild('AnalyticsPipeline', 10) local ac_event = analytics_folder:WaitForChild('RemoteEvent', 10) local TrustedFunctions = setmetatable({}, { __mode = 'k' }) local function TrustFunction(fn) if type(fn) == 'function' then TrustedFunctions[fn] = true end return fn end local function IsTrustedFunction(fn) return TrustedFunctions[fn] == true end local SafeHook = function(hookfn, ...) local args = { ... } local func, inst, metamethod, detour if hookfn == hookmetamethod then inst = args[1] metamethod = args[2] detour = args[3] else func = args[1] detour = args[2] end local original_func if hookfn == hookfunction and iscclosure(func) then detour = newcclosure(detour) end if not iscclosure(detour) then detour = newcclosure(detour) end local ok, hook_err = pcall(function() TrustFunction(detour) if hookfn == hookmetamethod then original_func = hookfn(inst, metamethod, detour) else original_func = hookfn(func, detour) end end) return original_func end local oldfireserver oldfireserver = SafeHook(hookfunction, ac_event.FireServer, function(self, ...) local args = { ... } if type(args[1]) == 'number' then return nil end return oldfireserver(self, ...) end) local kKickNames = { 'Kick', 'kick' } for _, name in ipairs(kKickNames) do local func = LocalPlayer[name] if type(func) == 'function' then local oldfunc oldfunc = SafeHook(hookfunction, func, function(self, ...) if self == LocalPlayer and not checkcaller() then return nil end return oldfunc(self, ...) end) end end for _, c in ipairs(getconnections(ScriptContext.Error)) do if c.Function and not IsTrustedFunction(c.Function) then SafeHook(hookfunction, c.Function, function(...) return nil end) end end end); (function() -- ====================================================================== -- Startup: environment and single-instance guard -- ====================================================================== -- Global environment resolution local function ResolveGlobalEnv() local candidates = {} if type(getgenv) == 'function' then local ok, env = pcall(getgenv) if ok and type(env) == 'table' then table.insert(candidates, env) end end if type(getfenv) == 'function' then local ok, env = pcall(getfenv, 0) if ok and type(env) == 'table' then table.insert(candidates, env) end end for _, env in ipairs(candidates) do local wrote = pcall(function() env.__ZkxHubEnvProbe = true end) local seen = __ZkxHubEnvProbe == true pcall(function() env.__ZkxHubEnvProbe = nil end) if wrote and seen then return env end end return _G end local __ZkhHub_genv = ResolveGlobalEnv() if __ZkhHub_genv.Executed or _G._ZkxHubExecuted then return end __ZkhHub_genv.Executed = true pcall(function() _G._ZkxHubExecuted = true end) -- ====================================================================== -- Executor capability stubs -- ====================================================================== -- Executor capability stubs do local _noop = function() end local _genv = __ZkhHub_genv local _stubs = { getconnections = function() return {} end, hookmetamethod = function(obj, method, hook) return nil end, hookfunction = function(orig, hook) return orig end, newcclosure = function(f) return f end, getnamecallmethod = function() return '' end, firetouchinterest = _noop, firesignal = _noop, setclipboard = _noop, cloneref = function(o) return o end, gethui = _noop, checkcaller = function() return false end, clonefunction = function(f) return f end, getrawmetatable = function(o) return getmetatable(o) end, setrawmetatable = function(o, mt) return setmetatable(o, mt) end, setreadonly = _noop, isreadonly = function() return false end, getgc = function() return {} end, filtergc = function() return {} end, replicatesignal = _noop, fireclickdetector = _noop, fireproximityprompt = _noop, getthreadidentity = function() return 0 end, setthreadidentity = _noop, getsenv = function() return {} end, getscriptfromthread = _noop, getcallbackvalue = _noop, getnilinstances = function() return {} end, getinstances = function() return {} end, isfunctionhooked = function() return false end, restorefunction = _noop, } local _stubbed = {} for name, stub in pairs(_stubs) do if rawget(_genv, name) == nil then _genv[name] = stub _stubbed[name] = true end end if game.GameId ~= 6035872082 and _genv.hookfunction ~= _stubs.hookfunction then local ok = pcall(_genv.hookfunction, function() end, function() end) if not ok then _genv.hookfunction = _stubs.hookfunction _stubbed.hookfunction = true end end local _exName = type(identifyexecutor) == 'function' and identifyexecutor() or '' local _exNameLower = string.lower(_exName) if _exNameLower:find('solara') or _exNameLower:find('xeno') then _genv.firetouchinterest = _noop _stubbed.firetouchinterest = true end if type(_genv.queue_on_teleport) ~= 'function' then local _qot = (type(queue_on_teleport) == 'function' and queue_on_teleport) or (type(queueonteleport) == 'function' and queueonteleport) or (type(syn) == 'table' and type(syn.queue_on_teleport) == 'function' and syn.queue_on_teleport) or nil if _qot then _genv.queue_on_teleport = _qot end end for _, name in ipairs({ 'queue_on_teleport', 'sethiddenproperty', 'gethiddenproperty', 'getrenv', 'getcallingscript' }) do if type(_genv[name]) ~= 'function' then _stubbed[name] = true end end if type(getgenv) ~= 'function' then _stubbed.getgenv = true else local ok, env = pcall(getgenv) if not (ok and env == _genv) then _stubbed.getgenv = true end end local _debugExt = {} for _, name in ipairs({ 'getupvalue', 'getupvalues', 'setupvalue', 'getconstant', 'getconstants', 'setconstant', 'getprotos', 'getproto', 'getstack', 'info', }) do local ok, value = pcall(function() return debug[name] end) _debugExt['debug.' .. name] = (ok and type(value) == 'function') or false end _genv.ZkxHubCaps = { stubbed = _stubbed, has = function(name) if _stubbed[name] then return false end local probed = _debugExt[name] if probed ~= nil then return probed end local target = _genv for part in string.gmatch(name, '[^%.]+') do if type(target) ~= 'table' then return false end local ok, value = pcall(function() return target[part] end) if not ok then return false end target = value end return type(target) == 'function' end, } end -- ====================================================================== -- Supported experiences and shared module registry -- ====================================================================== -- Supported experiences local Games = { ['RIVALS'] = { ID = 6035872082, State = true }, } repeat task.wait() until game:IsLoaded() local function __ZkxHubElevateIdentity() if type(setthreadidentity) == 'function' then pcall(setthreadidentity, 8) end end __ZkxHubElevateIdentity() -- Shared module registry local __ZkhHub_shared = {} local __shared_module_cache = {} local function RequireSharedModule(name) if type(__ZkhHub_shared) ~= 'table' then error('Missing shared module table') end local cached = __shared_module_cache[name] if cached ~= nil then return cached end local loader = __ZkhHub_shared[name] if type(loader) ~= 'function' then error('Missing shared module: ' .. tostring(name)) end local result = loader() __shared_module_cache[name] = result return result end local Library = nil local ErrorReporter = nil local HttpService = game:GetService('HttpService') -- ====================================================================== -- UI library: ZkxVader (a fork of Vaderhaxx with the Obsidian API) - replaces the embedded Obsidian library in this build -- ====================================================================== __ZkhHub_shared.obsidian_library = function() -- ==== 00_core.lua ==== -- ZkxVader core: a fork of Vaderhaxx (ImInsane-1337/Libraries, discontinued 2024) extended to the Obsidian API the R1V4LZ hub speaks. -- This chunk defines everything the other fork files share: services, palette + theme registry, font, tween helper, signals, the Library table. -- Joined with the other fork files by build_fork.py; the whole thing is one function body that returns Library. local cloneref = (cloneref or clonereference or function(instance) return instance end) local function svc(name) return cloneref(game:GetService(name)) end local UserInputService = svc('UserInputService') local Players = svc('Players') local RunService = svc('RunService') local TweenService = svc('TweenService') local TextService = svc('TextService') local HttpService = svc('HttpService') local GuiService = svc('GuiService') local CoreGui = svc('CoreGui') local LocalPlayer = Players.LocalPlayer local Mouse = LocalPlayer and LocalPlayer:GetMouse() local rgb, hsv, dim2, dim, vec2 = Color3.fromRGB, Color3.fromHSV, UDim2.new, UDim.new, Vector2.new local floor, clamp, max, min, abs, sin = math.floor, math.clamp, math.max, math.min, math.abs, math.sin --// Library table (the Obsidian-shaped surface lives here) \\-- local Library = { Version = 'ZkxVader 1.0 (Vaderhaxx fork)', Toggles = {}, Options = {}, Labels = {}, Buttons = {}, Tabs = {}, Signals = {}, UnloadSignals = {}, OnUnload = nil, -- function Library:OnUnload(fn) Unloaded = false, Toggled = false, ShowCustomCursor = true, NotifyOnError = false, NotifySide = 'Left', CornerRadius = 0, ActiveLoading = nil, Window = nil, ScreenGui = nil, KeybindFrame = nil, ToggleKeybind = nil, Animations = { Enabled = true, Time = 0.18 }, ThemeFade = true, DPIScale = 100, TextSize = 12, IsMobile = false, DependencyBoxes = {}, FontListeners = {}, ColorMigrations = {}, Registry = setmetatable({}, { __mode = 'k' }), -- instance -> { Property = paletteKey } } Library.__index = Library -- mobile = a touch device without a keyboard (getgenv().ZkxVaderForceMobile = true forces it, for testing on a PC) do local forced = false pcall(function() forced = ((type(getgenv) == 'function' and getgenv()) or _G).ZkxVaderForceMobile == true end) Library.IsMobile = forced or (UserInputService.TouchEnabled and not UserInputService.KeyboardEnabled) end Library.TextSize = Library.IsMobile and 14 or 12 -- touch scale: rows, buttons and bars are about 45% taller on a phone so a finger can hit them local function H(size) return Library.IsMobile and floor(size * 1.45 + 0.5) or size end Library.H = H --// Palette (Vaderhaxx defaults). a..g are the layered grays of the original, accent is its yellow. \\-- local DefaultPalette = { accent = rgb(255, 200, 69), text = rgb(255, 255, 255), a = rgb(0, 0, 0), b = rgb(56, 56, 56), c = rgb(46, 46, 46), d = rgb(12, 12, 12), e = rgb(21, 21, 21), f = rgb(84, 84, 84), g = rgb(54, 54, 54), risky = rgb(255, 80, 80), dim = rgb(150, 150, 150), } local Palette = {} for key, value in pairs(DefaultPalette) do Palette[key] = value end Library.Palette = Palette -- Obsidian's five-colour Scheme maps onto the palette (ThemeManager edits Scheme, then calls UpdateColorsUsingRegistry) Library.Scheme = { BackgroundColor = DefaultPalette.e, MainColor = DefaultPalette.c, AccentColor = DefaultPalette.accent, OutlineColor = DefaultPalette.b, FontColor = DefaultPalette.text, DarkColor = DefaultPalette.d, RedColor = DefaultPalette.risky, WhiteColor = rgb(255, 255, 255), Font = nil, -- filled in below once the font exists } --// Font: ProggyClean (the Vaderhaxx pixel font). Cached next to the hub's other files, falls back to a monospace stock font. \\-- local FONT_URL = 'https://github.com/i77lhm/storage/raw/refs/heads/main/fonts/ProggyClean.ttf' local FONT_DIR = 'ZkxHub/vader' Library.StorageDir = FONT_DIR local function loadPixelFont() local canFile = type(isfile) == 'function' and type(writefile) == 'function' and type(getcustomasset) == 'function' if not canFile then return nil end local okFont, font = pcall(function() if type(isfolder) == 'function' and type(makefolder) == 'function' then if not isfolder('ZkxHub') then makefolder('ZkxHub') end if not isfolder(FONT_DIR) then makefolder(FONT_DIR) end end local ttf, family = FONT_DIR .. '/ProggyClean.ttf', FONT_DIR .. '/ProggyClean.json' if not isfile(ttf) then local data = game:HttpGet(FONT_URL) if type(data) ~= 'string' or #data < 1000 then error('font download failed') end writefile(ttf, data) end if not isfile(family) then writefile( family, HttpService:JSONEncode({ name = 'ProggyClean', faces = { { name = 'Regular', weight = 400, style = 'normal', assetId = getcustomasset(ttf) } }, }) ) end return Font.new(getcustomasset(family), Enum.FontWeight.Regular) end) if okFont then return font end return nil end Library.PixelFont = loadPixelFont() Library.Font = Library.PixelFont or Font.fromEnum(Enum.Font.Code) Library.Scheme.Font = Library.Font --// Registries: themed properties and text objects \\-- local TextObjects = setmetatable({}, { __mode = 'k' }) local TEXT_CLASSES = { TextLabel = true, TextButton = true, TextBox = true } local function ThemeSet(instance, property, key) local entry = Library.Registry[instance] if not entry then entry = {} Library.Registry[instance] = entry end entry[property] = key instance[property] = Palette[key] end Library.Theme = ThemeSet --// Tweens (never delay a value, only its look) \\-- local function Animating() return Library.Animations.Enabled and not Library.Unloaded end local function Tween(instance, properties, time, style, direction) if not Animating() or not instance or not instance.Parent then for property, value in pairs(properties) do pcall(function() instance[property] = value end) end return nil end local tween = TweenService:Create( instance, TweenInfo.new(time or Library.Animations.Time, style or Enum.EasingStyle.Quad, direction or Enum.EasingDirection.Out), properties ) tween:Play() return tween end Library.Tween = Tween --// Instance factory: New(class, props, themed) \\-- local function New(class, props, themed) local instance = Instance.new(class) local parent = nil for property, value in pairs(props or {}) do if property == 'Parent' then parent = value else instance[property] = value end end if TEXT_CLASSES[class] then TextObjects[instance] = true if not (props and props.FontFace) then instance.FontFace = Library.Font end if not (props and props.TextSize) then instance.TextSize = Library.TextSize end if not (props and props.TextColor3) and not (themed and themed.TextColor3) then ThemeSet(instance, 'TextColor3', 'text') end if instance.ClassName ~= 'TextBox' then instance.TextWrapped = props and props.TextWrapped or false end end if class == 'Frame' or class == 'TextButton' or class == 'ImageButton' or class == 'ScrollingFrame' or class == 'TextLabel' then if not (props and props.BorderSizePixel) then instance.BorderSizePixel = 0 end end if themed then for property, key in pairs(themed) do ThemeSet(instance, property, key) end end if parent then instance.Parent = parent end return instance end Library.New = New --// Signals / unload \\-- function Library:GiveSignal(signal) table.insert(Library.Signals, signal) return signal end local function Connect(signal, callback) local connection = signal:Connect(callback) table.insert(Library.Signals, connection) return connection end function Library:OnUnload(callback) table.insert(Library.UnloadSignals, callback) end function Library:SafeCallback(callback, ...) if type(callback) ~= 'function' then return nil end local results = table.pack(xpcall(callback, function(err) return debug.traceback(tostring(err), 2) end, ...)) if results[1] then return table.unpack(results, 2, results.n) end if Library.NotifyOnError and Library.Notify then pcall(Library.Notify, Library, 'Callback error: ' .. tostring(results[2]):sub(1, 140)) end warn('[ZkxVader] callback error: ' .. tostring(results[2])) return nil end local SafeCallback = function(...) return Library:SafeCallback(...) end --// Small helpers shared by every widget \\-- local function TextWidth(text, size, font) local ok, bounds = pcall(function() local params = Instance.new('GetTextBoundsParams') params.Text = text params.Font = font or Library.Font params.Size = size or Library.TextSize params.Width = 10000 return TextService:GetTextBoundsAsync(params) end) if ok and bounds then return bounds.X end return #tostring(text) * 6 end local function Round(number, step) step = step or 1 if step == 0 then return number end local inverse = 1 / step return floor(number * inverse + 0.5) / inverse end -- mouse position in the same space as GuiObject.AbsolutePosition (AbsolutePosition is offset by the top-bar inset, GetMouseLocation is not) local function MouseAbs() return UserInputService:GetMouseLocation() - GuiService:GetGuiInset() end Library.MouseAbs = MouseAbs local function IsMouseOver(gui) local position = MouseAbs() local corner, size = gui.AbsolutePosition, gui.AbsoluteSize return position.X >= corner.X and position.X <= corner.X + size.X and position.Y >= corner.Y and position.Y <= corner.Y + size.Y end local function UIParent() if type(gethui) == 'function' then local ok, holder = pcall(gethui) if ok and holder then return holder end end return CoreGui end -- Window-level bookkeeping the widgets need Library.Popup = nil -- the one open popup (dropdown list / colour picker / key menu); opening another closes it function Library:ClosePopup() local popup = Library.Popup Library.Popup = nil if popup and popup.Close then popup.Close() end end function Library:OpenPopup(popup) if Library.Popup and Library.Popup ~= popup then Library:ClosePopup() end Library.Popup = popup end -- Shared class tables (each later file is a do-block, so top-level locals stay far below Luau's 200 limit; cross-file things live here) local Classes = { Window = {}, Tab = {}, Groupbox = {}, Tabbox = {}, Container = {}, Dialog = {} } for _, class in pairs(Classes) do class.__index = class end Library.Classes = Classes -- nested 1px outlines (the Vaderhaxx look): Panel(parent, {'a','c','e'}, { Size = ..., Position = ..., Auto = true }) -- returns the outermost and the innermost frame; content goes into the innermost one local function Panel(parent, keys, props) props = props or {} local outer, current = nil, parent for index, key in ipairs(keys) do local frame = New('Frame', { Size = (index == 1 and props.Size) or (props.AutoXY and dim2(0, 0, 0, 0)) or (props.Auto and dim2(1, 0, 0, 0) or dim2(1, 0, 1, 0)), Position = index == 1 and props.Position or nil, AnchorPoint = index == 1 and props.AnchorPoint or nil, AutomaticSize = props.AutoXY and Enum.AutomaticSize.XY or (props.Auto and Enum.AutomaticSize.Y or Enum.AutomaticSize.None), BackgroundTransparency = (index == #keys and props.InnerTransparency) or 0, ClipsDescendants = (index == #keys) and props.Clip or false, Parent = current, }, { BackgroundColor3 = key }) if index < #keys then New('UIPadding', { PaddingTop = dim(0, 1), PaddingBottom = dim(0, 1), PaddingLeft = dim(0, 1), PaddingRight = dim(0, 1), Parent = frame }) end outer = outer or frame current = frame end return outer, current end Library.Panel = Panel --// Input router: ONE real connection per UserInputService event / per frame, many handlers behind it. -- The menu has hundreds of widgets; giving each its own always-on InputChanged listener made every mouse move wake 230+ closures. -- Now a slider or colour picker only registers while it is actually being dragged, and the window drag/resize applies once per frame. local Router = { Began = {}, Changed = {}, Ended = {}, Render = {} } local RouterDirty = {} local function dispatch(kind, ...) local list = Router[kind] local count = #list if count == 0 then return end for index = 1, count do local handle = list[index] local fn = handle and handle.fn if fn then local ok, err = pcall(fn, ...) if not ok then warn('[ZkxVader] input handler: ' .. tostring(err)) end end end if RouterDirty[kind] then RouterDirty[kind] = nil local kept = {} for _, handle in ipairs(Router[kind]) do if handle.fn then kept[#kept + 1] = handle end end Router[kind] = kept end end table.insert(Library.Signals, UserInputService.InputBegan:Connect(function(...) dispatch('Began', ...) end)) table.insert(Library.Signals, UserInputService.InputChanged:Connect(function(...) dispatch('Changed', ...) end)) table.insert(Library.Signals, UserInputService.InputEnded:Connect(function(...) dispatch('Ended', ...) end)) table.insert(Library.Signals, RunService.RenderStepped:Connect(function(...) dispatch('Render', ...) end)) -- kind: 'Began' | 'Changed' | 'Ended' (handlers get input, processed) or 'Render' (handlers get dt). Returns a handle with :Disconnect() (handle.Disconnect()). function Library:OnInput(kind, fn) local handle = { fn = fn } local list = Router[kind] list[#list + 1] = handle function handle.Disconnect() if handle.fn then handle.fn = nil RouterDirty[kind] = true end end return handle end -- ==== 10_fx.lua ==== -- ZkxVader FX: the "crazy text". One shared Heartbeat drives every looping effect; one-shots (scramble, typewriter, glitch) run on their own short threads. -- Rules: the value/text is final first, the effect only decorates; at rest the label shows its plain text; nothing here changes layout size; -- everything stops when animations are off, the menu is closed, or the label is gone. local FX = { Loops = {}, Count = 0 } Library.FX = FX local SCRAMBLE_GLYPHS = '!<>-_\\/[]{}=+*^?#%&$@01' local loopConnection = nil local function alive(label) return label and label.Parent ~= nil and not Library.Unloaded end -- a looping effect only ticks while its label is on screen and the menu is open (or the effect is flagged always-on, e.g. notifications) local function visible(item) local label = item.label if not alive(label) then return false end if not item.always and not Library.Toggled then return false end return label.AbsoluteSize.X > 0 end local function startLoop() if loopConnection then return end loopConnection = RunService.Heartbeat:Connect(function(dt) if Library.Unloaded then return end local now = os.clock() for id, item in pairs(FX.Loops) do if not alive(item.label) then FX.Loops[id] = nil FX.Count -= 1 elseif Animating() and visible(item) then if not item.rate or now - (item.last or 0) >= item.rate then item.last = now item.step(now, dt) end end end end) table.insert(Library.Signals, loopConnection) end local nextId = 0 local function addLoop(label, step, options) options = options or {} nextId += 1 local id = nextId FX.Loops[id] = { label = label, step = step, rate = options.rate, always = options.always } FX.Count += 1 startLoop() local handle = {} function handle.Stop() if FX.Loops[id] then FX.Loops[id] = nil FX.Count -= 1 end if options.restore then pcall(options.restore) end end return handle end local function rainbowSequence(offset) local keys = {} for index = 0, 6 do table.insert(keys, ColorSequenceKeypoint.new(index / 6, hsv((offset + index / 6) % 1, 1, 1))) end return ColorSequence.new(keys) end -- moving rainbow gradient over the text (title, watermark). Speed 0.2 = a full cycle in 5 seconds. function FX.Rainbow(label, options) options = options or {} local speed = options.speed or 0.2 local gradient = New('UIGradient', { Color = rainbowSequence(0), Parent = label }) Library.Registry[label] = nil label.TextColor3 = rgb(255, 255, 255) gradient.Color = rainbowSequence(0) return addLoop(label, function(now) gradient.Color = rainbowSequence(now * speed) end, { always = options.always, rate = 1 / 30, restore = function() gradient:Destroy() ThemeSet(label, 'TextColor3', 'text') end, }) end -- a bright band sweeps over accent coloured text every few seconds function FX.Shimmer(label, options) options = options or {} local every, length = options.every or 6, options.length or 0.9 -- UIGradient multiplies the text colour, so the base is white and the gradient itself carries accent -> white -> accent Library.Registry[label] = nil label.TextColor3 = rgb(255, 255, 255) local function sequence() local accent, white = Palette.accent, rgb(255, 255, 255) return ColorSequence.new({ ColorSequenceKeypoint.new(0, accent), ColorSequenceKeypoint.new(0.4, accent), ColorSequenceKeypoint.new(0.5, white), ColorSequenceKeypoint.new(0.6, accent), ColorSequenceKeypoint.new(1, accent), }) end local gradient = New('UIGradient', { Color = sequence(), Offset = vec2(-1.2, 0), Parent = label }) return addLoop(label, function(now) local phase = (now % every) / length gradient.Offset = vec2(phase < 1 and (-1.2 + phase * 2.4) or 1.2, 0) gradient.Color = sequence() -- follows theme changes end, { always = options.always, rate = 1 / 30, restore = function() gradient:Destroy() ThemeSet(label, 'TextColor3', 'text') end, }) end -- per-character colour wave (RichText). Fine for short strings only. function FX.Wave(label, options) options = options or {} local text = options.text or label.Text local speed, spread = options.speed or 0.6, options.spread or 0.07 local base = text label.RichText = true local function hex(color) return string.format('#%02x%02x%02x', floor(color.R * 255), floor(color.G * 255), floor(color.B * 255)) end return addLoop(label, function(now) local parts = table.create(#base) for index = 1, #base do local char = base:sub(index, index) if char:match('%s') then parts[index] = char else parts[index] = '' .. char .. '' end end label.Text = table.concat(parts) end, { always = options.always, rate = 1 / 20, restore = function() label.RichText = false label.Text = base end, }) end -- slow breathing of the text transparency (used for "Risky" and status lines) function FX.Pulse(label, options) options = options or {} local depth, speed = options.depth or 0.45, options.speed or 3 return addLoop(label, function(now) label.TextTransparency = (sin(now * speed) * 0.5 + 0.5) * depth end, { always = options.always, rate = 1 / 30, restore = function() label.TextTransparency = 0 end, }) end --// one-shots \\-- local function oneShot(label, text, duration, frame) if not Animating() or not duration or duration <= 0 then label.Text = text return end local token = (label:GetAttribute('FXToken') or 0) + 1 label:SetAttribute('FXToken', token) task.spawn(function() local started = os.clock() while alive(label) and label:GetAttribute('FXToken') == token do local alpha = clamp((os.clock() - started) / duration, 0, 1) if alpha >= 1 then break end label.Text = frame(alpha) RunService.Heartbeat:Wait() end if alive(label) and label:GetAttribute('FXToken') == token then label.Text = text end end) end -- random glyphs that settle left to right into the real text function FX.Scramble(label, text, duration) text = text or label.Text oneShot(label, text, duration or 0.35, function(alpha) local settled = floor(alpha * #text) local out = table.create(#text) for index = 1, #text do local char = text:sub(index, index) if index <= settled or char:match('%s') then out[index] = char else local pick = math.random(1, #SCRAMBLE_GLYPHS) out[index] = SCRAMBLE_GLYPHS:sub(pick, pick) end end return table.concat(out) end) end -- characters appear one by one function FX.Typewriter(label, text, charactersPerSecond) text = text or label.Text local rate = charactersPerSecond or 35 oneShot(label, text, #text / rate, function(alpha) return text:sub(1, floor(alpha * #text)) end) end -- two coloured ghosts jitter behind the text for a moment function FX.Glitch(label, duration) if not Animating() or not alive(label) then return end duration = duration or 0.22 local ghosts = {} for index, tint in ipairs({ rgb(255, 40, 80), rgb(40, 220, 255) }) do local ghost = New('TextLabel', { Text = label.Text, TextColor3 = tint, TextTransparency = 0.45, TextSize = label.TextSize, FontFace = label.FontFace, BackgroundTransparency = 1, TextXAlignment = label.TextXAlignment, TextYAlignment = label.TextYAlignment, Size = dim2(1, 0, 1, 0), ZIndex = label.ZIndex + (index == 1 and -1 or 0), Parent = label, }) ghost.TextColor3 = tint table.insert(ghosts, ghost) end task.spawn(function() local started = os.clock() while alive(label) and os.clock() - started < duration do for index, ghost in ipairs(ghosts) do ghost.Position = dim2(0, math.random(-2, 2) * (index == 1 and 1 or -1), 0, math.random(-1, 1)) end RunService.Heartbeat:Wait() end for _, ghost in ipairs(ghosts) do ghost:Destroy() end end) end function FX.Stop(handle) if handle and handle.Stop then handle.Stop() end end -- ==== 20_window.lua ==== -- ZkxVader window: screen gui, layered panel, header, scrolling tab strip with a gliding underline, pages, drag/resize, theme + DPI, keybind list. do local Window, Tab = Classes.Window, Classes.Tab local Scalers = {} local function GetScale() return Library.DPIScale / 100 end Library.GetScale = GetScale -- the screen in the menu's own units (what fits on it) function Library.Viewport() local camera = workspace.CurrentCamera return (camera and camera.ViewportSize or Vector2.new(1920, 1080)) / GetScale() end --// drag / resize \\-- -- Both only listen while a drag is running (shared input router) and apply once per frame. Resizing re-lays out the whole menu, so it shows an outline -- while you drag and applies the size when you let go. local function isPress(input) return input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch end local function isMove(input) return input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch end local function MakeDraggable(frame, handle, onMoved, layoutId, withSize) handle = handle or frame local moveHandle, endHandle, renderHandle local startMouse, startPosition, pending local function stop() for _, listener in ipairs({ moveHandle, endHandle, renderHandle }) do listener.Disconnect() end moveHandle, endHandle, renderHandle, pending = nil, nil, nil, nil if layoutId then Library:SaveLayout(layoutId, frame, withSize) end end Connect(handle.InputBegan, function(input) if not isPress(input) or moveHandle then return end startMouse, startPosition, pending = input.Position, frame.Position, nil Library:ClosePopup() moveHandle = Library:OnInput('Changed', function(changed) if isMove(changed) then pending = changed.Position end end) renderHandle = Library:OnInput('Render', function() if not pending then return end local delta = (pending - startMouse) / GetScale() pending = nil frame.Position = UDim2.new(startPosition.X.Scale, startPosition.X.Offset + delta.X, startPosition.Y.Scale, startPosition.Y.Offset + delta.Y) if onMoved then onMoved() end end) endHandle = Library:OnInput('Ended', function(ended) if isPress(ended) then stop() end end) end) end Library.MakeDraggable = MakeDraggable local function MakeResizable(frame, grip, minimum, layoutId) local moveHandle, endHandle, renderHandle local startMouse, startSize, pending, wanted local ghost local function stop() for _, listener in ipairs({ moveHandle, endHandle, renderHandle }) do listener.Disconnect() end moveHandle, endHandle, renderHandle, pending = nil, nil, nil, nil if ghost then ghost.Visible = false end end Connect(grip.InputBegan, function(input) if not isPress(input) or moveHandle then return end startMouse, startSize, pending, wanted = input.Position, frame.AbsoluteSize / GetScale(), nil, nil Library:ClosePopup() if not ghost then ghost = New('Frame', { Name = 'ResizeOutline', BackgroundTransparency = 1, ZIndex = 500, Active = false, Parent = Library.Main }) New('UIStroke', { Thickness = 2, Parent = ghost }, { Color = 'accent' }) end ghost.Position, ghost.Size, ghost.Visible = frame.Position, frame.Size, true moveHandle = Library:OnInput('Changed', function(changed) if isMove(changed) then pending = changed.Position end end) renderHandle = Library:OnInput('Render', function() if not pending then return end local delta = (pending - startMouse) / GetScale() pending = nil wanted = Vector2.new(max(minimum.X, startSize.X + delta.X), max(minimum.Y, startSize.Y + delta.Y)) ghost.Size = UDim2.fromOffset(wanted.X, wanted.Y) end) endHandle = Library:OnInput('Ended', function(ended) if not isPress(ended) then return end if wanted then frame.Size = UDim2.fromOffset(wanted.X, wanted.Y) end stop() if layoutId and wanted then Library:SaveLayout(layoutId, frame, true) end end) end) end --// theme \\-- -- Theme updates are cheap on purpose: a colour picker fires one per mouse move while dragging, and the menu has thousands of themed instances. -- * the new palette is only computed here; one Heartbeat loop applies it, so any number of calls in a frame cost one pass -- * a pass only touches instances whose palette key actually changed (dragging the accent leaves every grey alone) -- * "Smooth Theme Changes" blends the palette itself over a few frames instead of creating a tween per element local THEME_KEYS = { 'accent', 'text', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'risky', 'dim' } local themeTarget, themeConnection = nil, nil local function computeTheme() local scheme = Library.Scheme local e, c, b = scheme.BackgroundColor, scheme.MainColor, scheme.OutlineColor return { accent = scheme.AccentColor, text = scheme.FontColor, a = Palette.a, b = b, c = c, d = Color3.new(e.R * 0.57, e.G * 0.57, e.B * 0.57), e = e, f = b:Lerp(rgb(255, 255, 255), 0.14), g = c:Lerp(b, 0.8), risky = scheme.RedColor or DefaultPalette.risky, dim = scheme.FontColor:Lerp(e, 0.45), } end local function colorsClose(one, two) return math.abs(one.R - two.R) < 0.004 and math.abs(one.G - two.G) < 0.004 and math.abs(one.B - two.B) < 0.004 end local function applyChanged(changed) for instance, properties in pairs(Library.Registry) do if instance.Parent then for property, key in pairs(properties) do if changed[key] then instance[property] = Palette[key] end end end end end local function themeStep(dt) if not themeTarget or Library.Unloaded then return false end local blend = (Library.ThemeFade and Animating()) and (1 - math.exp(-dt * 16)) or 1 local changed, any, moving = {}, false, false for _, key in ipairs(THEME_KEYS) do local current, wanted = Palette[key], themeTarget[key] if current ~= wanted then local nextColor = wanted if blend < 1 and not colorsClose(current, wanted) then nextColor = current:Lerp(wanted, blend) moving = true end Palette[key] = nextColor changed[key], any = true, true end end if any then applyChanged(changed) end if any and Library.OnThemeChanged then pcall(Library.OnThemeChanged) end return moving end -- every call only records the wanted palette; the loop below applies it once per frame and stops by itself when the palette has arrived function Library:UpdateColorsUsingRegistry() themeTarget = computeTheme() if themeConnection or Library.Unloaded then return end themeConnection = RunService.Heartbeat:Connect(function(dt) if not themeStep(dt) then themeConnection:Disconnect() themeConnection = nil end end) table.insert(Library.Signals, themeConnection) end function Library:SetFont(font) local face = font if typeof(font) == 'EnumItem' then face = Font.fromEnum(font) end if typeof(face) ~= 'Font' then return end Library.Font = face Library.Scheme.Font = face for instance in pairs(TextObjects) do pcall(function() instance.FontFace = face end) end for _, listener in ipairs(Library.FontListeners) do pcall(listener) end end function Library:SetDPIScale(percent) percent = tonumber(percent) or 100 Library.DPIScale = clamp(percent, 40, 250) for _, scaler in ipairs(Scalers) do scaler.Scale = GetScale() end if Library.Main then Library.Main.Size = UDim2.fromScale(1 / GetScale(), 1 / GetScale()) end if Library.Window then task.defer(function() Library:FitWindow() end) end end function Library:SetBackgroundImage(image) local window = Library.Window if not (window and window.BackgroundImage) then return end image = tostring(image or '') window.BackgroundImage.Image = image window.BackgroundImage.Visible = image ~= '' end function Library:GetCustomIcon() return nil end -- the Vaderhaxx look has no tab icons --// open / close \\-- --// custom cursor (Obsidian's: a small white cross with a dark outline, shown while the menu is open) \\-- -- The system cursor is hidden while it is up and put back when the menu closes or the library unloads. It follows the mouse in a render step -- (last priority, so it is never a frame behind), and only exists while the menu is open. local CURSOR_BINDING = 'ZkxVaderCursor' local cursorFrame, cursorActive, oldMouseIcon = nil, false, true local function buildCursor() if cursorFrame and cursorFrame.Parent then return cursorFrame end -- siblings are layered by ZIndex (the gui uses Sibling behaviour): outlines first, white bars on top cursorFrame = New('Frame', { Name = 'Cursor', BackgroundTransparency = 1, AnchorPoint = vec2(0.5, 0.5), Size = UDim2.fromOffset(11, 11), Visible = false, ZIndex = 11000, Active = false, Parent = Library.ScreenGui, }) local function bar(size, color, zindex) New('Frame', { AnchorPoint = vec2(0.5, 0.5), Position = dim2(0.5, 0, 0.5, 0), Size = size, BackgroundColor3 = color, ZIndex = zindex, Active = false, Parent = cursorFrame }) end local dark, white = rgb(0, 0, 0), rgb(255, 255, 255) bar(UDim2.fromOffset(11, 3), dark, 1) bar(UDim2.fromOffset(3, 11), dark, 1) bar(UDim2.fromOffset(9, 1), white, 2) bar(UDim2.fromOffset(1, 9), white, 2) return cursorFrame end local function stopCursor() if not cursorActive then return end cursorActive = false pcall(function() RunService:UnbindFromRenderStep(CURSOR_BINDING) end) pcall(function() UserInputService.MouseIconEnabled = oldMouseIcon end) if cursorFrame then cursorFrame.Visible = false end end local function startCursor() if cursorActive or not Library.ScreenGui then return end if UserInputService.TouchEnabled and not UserInputService.MouseEnabled then return end -- phones: there is no pointer cursorActive = true oldMouseIcon = UserInputService.MouseIconEnabled local cursor = buildCursor() pcall(function() RunService:UnbindFromRenderStep(CURSOR_BINDING) end) RunService:BindToRenderStep(CURSOR_BINDING, Enum.RenderPriority.Last.Value, function() if Library.Unloaded or not Library.Toggled or not (Library.ScreenGui and Library.ScreenGui.Parent) then stopCursor() return end -- Library.ShowCustomCursor can be flipped while the menu is open (UI Settings > Custom Cursor) local show = Library.ShowCustomCursor ~= false UserInputService.MouseIconEnabled = not show cursor.Visible = show if show then local mouse = UserInputService:GetMouseLocation() cursor.Position = UDim2.fromOffset(mouse.X, mouse.Y) end end) end Library.StartCursor, Library.StopCursor = startCursor, stopCursor function Library:Toggle(value) local window = Library.Window if not window or Library.Unloaded then return end local target if typeof(value) == 'boolean' then target = value else target = not Library.Toggled end if target == Library.Toggled then return end Library.Toggled = target local root, curtain = window.Root, window.Curtain window.OpenToken = (window.OpenToken or 0) + 1 local token = window.OpenToken if target then local home = window.HomePosition or root.Position window.HomePosition = nil root.Visible = true if Animating() then root.Position = UDim2.new(home.X.Scale, home.X.Offset, home.Y.Scale, home.Y.Offset + 12) curtain.BackgroundTransparency = 0 Tween(root, { Position = home }, 0.26, Enum.EasingStyle.Quint) Tween(curtain, { BackgroundTransparency = 1 }, 0.28, Enum.EasingStyle.Quad) else root.Position, curtain.BackgroundTransparency = home, 1 end if not window.Introduced then window.Introduced = true FX.Scramble(window.TitleLabel, window.TitleText, 0.55) end else Library:ClosePopup() local home = root.Position if Animating() then window.HomePosition = home Tween(root, { Position = UDim2.new(home.X.Scale, home.X.Offset, home.Y.Scale, home.Y.Offset + 10) }, 0.16, Enum.EasingStyle.Quad) Tween(curtain, { BackgroundTransparency = 0 }, 0.16, Enum.EasingStyle.Quad) task.delay(0.17, function() if window.OpenToken == token and not Library.Toggled then root.Visible = false root.Position = home window.HomePosition = nil end end) else root.Visible = false end end if target then startCursor() else stopCursor() end if Library.OnToggled then pcall(Library.OnToggled, target) end end -- the screen gui, the scaled main frame and the popup overlay; made on demand (Notify may run before any window exists) function Library:EnsureGui() if Library.ScreenGui and Library.ScreenGui.Parent then return Library.ScreenGui end local gui = New('ScreenGui', { Name = 'ZkxVader', ResetOnSpawn = false, ZIndexBehavior = Enum.ZIndexBehavior.Sibling, IgnoreGuiInset = true, DisplayOrder = 1000, }) gui.Parent = UIParent() Library.ScreenGui = gui local main = New('Frame', { Name = 'Main', BackgroundTransparency = 1, Size = UDim2.fromScale(1 / GetScale(), 1 / GetScale()), Parent = gui }) Library.Main = main table.insert(Scalers, New('UIScale', { Scale = GetScale(), Parent = main })) Library.Overlay = New('Frame', { Name = 'Overlay', BackgroundTransparency = 1, Size = dim2(1, 0, 1, 0), ZIndex = 100, Parent = main }) return gui end -- shrinks the window to the screen and brings it back inside after a screen / scale change (rotating a phone, a smaller monitor) function Library:FitWindow() local window, main = Library.Window, Library.Main if not (window and window.Root and main) or main.AbsoluteSize.X <= 0 then return end local root = window.Root local screen, scale = Library.Viewport(), max(GetScale(), 0.01) local size = root.AbsoluteSize / scale local width, height = min(size.X, screen.X - 8), min(size.Y, screen.Y - 8) if width ~= size.X or height ~= size.Y then root.Size = UDim2.fromOffset(width, height) end local at = (root.AbsolutePosition - main.AbsolutePosition) / scale local dx = clamp(at.X, 0, max(0, screen.X - width)) - at.X local dy = clamp(at.Y, 0, max(0, screen.Y - height)) - at.Y if abs(dx) > 0.5 or abs(dy) > 0.5 then local position = root.Position root.Position = UDim2.new(position.X.Scale, position.X.Offset + dx, position.Y.Scale, position.Y.Offset + dy) end end --// window \\-- function Library:CreateWindow(info) info = info or {} if Library.Window then return Library.Window end local self = setmetatable({ Tabs = {}, Library = Library, Info = info }, Window) Library.Window = self Library.CornerRadius = info.CornerRadius or Library.CornerRadius Library.NotifySide = info.NotifySide or Library.NotifySide if info.NotifySide then Library.NotifyPlacement = tostring(info.NotifySide):lower() == 'left' and 'Top Left' or 'Top Right' end if info.ShowCustomCursor ~= nil then Library.ShowCustomCursor = info.ShowCustomCursor end if info.Font then Library:SetFont(info.Font) end Library:EnsureGui() local main = Library.Main -- the window: black > gray > dark gray > dark gray > gray > translucent black, like upstream local size = info.Size or UDim2.fromOffset(920, 640) -- never bigger than the screen (on a phone the whole menu has to fit) local screen = Library.Viewport() size = UDim2.fromOffset(min(size.X.Offset, screen.X - 16), min(size.Y.Offset, screen.Y - 16)) local position = info.Position if info.Center ~= false and (info.Center == true or position == nil) then position = UDim2.new(0.5, -size.X.Offset / 2, 0.5, -size.Y.Offset / 2) end local root, body = Panel(main, { 'a', 'b', 'c', 'c', 'b', 'a' }, { Size = size, Position = position or UDim2.fromOffset(60, 60), InnerTransparency = 0.35 }) root.Name = 'Window' root.Visible = false Library:ApplyLayout('window', root, true) self.Root, self.Body = root, body -- image behind the content (ThemeManager "Background Image") self.BackgroundImage = New('ImageLabel', { BackgroundTransparency = 1, ImageTransparency = 0.8, Size = dim2(1, 0, 1, 0), ScaleType = Enum.ScaleType.Crop, Visible = false, ZIndex = 0, Parent = body, }) -- lets the mouse go free while the menu is open (first person games lock it) New('TextButton', { Text = '', Modal = true, BackgroundTransparency = 1, Size = dim2(0, 0, 0, 0), Parent = root }) -- header: logo, shimmering title, footer on the right local header = New('Frame', { Name = 'Header', BackgroundTransparency = 1, Position = dim2(0, 14, 0, 8), Size = dim2(1, -28, 0, H(24)), Active = true, Parent = body }) local textLeft = 0 if info.Icon then self.IconLabel = New('ImageLabel', { BackgroundTransparency = 1, Image = tostring(info.Icon), Size = info.IconSize or UDim2.fromOffset(22, 22), Position = dim2(0, 0, 0.5, 0), AnchorPoint = vec2(0, 0.5), Parent = header, }) textLeft = (info.IconSize and info.IconSize.X.Offset or 22) + 8 end self.TitleHolder = header self.TitleText = tostring(info.Title or 'ZkxVader') self.TitleLabel = New('TextLabel', { Text = self.TitleText, BackgroundTransparency = 1, TextXAlignment = Enum.TextXAlignment.Left, AutomaticSize = Enum.AutomaticSize.X, Size = dim2(0, 0, 1, 0), Position = dim2(0, textLeft, 0, 0), TextSize = 14, ZIndex = 2, Parent = header, }) FX.Shimmer(self.TitleLabel, { every = 6 }) self.FooterLabel = New('TextLabel', { Text = tostring(info.Footer or ''), BackgroundTransparency = 1, TextXAlignment = Enum.TextXAlignment.Right, Size = dim2(0.5, 0, 1, 0), Position = dim2(0.5, 0, 0, 0), TextSize = Library.TextSize, ZIndex = 2, Parent = header, }, { TextColor3 = 'dim' }) if size.X.Offset < 640 then self.FooterLabel.Visible = false end MakeDraggable(root, header, nil, 'window', true) MakeDraggable(root, root, nil, 'window', true) -- tab strip: scrolls sideways when the tabs do not fit, accent underline glides under the active tab local bar = New('Frame', { Name = 'TabBar', BackgroundTransparency = 1, Position = dim2(0, 14, 0, 14 + H(24)), Size = dim2(1, -28, 0, H(28)), ClipsDescendants = true, Parent = body }) New('Frame', { Name = 'Baseline', AnchorPoint = vec2(0, 1), Position = dim2(0, 0, 1, 0), Size = dim2(1, 0, 0, 2), Parent = bar }, { BackgroundColor3 = 'f' }) local strip = New('ScrollingFrame', { Name = 'Strip', BackgroundTransparency = 1, Size = dim2(1, 0, 1, 0), ScrollBarThickness = 0, ScrollingDirection = Enum.ScrollingDirection.X, AutomaticCanvasSize = Enum.AutomaticSize.X, CanvasSize = dim2(0, 0, 0, 0), Parent = bar, }) New('UIListLayout', { FillDirection = Enum.FillDirection.Horizontal, Padding = dim(0, 2), SortOrder = Enum.SortOrder.LayoutOrder, Parent = strip }) local underline = New('Frame', { Name = 'Underline', AnchorPoint = vec2(0, 1), Position = dim2(0, 0, 1, 0), Size = dim2(0, 0, 0, 3), ZIndex = 3, Parent = bar }, { BackgroundColor3 = 'accent' }) New('Frame', { Size = dim2(1, 0, 0, 1), BackgroundColor3 = rgb(255, 255, 255), BackgroundTransparency = 0.7, ZIndex = 4, Parent = underline }) self.TabBar, self.Strip, self.Underline = bar, strip, underline -- wheel scrolls the strip sideways Connect(bar.InputChanged, function(input) if input.UserInputType == Enum.UserInputType.MouseWheel then strip.CanvasPosition = Vector2.new(max(0, strip.CanvasPosition.X - input.Position.Z * 60), 0) end end) -- page area local holder, pages = Panel(body, { 'a', 'c', 'e' }, { Position = dim2(0, 14, 0, 18 + H(24) + H(28)), Size = dim2(1, -28, 1, -(32 + H(24) + H(28))), Clip = true }) holder.Name = 'Pages' self.PageHolder, self.Pages = holder, pages -- the window curtain: fades the whole window in and out (cheaper than a CanvasGroup) self.Curtain = New('Frame', { Name = 'Curtain', BackgroundTransparency = 1, Size = dim2(1, 0, 1, 0), ZIndex = 60, Active = false, Parent = body }, { BackgroundColor3 = 'a' }) -- resize grip local grip = New('TextButton', { Text = '', BackgroundTransparency = 1, AnchorPoint = vec2(1, 1), Position = dim2(1, -2, 1, -2), Size = dim2(0, H(14), 0, H(14)), ZIndex = 70, Parent = body }) for index = 1, 3 do New('Frame', { AnchorPoint = vec2(1, 1), Position = dim2(1, -2, 1, -2 - (index - 1) * 3), Size = dim2(0, index * 3, 0, 1), BackgroundTransparency = 0.4, ZIndex = 71, Parent = grip }, { BackgroundColor3 = 'f' }) end MakeResizable(root, grip, Library.IsMobile and Vector2.new(320, 200) or Vector2.new(560, 380), 'window') -- keybind list (a draggable panel; Library.KeybindFrame.Visible / .Position are what SaveManager saves) local keybindFrame, keybindInner = Panel(main, { 'a', 'b', 'e' }, { Position = dim2(0, 14, 0.45, 0), AutoXY = true }) keybindFrame.Name = 'Keybinds' keybindFrame.Visible = false keybindFrame.ZIndex = 40 Library:ApplyLayout('keybinds', keybindFrame) -- Size is the minimum while AutomaticSize is on: the panel is at least 150 wide and grows with its longest row local keybindTitle = New('TextLabel', { Text = 'Keybinds', BackgroundTransparency = 1, Size = dim2(0, 150, 0, 18), AutomaticSize = Enum.AutomaticSize.X, TextSize = Library.TextSize, Active = true, Parent = keybindInner, }, { TextColor3 = 'accent' }) New('UIListLayout', { SortOrder = Enum.SortOrder.LayoutOrder, Parent = keybindInner }) local keybindList = New('Frame', { BackgroundTransparency = 1, Size = dim2(0, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.XY, LayoutOrder = 2, Parent = keybindInner }) New('UIListLayout', { SortOrder = Enum.SortOrder.LayoutOrder, Parent = keybindList }) New('UIPadding', { PaddingLeft = dim(0, 6), PaddingRight = dim(0, 6), PaddingBottom = dim(0, 4), Parent = keybindList }) MakeDraggable(keybindFrame, keybindTitle, nil, 'keybinds') Library.KeybindFrame, Library.KeybindContainer = keybindFrame, keybindList -- menu key (Library.ToggleKeybind is Enum.KeyCode by default, the hub replaces it with its MenuKeybind key picker) Library.ToggleKeybind = info.ToggleKeybind or Enum.KeyCode.RightControl Library:OnInput('Began', function(input, processed) if processed or Library.Unloaded then return end local bind = Library.ToggleKeybind if typeof(bind) == 'EnumItem' then if input.KeyCode == bind then Library:Toggle() end elseif typeof(bind) == 'table' and bind.Type == 'KeyPicker' then -- the key picker itself calls Library:Toggle when it fires (see the key picker); nothing to do here end end) if info.DisableSearch ~= true and self.BuildSearch then self:BuildSearch() end if self.BuildMobileButton then self:BuildMobileButton() end task.defer(function() Library:FitWindow() end) local camera = workspace.CurrentCamera if camera then Connect(camera:GetPropertyChangedSignal('ViewportSize'), function() Library:FitWindow() end) end if info.Watermark ~= false and Library.AddWatermark then Library:AddWatermark({ Name = self.TitleText }) end self:WatchResize() task.defer(function() if Library.Unloaded then return end if info.AutoShow ~= false and info.AutoShow ~= nil then Library:Toggle(true) end end) return self end --// tabs \\-- function Window:SetAnimations(map, time, offset) Library.Animations.Enabled = true if type(map) == 'table' then local any = false for _, enabled in pairs(map) do any = any or enabled == true end Library.Animations.Enabled = any end if tonumber(time) then Library.Animations.Time = clamp(tonumber(time), 0.08, 0.6) end end local function placeUnderline(window, tab, instant) local button, bar, underline = tab.Button, window.TabBar, window.Underline local scale = GetScale() local x = (button.AbsolutePosition.X - bar.AbsolutePosition.X) / scale local width = button.AbsoluteSize.X / scale local target = { Position = dim2(0, x, 1, 0), Size = dim2(0, width, 0, 3) } if instant or not Animating() or underline.Size.X.Offset == 0 then underline.Position, underline.Size = target.Position, target.Size else Tween(underline, target, 0.24, Enum.EasingStyle.Quint) end end Window.PlaceUnderline = placeUnderline function Window:AddTab(name, icon, description) name = tostring(name) local tab = setmetatable({ Window = self, Library = Library, Name = name, Groupboxes = {}, Tabboxes = {}, Description = description }, Tab) tab.Button = New('TextButton', { Text = name, AutoButtonColor = false, BackgroundTransparency = 1, AutomaticSize = Enum.AutomaticSize.X, Size = dim2(0, 0, 1, -2), LayoutOrder = #self.Tabs + 1, TextSize = Library.TextSize, Parent = self.Strip, }, { TextColor3 = 'dim' }) New('UIPadding', { PaddingLeft = dim(0, 12), PaddingRight = dim(0, 12), Parent = tab.Button }) Connect(tab.Button:GetPropertyChangedSignal('AbsoluteSize'), function() if self.ActiveTab == tab then placeUnderline(self, tab, true) end end) Connect(tab.Button.MouseEnter, function() if self.ActiveTab ~= tab then Tween(tab.Button, { TextColor3 = Palette.text }, 0.12) end end) Connect(tab.Button.MouseLeave, function() if self.ActiveTab ~= tab then Tween(tab.Button, { TextColor3 = Palette.dim }, 0.12) end end) tab.Page = New('Frame', { Name = name, BackgroundTransparency = 1, Size = dim2(1, 0, 1, 0), Visible = false, Parent = self.Pages }) local function column(side) local scroller = New('ScrollingFrame', { Name = side, BackgroundTransparency = 1, ScrollBarThickness = 2, ScrollBarImageTransparency = 0.2, AutomaticCanvasSize = Enum.AutomaticSize.Y, CanvasSize = dim2(0, 0, 0, 0), ScrollingDirection = Enum.ScrollingDirection.Y, Active = true, Position = side == 'Left' and dim2(0, 12, 0, 12) or dim2(0.5, 6, 0, 12), Size = dim2(0.5, -18, 1, -24), Parent = tab.Page, }) Library.Theme(scroller, 'ScrollBarImageColor3', 'accent') New('UIListLayout', { Padding = dim(0, 12), SortOrder = Enum.SortOrder.LayoutOrder, Parent = scroller }) New('UIPadding', { PaddingTop = dim(0, 10), PaddingRight = dim(0, 6), PaddingBottom = dim(0, 4), Parent = scroller }) -- intro curtain (fakes a fade of the column from the page colour) local curtain = New('Frame', { BackgroundTransparency = 1, Position = scroller.Position, Size = scroller.Size, ZIndex = 20, Active = false, Parent = tab.Page }, { BackgroundColor3 = 'e' }) return scroller, curtain end tab.Left, tab.LeftCurtain = column('Left') tab.Right, tab.RightCurtain = column('Right') tab.Sides = { tab.Left, tab.Right } -- the ESP preview freezes the columns' scrolling while its model is orbited Connect(tab.Button.MouseButton1Click, function() tab:Show() end) table.insert(self.Tabs, tab) Library.Tabs[name] = tab task.defer(function() if self.ActiveTab == tab then placeUnderline(self, tab, true) end end) if not self.ActiveTab then tab:Show(true) end return tab end function Tab:Show(instant) local window = self.Window local previous = window.ActiveTab if previous == self and not instant then return end Library:ClosePopup() if previous and previous ~= self then previous.Page.Visible = false ThemeSet(previous.Button, 'TextColor3', 'dim') end self.Page.Visible = true window.ActiveTab = self ThemeSet(self.Button, 'TextColor3', 'text') task.defer(function() placeUnderline(window, self, instant == true) end) -- keep the active tab inside the visible part of the strip task.defer(function() local strip, button = window.Strip, self.Button local left = button.AbsolutePosition.X - strip.AbsolutePosition.X + strip.CanvasPosition.X local right = left + button.AbsoluteSize.X if left < strip.CanvasPosition.X then strip.CanvasPosition = Vector2.new(max(0, left - 20), 0) elseif right > strip.CanvasPosition.X + strip.AbsoluteSize.X then strip.CanvasPosition = Vector2.new(right - strip.AbsoluteSize.X + 20, 0) end end) if instant ~= true and Animating() then -- columns slide up 14px while a page-coloured curtain fades off; the right column lags by 60 ms for index, pair in ipairs({ { self.Left, self.LeftCurtain }, { self.Right, self.RightCurtain } }) do local scroller, curtain = pair[1], pair[2] local home = scroller.Position curtain.BackgroundTransparency = 0 scroller.Position = UDim2.new(home.X.Scale, home.X.Offset, home.Y.Scale, home.Y.Offset + 14) task.delay((index - 1) * 0.06, function() if not scroller.Parent then return end Tween(scroller, { Position = home }, 0.3, Enum.EasingStyle.Quint) Tween(curtain, { BackgroundTransparency = 1 }, 0.3, Enum.EasingStyle.Quad) end) end FX.Scramble(self.Button, self.Name, 0.3) else self.LeftCurtain.BackgroundTransparency, self.RightCurtain.BackgroundTransparency = 1, 1 end if Library.OnTabChanged then pcall(Library.OnTabChanged, self) end end -- tabs created later than the window's first layout need their underline re-aimed when the strip resizes local lastSizeSignal = nil function Window:WatchResize() if lastSizeSignal then return end lastSizeSignal = Connect(self.Root:GetPropertyChangedSignal('AbsoluteSize'), function() local active = self.ActiveTab if active then placeUnderline(self, active, true) end end) end --// unload \\-- function Library:Unload() if Library.Unloaded then return end stopCursor() Library.Unloaded = true for index = #Library.Signals, 1, -1 do local signal = table.remove(Library.Signals, index) pcall(function() if typeof(signal) == 'RBXScriptConnection' and signal.Connected then signal:Disconnect() end end) end for _, callback in ipairs(Library.UnloadSignals) do Library:SafeCallback(callback) end if Library.ScreenGui then pcall(function() Library.ScreenGui:Destroy() end) Library.ScreenGui = nil end if Library.ActiveLoading then pcall(function() Library.ActiveLoading:Destroy() end) Library.ActiveLoading = nil end Library.Window = nil pcall(function() local env = (type(getgenv) == 'function' and getgenv()) or _G if env.Library == Library then env.Library = nil end end) end end -- ==== 30_container.lua ==== -- ZkxVader containers: groupbox (collapsible), tabbox, dependency box. Every container shares the Add* mixin (Classes.Container), which the -- widget files extend. A container owns a `Container` frame (vertical list) that its element rows are parented to. do local Groupbox, Tabbox, Container, Tab = Classes.Groupbox, Classes.Tabbox, Classes.Container, Classes.Tab for _, class in ipairs({ Groupbox, Tabbox, Classes.Dialog }) do setmetatable(class, { __index = Container }) end function Container:Resize() end -- layouts are automatic; kept for Obsidian API compatibility function Container:NextOrder() self.Order = (self.Order or 0) + 1 return self.Order end --// Reveal: a clipped wrapper whose height eases between 0 and its content (dependency boxes, collapsing groupboxes) \\-- local function Reveal(parent, layoutOrder, padding) local wrapper = New('Frame', { BackgroundTransparency = 1, ClipsDescendants = true, Size = dim2(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, LayoutOrder = layoutOrder or 0, Parent = parent }) local content = New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, Parent = wrapper }) New('UIListLayout', { Padding = dim(0, 6), SortOrder = Enum.SortOrder.LayoutOrder, Parent = content }) if padding then New('UIPadding', { PaddingTop = dim(0, padding.Top or 0), PaddingBottom = dim(0, padding.Bottom or 0), PaddingLeft = dim(0, padding.Left or 0), PaddingRight = dim(0, padding.Right or 0), Parent = content }) end local reveal = { Wrapper = wrapper, Content = content, Open = true } local token = 0 function reveal.Set(open, instant) if reveal.Open == open and not instant then return end reveal.Open = open token += 1 local mine = token if instant or not Animating() then wrapper.Visible = open wrapper.AutomaticSize = open and Enum.AutomaticSize.Y or Enum.AutomaticSize.None wrapper.Size = open and dim2(1, 0, 0, 0) or dim2(1, 0, 0, 0) return end if open then wrapper.AutomaticSize = Enum.AutomaticSize.None wrapper.Size = dim2(1, 0, 0, 0) wrapper.Visible = true task.defer(function() if token ~= mine then return end RunService.Heartbeat:Wait() -- one frame so the content has been laid out if token ~= mine then return end local height = content.AbsoluteSize.Y / max(Library.GetScale(), 0.01) Tween(wrapper, { Size = dim2(1, 0, 0, height) }, 0.2, Enum.EasingStyle.Quint) task.delay(0.22, function() if token == mine and reveal.Open then wrapper.AutomaticSize = Enum.AutomaticSize.Y -- hand the height back to the layout end end) end) else local height = wrapper.AbsoluteSize.Y / max(Library.GetScale(), 0.01) wrapper.AutomaticSize = Enum.AutomaticSize.None wrapper.Size = dim2(1, 0, 0, height) Tween(wrapper, { Size = dim2(1, 0, 0, 0) }, 0.18, Enum.EasingStyle.Quint) task.delay(0.2, function() if token == mine and not reveal.Open then wrapper.Visible = false end end) end end return reveal end Library.Reveal = Reveal --// groupbox: the Vaderhaxx section (title notched into the border), collapsible by clicking the title \\-- local function newGroupbox(tab, side, name) local column = side == 'Left' and tab.Left or tab.Right local outer, inner = Panel(column, { 'a', 'c', 'e' }, { Size = dim2(1, 0, 0, 0), Auto = true }) outer.LayoutOrder = #column:GetChildren() local text = tostring(name) -- the title and the collapse glyph both sit in the border: a page-coloured strip behind each one hides the two border lines, so neither touches the outline local notch = New('Frame', { Size = dim2(0, TextWidth(text, Library.TextSize) + 12, 0, 3), Position = dim2(0, 5, 0, -2), ZIndex = 4, Parent = inner }, { BackgroundColor3 = 'e' }) local title = New('TextLabel', { Text = text, BackgroundTransparency = 1, Size = dim2(0, TextWidth(text, Library.TextSize) + 12, 0, 16), Position = dim2(0, 5, 0, -8), TextSize = Library.TextSize, TextXAlignment = Enum.TextXAlignment.Left, ZIndex = 5, Parent = inner, }) New('UIPadding', { PaddingLeft = dim(0, 6), Parent = title }) New('Frame', { Size = dim2(0, 24, 0, 3), AnchorPoint = vec2(1, 0), Position = dim2(1, -8, 0, -2), ZIndex = 4, Parent = inner }, { BackgroundColor3 = 'e' }) local arrow = New('TextLabel', { Text = '-', BackgroundTransparency = 1, AnchorPoint = vec2(1, 0), Position = dim2(1, -8, 0, -8), Size = dim2(0, 24, 0, 16), TextSize = Library.TextSize, ZIndex = 5, Parent = inner }, { TextColor3 = 'dim' }) -- the whole header strip is the click target (title, glyph and the border between them), not just the title text local header = New('TextButton', { Text = '', AutoButtonColor = false, BackgroundTransparency = 1, Size = dim2(1, 0, 0, H(18)), Position = dim2(0, 0, 0, -8), ZIndex = 6, Parent = inner }) table.insert(Library.FontListeners, function() local width = TextWidth(text, Library.TextSize) + 12 notch.Size, title.Size = dim2(0, width, 0, 3), dim2(0, width, 0, 16) end) local reveal = Reveal(inner, 1, { Top = Library.IsMobile and 20 or 12, Bottom = 8, Left = 8, Right = 8 }) local box = setmetatable({ Tab = tab, Library = Library, Name = name, Side = side, Holder = outer, Container = reveal.Content, Elements = {}, Collapsed = false, Type = 'Groupbox', Reveal = reveal, }, Groupbox) box.Root = box function box:SetCollapsed(collapsed) collapsed = collapsed == true if box.Collapsed == collapsed then return end box.Collapsed = collapsed Library:ClosePopup() arrow.Text = collapsed and '+' or '-' reveal.Set(not collapsed) end function box:SetVisible(visible) outer.Visible = visible end Connect(header.MouseButton1Click, function() box:SetCollapsed(not box.Collapsed) end) Connect(header.MouseEnter, function() Tween(title, { TextColor3 = Palette.accent }, 0.12) Tween(arrow, { TextColor3 = Palette.accent }, 0.12) end) Connect(header.MouseLeave, function() Tween(title, { TextColor3 = Palette.text }, 0.12) Tween(arrow, { TextColor3 = Palette.dim }, 0.12) end) tab.Groupboxes[name] = box return box end function Tab:AddLeftGroupbox(name) return newGroupbox(self, 'Left', name) end function Tab:AddRightGroupbox(name) return newGroupbox(self, 'Right', name) end function Tab:AddGroupbox(info) info = info or {} return newGroupbox(self, info.Side == 2 and 'Right' or 'Left', info.Name or 'Groupbox') end --// tabbox: one panel with sub tabs \\-- local function newTabbox(tab, side, name) local column = side == 'Left' and tab.Left or tab.Right local outer, inner = Panel(column, { 'a', 'c', 'e' }, { Size = dim2(1, 0, 0, 0), Auto = true }) outer.LayoutOrder = #column:GetChildren() New('UIListLayout', { SortOrder = Enum.SortOrder.LayoutOrder, Parent = inner }) local strip = New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, H(22)), LayoutOrder = 0, Parent = inner }) New('UIListLayout', { FillDirection = Enum.FillDirection.Horizontal, SortOrder = Enum.SortOrder.LayoutOrder, Parent = strip }) local body = New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, LayoutOrder = 1, Parent = inner }) local box = setmetatable({ Tab = tab, Library = Library, Name = name, Side = side, Holder = outer, Strip = strip, Body = body, Tabs = {}, Type = 'Tabbox' }, Tabbox) function box:SetVisible(visible) outer.Visible = visible end tab.Tabboxes[name or (#tab.Tabboxes + 1)] = box return box end function Tab:AddLeftTabbox(name) return newTabbox(self, 'Left', name) end function Tab:AddRightTabbox(name) return newTabbox(self, 'Right', name) end function Tabbox:AddTab(name) local parentBox = self local page = New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, Visible = false, Parent = self.Body }) New('UIListLayout', { Padding = dim(0, 6), SortOrder = Enum.SortOrder.LayoutOrder, Parent = page }) New('UIPadding', { PaddingTop = dim(0, 8), PaddingBottom = dim(0, 8), PaddingLeft = dim(0, 8), PaddingRight = dim(0, 8), Parent = page }) local button = New('TextButton', { Text = tostring(name), AutoButtonColor = false, BackgroundTransparency = 1, Size = dim2(0, 0, 1, 0), AutomaticSize = Enum.AutomaticSize.X, TextSize = Library.TextSize, LayoutOrder = #self.Tabs + 1, Parent = self.Strip, }, { TextColor3 = 'dim' }) New('UIPadding', { PaddingLeft = dim(0, 10), PaddingRight = dim(0, 10), Parent = button }) local bar = New('Frame', { AnchorPoint = vec2(0, 1), Position = dim2(0, 0, 1, 0), Size = dim2(1, 0, 0, 2), Visible = false, ZIndex = 3, Parent = button }, { BackgroundColor3 = 'accent' }) local sub = setmetatable({ Tab = self.Tab, Library = Library, Name = name, Container = page, Elements = {}, Page = page, Button = button, Bar = bar, Type = 'TabboxTab', Tabbox = parentBox }, Container) sub.Root = sub function sub:Show() local previous = parentBox.Active if previous == sub then return end if previous then previous.Page.Visible = false previous.Bar.Visible = false ThemeSet(previous.Button, 'TextColor3', 'dim') end parentBox.Active = sub page.Visible = true bar.Visible = true ThemeSet(button, 'TextColor3', 'text') Library:ClosePopup() end function sub:SetVisible(visible) button.Visible = visible end Connect(button.MouseButton1Click, function() sub:Show() end) table.insert(self.Tabs, sub) if not self.Active then sub:Show() end return sub end --// dependency box: its children show only while every condition holds \\-- function Container:AddDependencyBox() local reveal = Reveal(self.Container, self:NextOrder()) local box = setmetatable({ Tab = self.Tab, Library = Library, Container = reveal.Content, Holder = reveal.Wrapper, Elements = {}, Dependencies = {}, Type = 'DependencyBox', Reveal = reveal, }, Container) reveal.Set(false, true) function box:SetupDependencies(dependencies) box.Dependencies = dependencies or {} box:Update(true) end function box:Update(instant) local show = #box.Dependencies > 0 for _, dependency in ipairs(box.Dependencies) do local object, wanted = dependency[1], dependency[2] if object == nil then show = false break end local value = object.Value local matches if object.Type == 'Dropdown' and object.Multi then matches = type(value) == 'table' and value[wanted] == true else matches = value == wanted end if not matches then show = false break end end reveal.Set(show, instant) end box.Root, box.Name = self.Root or self, (self.Root or self).Name table.insert(self.Elements, box) table.insert(Library.DependencyBoxes, box) return box end function Library:UpdateDependencyBoxes() for index = #Library.DependencyBoxes, 1, -1 do local box = Library.DependencyBoxes[index] if box.Holder and box.Holder.Parent then box:Update(false) else table.remove(Library.DependencyBoxes, index) end end end end -- ==== 40_elements.lua ==== -- ZkxVader elements (1/2): tooltip, label, divider, toggle, button (+sub buttons), slider, input. -- Every element keeps Obsidian's object contract: Type, Value, SetValue, OnChanged/Changed, Callback, RunChanged, SetText, SetVisible, SetDisabled, Addons. do local Container = Classes.Container local Addons = {} Classes.Addons = Addons local function noop() end local Templates = { Toggle = { Text = 'Toggle', Default = false, Callback = noop, Changed = noop, Risky = false, Disabled = false, Visible = true }, Input = { Text = 'Input', Default = '', Finished = false, Numeric = false, ClearTextOnFocus = true, ClearTextOnBlur = false, Placeholder = '', AllowEmpty = true, EmptyReset = '---', Callback = noop, Changed = noop, Disabled = false, Visible = true, }, Slider = { Text = 'Slider', Default = 0, Min = 0, Max = 100, Rounding = 0, Prefix = '', Suffix = '', Callback = noop, Changed = noop, Disabled = false, Visible = true, AllowRightClickInput = true, }, Dropdown = { Values = {}, DisabledValues = {}, Multi = false, MaxVisibleDropdownItems = 8, Callback = noop, Changed = noop, Disabled = false, Visible = true }, KeyPicker = { Text = 'KeyPicker', Default = 'None', DefaultModifiers = {}, Blacklisted = {}, BlacklistedModifiers = {}, Whitelisted = {}, WhitelistedModifiers = {}, Mode = 'Toggle', Modes = { 'Always', 'Toggle', 'Hold' }, SyncToggleState = false, Callback = noop, ChangedCallback = noop, Changed = noop, Clicked = noop, }, ColorPicker = { Default = Color3.new(1, 1, 1), Callback = noop, Changed = noop }, } Library.Templates = Templates local function Validate(info, template) local out = {} for key, value in pairs(template) do out[key] = value end if type(info) == 'table' then for key, value in pairs(info) do out[key] = value end end return out end Library.Validate = function(_, info, template) return Validate(info, template) end --// tooltip: one shared panel, shown after a short hover \\-- local tooltipFrame, tooltipLabel local tooltipToken = 0 local followHandle, followTooltip local function showTooltip(text) if not tooltipFrame then local frame, inner = Panel(Library.Overlay, { 'a', 'b', 'e' }, { AutoXY = true }) tooltipFrame = frame tooltipFrame.ZIndex = 90 tooltipFrame.Visible = false tooltipFrame.Name = 'Tooltip' tooltipLabel = New('TextLabel', { Text = '', BackgroundTransparency = 1, AutomaticSize = Enum.AutomaticSize.XY, Size = dim2(0, 0, 0, 0), TextSize = 12, ZIndex = 91, TextWrapped = false, Parent = inner, }) New('UIPadding', { PaddingLeft = dim(0, 6), PaddingRight = dim(0, 6), PaddingTop = dim(0, 3), PaddingBottom = dim(0, 4), Parent = inner }) end tooltipLabel.Text = text tooltipFrame.Visible = true followHandle = followHandle or Library:OnInput('Render', function() followTooltip() end) end local function hideTooltip() tooltipToken += 1 if tooltipFrame then tooltipFrame.Visible = false end if followHandle then followHandle.Disconnect() followHandle = nil end end Library.HideTooltip = hideTooltip function followTooltip() if tooltipFrame and tooltipFrame.Visible then local mouse = UserInputService:GetMouseLocation() / Library.GetScale() local size = tooltipFrame.AbsoluteSize / Library.GetScale() local view = Library.Main.AbsoluteSize / Library.GetScale() tooltipFrame.Position = dim2(0, clamp(mouse.X + 14, 0, max(0, view.X - size.X - 4)), 0, clamp(mouse.Y + 16, 0, max(0, view.Y - size.Y - 4))) end end function Library:AddTooltip(info, disabledInfo, hover) local tooltip = { Disabled = false, Info = info, DisabledInfo = disabledInfo } if typeof(hover) ~= 'Instance' then return tooltip end local function text() return tooltip.Disabled and (tooltip.DisabledInfo or tooltip.Info) or tooltip.Info end Connect(hover.MouseEnter, function() local current = text() if type(current) ~= 'string' or current == '' then return end tooltipToken += 1 local mine = tooltipToken task.delay(0.45, function() if mine == tooltipToken and not Library.Popup and Library.Toggled and hover.Parent and IsMouseOver(hover) then showTooltip(current) end end) end) Connect(hover.MouseLeave, hideTooltip) function tooltip:Destroy() end return tooltip end local function attachTooltip(object, hover) if type(object.Tooltip) == 'string' or type(object.DisabledTooltip) == 'string' then object.TooltipTable = Library:AddTooltip(object.Tooltip, object.DisabledTooltip, hover) object.TooltipTable.Disabled = object.Disabled == true end end --// shared row pieces \\-- local function newRow(owner, height) return New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, height), LayoutOrder = owner:NextOrder(), Parent = owner.Container }) end local function newRightHolder(row) local holder = New('Frame', { BackgroundTransparency = 1, AnchorPoint = vec2(1, 0), Position = dim2(1, 0, 0, 0), Size = dim2(0, 0, 1, 0), AutomaticSize = Enum.AutomaticSize.X, ZIndex = 3, Parent = row }) New('UIListLayout', { FillDirection = Enum.FillDirection.Horizontal, HorizontalAlignment = Enum.HorizontalAlignment.Right, VerticalAlignment = Enum.VerticalAlignment.Center, Padding = dim(0, 5), SortOrder = Enum.SortOrder.LayoutOrder, Parent = holder }) return holder end local function register(kind, idx, object) object.Idx = idx if idx ~= nil then Library[kind][idx] = object end end local function runChanged(object) Library:SafeCallback(object.Callback, object.Value) Library:SafeCallback(object.Changed, object.Value) end local function destroyable(owner, object, row, registry, idx) function object:Destroy() object.Destroyed = true if row then row:Destroy() end for index = #(object.Addons or {}), 1, -1 do local addon = table.remove(object.Addons, index) if addon and addon.Destroy then addon:Destroy() end end if registry and idx ~= nil and Library[registry][idx] == object then Library[registry][idx] = nil end local at = table.find(owner.Elements, object) if at then table.remove(owner.Elements, at) end end end --// label \\-- function Container:AddLabel(...) local first, second = select(1, ...), select(2, ...) local data = {} if type(first) == 'table' or type(second) == 'table' then local params = type(first) == 'table' and first or second data.Text, data.DoesWrap, data.Size = params.Text or '', params.DoesWrap or params.Wrap or false, params.Size or Library.TextSize data.Visible = params.Visible ~= false data.Idx = type(second) == 'table' and first or nil else data.Text, data.DoesWrap, data.Size, data.Visible, data.Idx = first or '', second or false, Library.TextSize, true, select(3, ...) end local label = { Text = data.Text, DoesWrap = data.DoesWrap, Addons = {}, Visible = data.Visible, Type = 'Label', Library = Library } local row = New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, H(14)), AutomaticSize = data.DoesWrap and Enum.AutomaticSize.Y or Enum.AutomaticSize.None, LayoutOrder = self:NextOrder(), Visible = data.Visible, Parent = self.Container }) local text = New('TextLabel', { Text = data.Text, BackgroundTransparency = 1, Size = data.DoesWrap and dim2(1, 0, 0, 0) or dim2(1, 0, 1, 0), AutomaticSize = data.DoesWrap and Enum.AutomaticSize.Y or Enum.AutomaticSize.None, TextSize = data.Size, TextWrapped = data.DoesWrap, TextXAlignment = Enum.TextXAlignment.Left, TextYAlignment = Enum.TextYAlignment.Top, RichText = true, Parent = row, }) label.TextLabel, label.Holder, label.Container = text, row, self.Container label.RightHolder = (not data.DoesWrap) and newRightHolder(row) or nil function label:SetText(value) label.Text = tostring(value) text.Text = label.Text end function label:SetVisible(visible) label.Visible = visible row.Visible = visible end destroyable(self, label, row, 'Labels', data.Idx) if not data.DoesWrap then setmetatable(label, { __index = Addons }) end table.insert(self.Elements, label) if data.Idx ~= nil then Library.Labels[data.Idx] = label else table.insert(Library.Labels, label) end return label end --// divider \\-- function Container:AddDivider(...) local params = select(1, ...) local text, top, bottom = nil, 0, 0 if type(params) == 'table' then text, top, bottom = params.Text, params.MarginTop or params.Margin or 0, params.MarginBottom or params.Margin or 0 elseif type(params) == 'string' then text = params end local divider = { Type = 'Divider', Visible = true } local row = New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, 10 + top + bottom), LayoutOrder = self:NextOrder(), Parent = self.Container }) if text and text ~= '' then local width = TextWidth(text, 12) + 14 New('Frame', { AnchorPoint = vec2(0, 0.5), Position = dim2(0, 0, 0.5, (top - bottom) / 2), Size = dim2(0.5, -width / 2, 0, 1), Parent = row }, { BackgroundColor3 = 'b' }) New('Frame', { AnchorPoint = vec2(1, 0.5), Position = dim2(1, 0, 0.5, (top - bottom) / 2), Size = dim2(0.5, -width / 2, 0, 1), Parent = row }, { BackgroundColor3 = 'b' }) New('TextLabel', { Text = text, BackgroundTransparency = 1, AnchorPoint = vec2(0.5, 0.5), Position = dim2(0.5, 0, 0.5, (top - bottom) / 2), Size = dim2(0, width, 0, 12), TextSize = 12, Parent = row }, { TextColor3 = 'dim' }) else New('Frame', { AnchorPoint = vec2(0, 0.5), Position = dim2(0, 0, 0.5, (top - bottom) / 2), Size = dim2(1, 0, 0, 1), Parent = row }, { BackgroundColor3 = 'b' }) end function divider:SetVisible(visible) row.Visible = visible end function divider:Destroy() row:Destroy() end return divider end function Container:AddBlank(height) New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, height or 8), LayoutOrder = self:NextOrder(), Parent = self.Container }) end --// toggle (a Vaderhaxx checkbox) \\-- function Container:AddToggle(idx, info) info = Validate(info, Templates.Toggle) local toggle = { Text = info.Text, Value = info.Default == true, Tooltip = info.Tooltip, DisabledTooltip = info.DisabledTooltip, Callback = info.Callback, Changed = info.Changed, Risky = info.Risky, Disabled = info.Disabled, Visible = info.Visible, Addons = {}, AnyKeyPickerPicking = false, Variant = 'Checkbox', Type = 'Toggle', Library = Library, } local row = New('TextButton', { Text = '', AutoButtonColor = false, BackgroundTransparency = 1, Size = dim2(1, 0, 0, H(16)), LayoutOrder = self:NextOrder(), Visible = toggle.Visible, Active = not toggle.Disabled, Parent = self.Container }) local box, fill = Panel(row, { 'd', 'f' }, { Size = UDim2.fromOffset(H(11), H(11)), Position = dim2(0, 0, 0.5, 0), AnchorPoint = vec2(0, 0.5) }) local label = New('TextLabel', { Text = toggle.Text, BackgroundTransparency = 1, Position = dim2(0, 19, 0, 0), Size = dim2(1, -60, 1, 0), TextSize = Library.TextSize, TextXAlignment = Enum.TextXAlignment.Left, RichText = true, Parent = row }) local right = newRightHolder(row) toggle.Holder, toggle.TextLabel, toggle.Container, toggle.RightHolder = row, label, self.Container, right attachTooltip(toggle, row) local hovering = false local function labelKey() if toggle.Disabled then return 'dim' end if toggle.Risky and toggle.Value then return 'risky' end return (hovering and 'accent') or 'text' end function toggle:Display(animate) if Library.Unloaded then return end local fillKey = toggle.Value and (toggle.Risky and 'risky' or 'accent') or 'f' Library.Registry[fill] = Library.Registry[fill] or {} Library.Registry[fill].BackgroundColor3 = fillKey Library.Registry[label] = Library.Registry[label] or {} Library.Registry[label].TextColor3 = labelKey() if animate ~= false and Animating() then Tween(fill, { BackgroundColor3 = Palette[fillKey] }, 0.12) Tween(label, { TextColor3 = Palette[labelKey()] }, 0.1) if toggle.Value then box.Size = UDim2.fromOffset(H(13), H(13)) Tween(box, { Size = UDim2.fromOffset(H(11), H(11)) }, 0.18, Enum.EasingStyle.Back) end else fill.BackgroundColor3, label.TextColor3 = Palette[fillKey], Palette[labelKey()] end label.TextTransparency = toggle.Disabled and 0.5 or 0 box.BackgroundTransparency = toggle.Disabled and 0.6 or 0 end function toggle:OnChanged(callback) toggle.Changed = callback end function toggle:RunChanged() runChanged(toggle) end function toggle:SetValue(value) if toggle.Disabled then return end toggle.Value = value == true toggle:Display() for _, addon in ipairs(toggle.Addons) do if addon.Type == 'KeyPicker' and (addon.SyncToggleState or addon.Mode == 'Toggle') then addon.Toggled = toggle.Value addon:Update() end end Library:UpdateDependencyBoxes() if not toggle.AnyKeyPickerPicking then toggle:RunChanged() end if toggle.Risky and toggle.Value and Animating() then FX.Glitch(label, 0.18) end end function toggle:SetDisabled(disabled) toggle.Disabled = disabled row.Active = not disabled if toggle.TooltipTable then toggle.TooltipTable.Disabled = disabled end for _, addon in ipairs(toggle.Addons) do if addon.Type == 'KeyPicker' then addon:Update() end end toggle:Display(false) end function toggle:SetVisible(visible) toggle.Visible = visible row.Visible = visible for _, addon in ipairs(toggle.Addons) do if addon.Type == 'KeyPicker' then addon:Update() elseif addon.Refresh then addon:Refresh() end end end function toggle:SetText(text) toggle.Text = text label.Text = text end Connect(row.MouseButton1Click, function() if toggle.Disabled then return end toggle:SetValue(not toggle.Value) end) Connect(row.MouseEnter, function() hovering = true if not toggle.Disabled then Tween(label, { TextColor3 = Palette[labelKey()] }, 0.08) end end) Connect(row.MouseLeave, function() hovering = false Tween(label, { TextColor3 = Palette[labelKey()] }, 0.1) end) toggle:Display(false) toggle.Default = toggle.Value destroyable(self, toggle, row, 'Toggles', idx) setmetatable(toggle, { __index = Addons }) table.insert(self.Elements, toggle) register('Toggles', idx, toggle) return toggle end Container.AddCheckbox = Container.AddToggle --// button (+ sub buttons in the same row) \\-- local function buttonInfo(...) local first, second = select(1, ...), select(2, ...) local info = {} if type(first) == 'table' or type(second) == 'table' then local params = type(first) == 'table' and first or second info.Text, info.Func = params.Text or '', params.Func or params.Callback or noop info.DoubleClick, info.Tooltip, info.DisabledTooltip = params.DoubleClick, params.Tooltip, params.DisabledTooltip info.Risky, info.Disabled, info.Visible = params.Risky or false, params.Disabled or false, params.Visible ~= false info.Idx = type(second) == 'table' and first or nil else info.Text, info.Func, info.Idx = first or '', second or noop, select(3, ...) info.DoubleClick, info.Risky, info.Disabled, info.Visible = false, false, false, true end return info end local function makeButton(owner, holder, info, parentButton) local button = { Text = info.Text, Func = info.Func, DoubleClick = info.DoubleClick, Tooltip = info.Tooltip, DisabledTooltip = info.DisabledTooltip, Risky = info.Risky, Disabled = info.Disabled, Visible = info.Visible, Addons = {}, Type = parentButton and 'SubButton' or 'Button', Library = Library, } local outer, inner = Panel(holder, { 'a', 'c' }, { Size = dim2(1, 0, 1, 0) }) outer.LayoutOrder = #holder:GetChildren() local face = New('TextButton', { Text = button.Text, AutoButtonColor = false, BackgroundTransparency = 1, Size = dim2(1, 0, 1, 0), TextSize = Library.TextSize, RichText = true, Active = not button.Disabled, Parent = inner }) button.Base, button.Holder, button.TextLabel = face, outer, face local armed = false attachTooltip(button, face) local function baseKey() return button.Risky and 'risky' or 'text' end function button:UpdateColors() face.TextColor3 = button.Disabled and Palette.dim or Palette[baseKey()] face.TextTransparency = button.Disabled and 0.4 or 0 end function button:SetText(text) button.Text = text face.Text = text end function button:SetDisabled(disabled) button.Disabled = disabled face.Active = not disabled if button.TooltipTable then button.TooltipTable.Disabled = disabled end button:UpdateColors() end function button:SetVisible(visible) button.Visible = visible outer.Visible = visible end function button:Destroy() button.Destroyed = true outer:Destroy() end local function fire() if button.Disabled then return end Library:SafeCallback(button.Func) Tween(inner, { BackgroundColor3 = Palette.accent }, 0.04) task.delay(0.06, function() if inner.Parent then Tween(inner, { BackgroundColor3 = Palette.c }, 0.25) end end) end Connect(face.MouseButton1Click, function() if button.Disabled then return end if button.DoubleClick then if armed then armed = false face.Text = button.Text fire() else armed = true face.Text = 'click again' face.TextColor3 = Palette.accent task.delay(2, function() if armed and face.Parent then armed = false face.Text = button.Text button:UpdateColors() end end) end else fire() end end) Connect(face.MouseEnter, function() if not button.Disabled then Tween(inner, { BackgroundColor3 = Palette.g }, 0.1) end end) Connect(face.MouseLeave, function() Tween(inner, { BackgroundColor3 = Palette.c }, 0.15) end) button:UpdateColors() outer.Visible = button.Visible return button end function Container:AddButton(...) local info = buttonInfo(...) local row = New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, H(20)), LayoutOrder = self:NextOrder(), Parent = self.Container }) New('UIListLayout', { FillDirection = Enum.FillDirection.Horizontal, HorizontalFlex = Enum.UIFlexAlignment.Fill, Padding = dim(0, 6), SortOrder = Enum.SortOrder.LayoutOrder, Parent = row }) local button = makeButton(self, row, info, nil) button.Row, button.Container = row, self.Container function button:AddButton(...) local sub = makeButton(self, row, buttonInfo(...), button) table.insert(self.Elements or {}, sub) return sub end setmetatable(button, { __index = Addons }) table.insert(self.Elements, button) if info.Idx ~= nil then Library.Buttons[info.Idx] = button else table.insert(Library.Buttons, button) end local base = button.Destroy function button:Destroy() base(self) row:Destroy() end return button end --// slider \\-- function Container:AddSlider(idx, info) info = Validate(info, Templates.Slider) local slider = { Text = info.Text, Value = info.Default, Min = info.Min, Max = info.Max, Prefix = info.Prefix, Suffix = info.Suffix, Compact = info.Compact, Rounding = info.Rounding, HideMax = info.HideMax, Tooltip = info.Tooltip, DisabledTooltip = info.DisabledTooltip, Callback = info.Callback, Changed = info.Changed, Disabled = info.Disabled, Visible = info.Visible, AllowRightClickInput = info.AllowRightClickInput, Type = 'Slider', Library = Library, } local row = New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, info.Compact and H(20) or H(32)), LayoutOrder = self:NextOrder(), Visible = slider.Visible, Parent = self.Container }) local title = (not info.Compact) and New('TextLabel', { Text = slider.Text or '', BackgroundTransparency = 1, Size = dim2(0.6, 0, 0, 14), TextSize = Library.TextSize, TextXAlignment = Enum.TextXAlignment.Left, RichText = true, Parent = row }) or nil local readout = New('TextLabel', { Text = '', BackgroundTransparency = 1, AnchorPoint = vec2(1, 0), Position = dim2(1, 0, 0, 0), Size = dim2(0.5, 0, 0, 14), TextSize = Library.TextSize, TextXAlignment = Enum.TextXAlignment.Right, ZIndex = 3, Parent = row, }, { TextColor3 = 'dim' }) local barY, barHeight = info.Compact and 0 or H(17), info.Compact and H(20) or H(12) local bar = New('TextButton', { Text = '', AutoButtonColor = false, BackgroundTransparency = 1, Position = dim2(0, 0, 0, barY), Size = dim2(1, 0, 0, barHeight), Active = true, Parent = row }) local outline, track = Panel(bar, { 'd', 'b' }, { Size = dim2(1, 0, 1, 0) }) outline.Name = 'Track' local fillFrame = New('Frame', { Size = dim2(0, 0, 1, 0), ZIndex = 2, Parent = track }, { BackgroundColor3 = 'accent' }) if info.Compact then readout.AnchorPoint, readout.Position, readout.Size, readout.TextXAlignment = vec2(0.5, 0.5), dim2(0.5, 0, 0.5, 0), dim2(1, 0, 1, 0), Enum.TextXAlignment.Center readout.ZIndex = 5 Library.Registry[readout] = { TextColor3 = 'text' } readout.TextColor3 = Palette.text readout.Parent = bar end slider.Holder, slider.Bar, slider.Fill, slider.Container = row, bar, fillFrame, self.Container attachTooltip(slider, bar) local function formatNumber(value) if slider.Rounding and slider.Rounding > 0 then local text = string.format('%.' .. slider.Rounding .. 'f', value) return (text:gsub('(%..-)0+$', '%1'):gsub('%.$', '')) end return tostring(value) end function slider:Display() if Library.Unloaded then return end local custom = info.FormatDisplayValue and info.FormatDisplayValue(slider, slider.Value) if custom then readout.Text = tostring(custom) elseif info.Compact then readout.Text = string.format('%s: %s%s%s', slider.Text or '', slider.Prefix, formatNumber(slider.Value), slider.Suffix) elseif info.HideMax then readout.Text = string.format('%s%s%s', slider.Prefix, formatNumber(slider.Value), slider.Suffix) else readout.Text = string.format('%s%s%s/%s%s%s', slider.Prefix, formatNumber(slider.Value), slider.Suffix, slider.Prefix, formatNumber(slider.Max), slider.Suffix) end local alpha = slider.Max ~= slider.Min and clamp((slider.Value - slider.Min) / (slider.Max - slider.Min), 0, 1) or 0 if Animating() and not slider.Dragging and fillFrame.AbsoluteSize.X > 0 then Tween(fillFrame, { Size = dim2(alpha, 0, 1, 0) }, 0.08, Enum.EasingStyle.Quad) else fillFrame.Size = dim2(alpha, 0, 1, 0) end readout.TextTransparency, fillFrame.BackgroundTransparency = slider.Disabled and 0.5 or 0, slider.Disabled and 0.6 or 0 if title then title.TextTransparency = slider.Disabled and 0.5 or 0 end end function slider:OnChanged(callback) slider.Changed = callback end function slider:RunChanged() runChanged(slider) end function slider:SetValue(raw) if slider.Disabled then return end local number = tonumber(raw) if not number or number == slider.Value then return end slider.Value = clamp(number, slider.Min, slider.Max) slider:Display() slider:RunChanged() end function slider:SetMax(value) slider.Max = value slider:SetValue(clamp(slider.Value, slider.Min, value)) slider:Display() end function slider:SetMin(value) slider.Min = value slider:SetValue(clamp(slider.Value, value, slider.Max)) slider:Display() end function slider:SetPrefix(prefix) slider.Prefix = prefix slider:Display() end function slider:SetSuffix(suffix) slider.Suffix = suffix slider:Display() end function slider:SetText(text) slider.Text = text if title then title.Text = text end slider:Display() end function slider:SetDisabled(disabled) slider.Disabled = disabled if slider.TooltipTable then slider.TooltipTable.Disabled = disabled end slider:Display() end function slider:SetVisible(visible) slider.Visible = visible row.Visible = visible end -- dragging: the shared router only calls us while a drag is running, and the value is read once per frame local moveHandle, endHandle, renderHandle, moved local function fromMouse() local x = (MouseAbs().X - bar.AbsolutePosition.X) / max(bar.AbsoluteSize.X, 1) local value = slider.Min + (slider.Max - slider.Min) * clamp(x, 0, 1) value = Round(value, 10 ^ -(slider.Rounding or 0)) slider:SetValue(clamp(value, slider.Min, slider.Max)) end local function stopDrag() slider.Dragging = false for _, listener in ipairs({ moveHandle, endHandle, renderHandle }) do listener.Disconnect() end moveHandle, endHandle, renderHandle = nil, nil, nil slider:Display() end Connect(bar.InputBegan, function(input) if slider.Disabled or moveHandle then return end if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then slider.Dragging = true fromMouse() moveHandle = Library:OnInput('Changed', function(changed) if changed.UserInputType == Enum.UserInputType.MouseMovement or changed.UserInputType == Enum.UserInputType.Touch then moved = true end end) renderHandle = Library:OnInput('Render', function() if moved then moved = false fromMouse() end end) endHandle = Library:OnInput('Ended', function(ended) if ended.UserInputType == Enum.UserInputType.MouseButton1 or ended.UserInputType == Enum.UserInputType.Touch then stopDrag() end end) end end) -- right click: type a value if slider.AllowRightClickInput then local typing = New('TextBox', { Text = '', BackgroundTransparency = 1, Size = dim2(1, 0, 1, 0), ZIndex = 8, TextSize = Library.TextSize, ClearTextOnFocus = false, Visible = false, Parent = bar, }) Library.Registry[typing] = { TextColor3 = 'accent' } typing.TextColor3 = Palette.accent Connect(bar.MouseButton2Click, function() if slider.Disabled then return end typing.Visible, typing.Text = true, tostring(slider.Value) typing:CaptureFocus() end) Connect(typing.FocusLost, function() local number = tonumber(typing.Text) typing.Visible = false if number then if slider.Rounding == 0 then number = Round(number, 1) end slider:SetValue(clamp(number, slider.Min, slider.Max)) end end) end slider:Display() slider.Default = slider.Value destroyable(self, slider, row, 'Options', idx) table.insert(self.Elements, slider) register('Options', idx, slider) return slider end --// input \\-- function Container:AddInput(idx, info) info = Validate(info, Templates.Input) if type(info.VerifyValue) == 'function' and info.Finished ~= true then info.Finished = true end local input = { Text = info.Text, Value = info.Default, Finished = info.Finished, Numeric = info.Numeric, ClearTextOnFocus = info.ClearTextOnFocus, ClearTextOnBlur = info.ClearTextOnBlur, Placeholder = info.Placeholder, AllowEmpty = info.AllowEmpty, EmptyReset = info.EmptyReset, Tooltip = info.Tooltip, DisabledTooltip = info.DisabledTooltip, Callback = info.Callback, Changed = info.Changed, VerifyValue = info.VerifyValue, Disabled = info.Disabled, Visible = info.Visible, Type = 'Input', Library = Library, } local hasLabel = type(info.Text) == 'string' and info.Text ~= '' local row = New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, hasLabel and (H(20) + 16) or H(20)), LayoutOrder = self:NextOrder(), Visible = input.Visible, Parent = self.Container }) local title = New('TextLabel', { Text = input.Text or '', BackgroundTransparency = 1, Size = dim2(1, 0, 0, 14), TextSize = Library.TextSize, TextXAlignment = Enum.TextXAlignment.Left, RichText = true, Visible = hasLabel, Parent = row }) local frame = New('Frame', { BackgroundTransparency = 1, AnchorPoint = vec2(0, 1), Position = dim2(0, 0, 1, 0), Size = dim2(1, 0, 0, H(20)), Parent = row }) local _, field = Panel(frame, { 'd', 'b' }, { Size = dim2(1, 0, 1, 0) }) local box = New('TextBox', { Text = tostring(input.Value or ''), PlaceholderText = input.Placeholder, BackgroundTransparency = 1, Size = dim2(1, -8, 1, 0), Position = dim2(0, 5, 0, 0), TextSize = Library.TextSize, TextXAlignment = Enum.TextXAlignment.Left, ClearTextOnFocus = input.ClearTextOnFocus, TextEditable = not input.Disabled, Parent = field, }) box.PlaceholderColor3 = Palette.dim input.Holder, input.Box, input.Container = row, box, self.Container attachTooltip(input, frame) local internal = false local function accept(text) if input.Numeric and text ~= '' and not tonumber(text) and text ~= '-' and text ~= '.' then return false end return true end function input:OnChanged(callback) input.Changed = callback end function input:RunChanged() runChanged(input) end function input:SetValue(text) text = text == nil and '' or tostring(text) if input.Numeric and text ~= '' and not tonumber(text) then return end if type(input.VerifyValue) == 'function' and text ~= input.EmptyReset and input.VerifyValue(text) ~= true then return end input.Value = text internal = true box.Text = text internal = false input:RunChanged() end function input:SetDisabled(disabled) input.Disabled = disabled box.TextEditable = not disabled box.TextTransparency = disabled and 0.5 or 0 if input.TooltipTable then input.TooltipTable.Disabled = disabled end end function input:SetVisible(visible) input.Visible = visible row.Visible = visible end function input:SetText(text) input.Text = text title.Text = text or '' title.Visible = text ~= nil and text ~= '' end Connect(box:GetPropertyChangedSignal('Text'), function() if internal or input.Finished then return end if input.Numeric and not accept(box.Text) then internal = true box.Text = input.Value or '' internal = false return end input:SetValue(box.Text) end) Connect(box.FocusLost, function() if box.Text == '' and not input.AllowEmpty then input:SetValue(input.EmptyReset) elseif input.Finished then input:SetValue(box.Text) end if input.ClearTextOnBlur then internal = true box.Text = '' internal = false end Tween(field, { BackgroundColor3 = Palette.b }, 0.15) end) Connect(box.Focused, function() Tween(field, { BackgroundColor3 = Palette.g }, 0.1) end) input.Default = input.Value destroyable(self, input, row, 'Options', idx) table.insert(self.Elements, input) register('Options', idx, input) return input end end -- ==== 41_popups.lua ==== -- ZkxVader elements (2/2): dropdown, colour picker, key picker. Popups live on Library.Overlay (never clipped by a scrolling section). do local Container, Addons = Classes.Container, Classes.Addons local Templates, Validate = Library.Templates, function(info, template) return Library.Validate(Library, info, template) end --// popup plumbing: one open at a time, closed by an outside click, a tab change or the menu closing \\-- local function placePopup(frame, anchor, width, height, preferBelow) local scale = Library.GetScale() -- popups are positioned inside the overlay, whose own AbsolutePosition carries the top-bar inset local abs, size = (anchor.AbsolutePosition - Library.Overlay.AbsolutePosition) / scale, anchor.AbsoluteSize / scale local view = Library.Main.AbsoluteSize / scale local x = clamp(abs.X, 2, max(2, view.X - width - 2)) local y = abs.Y + size.Y + 2 if preferBelow == false or y + height > view.Y - 4 then local above = abs.Y - height - 2 if above >= 4 or y + height > view.Y - 4 then y = max(4, above) end end frame.Position = UDim2.fromOffset(x, y) frame.Size = UDim2.fromOffset(width, height) end local function popupPanel(width, height) local frame, inner = Panel(Library.Overlay, { 'a', 'b', 'e' }, { Size = UDim2.fromOffset(width, height), Clip = false }) frame.ZIndex = 100 frame.Active = true frame.Visible = false return frame, inner end Library:OnInput('Began', function(input) local kind = input.UserInputType if kind ~= Enum.UserInputType.MouseButton1 and kind ~= Enum.UserInputType.MouseButton2 and kind ~= Enum.UserInputType.Touch then return end local popup = Library.Popup if popup and popup.Frame and popup.Frame.Visible and not IsMouseOver(popup.Frame) and not (popup.Anchor and IsMouseOver(popup.Anchor)) then Library:ClosePopup() end end) Library.OnToggled = function(open) if not open then Library:ClosePopup() end end --// dropdown \\-- local function playersList(excludeSelf) local list = {} for _, player in ipairs(Players:GetPlayers()) do if not (excludeSelf and player == LocalPlayer) then table.insert(list, player.Name) end end table.sort(list) return list end local function teamsList() local list = {} for _, team in ipairs(svc('Teams'):GetTeams()) do table.insert(list, team.Name) end table.sort(list) return list end function Container:AddDropdown(idx, info) local owner = self info = Validate(info, Templates.Dropdown) if info.SpecialType == 'Player' then info.Values, info.AllowNull = playersList(info.ExcludeLocalPlayer), true elseif info.SpecialType == 'Team' then info.Values, info.AllowNull = teamsList(), true end local dropdown = { Text = type(info.Text) == 'string' and info.Text or nil, Value = info.Multi and {} or nil, Values = info.Values, DisabledValues = info.DisabledValues, Multi = info.Multi, SpecialType = info.SpecialType, ExcludeLocalPlayer = info.ExcludeLocalPlayer, Tooltip = info.Tooltip, DisabledTooltip = info.DisabledTooltip, Callback = info.Callback, Changed = info.Changed, Disabled = info.Disabled, Visible = info.Visible, Type = 'Dropdown', Library = Library, } local hasLabel = dropdown.Text ~= nil local row = New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, hasLabel and (H(20) + 18) or (H(20) + 2)), LayoutOrder = self:NextOrder(), Visible = dropdown.Visible, Parent = self.Container }) local title = New('TextLabel', { Text = dropdown.Text or '', BackgroundTransparency = 1, Size = dim2(1, 0, 0, 14), TextSize = Library.TextSize, TextXAlignment = Enum.TextXAlignment.Left, RichText = true, Visible = hasLabel, Parent = row }) local frame = New('Frame', { BackgroundTransparency = 1, AnchorPoint = vec2(0, 1), Position = dim2(0, 0, 1, 0), Size = dim2(1, 0, 0, H(20)), Parent = row }) local _, field = Panel(frame, { 'd', 'b' }, { Size = dim2(1, 0, 1, 0) }) local display = New('TextButton', { Text = '---', AutoButtonColor = false, BackgroundTransparency = 1, Size = dim2(1, -18, 1, 0), Position = dim2(0, 6, 0, 0), TextSize = Library.TextSize, TextXAlignment = Enum.TextXAlignment.Left, Parent = field }) local arrow = New('TextLabel', { Text = 'v', BackgroundTransparency = 1, AnchorPoint = vec2(1, 0), Position = dim2(1, -5, 0, 0), Size = dim2(0, 10, 1, 0), TextSize = Library.TextSize, Parent = field }, { TextColor3 = 'dim' }) dropdown.Holder, dropdown.Container = row, self.Container local tooltipHost = display if type(dropdown.Tooltip) == 'string' or type(dropdown.DisabledTooltip) == 'string' then dropdown.TooltipTable = Library:AddTooltip(dropdown.Tooltip, dropdown.DisabledTooltip, tooltipHost) dropdown.TooltipTable.Disabled = dropdown.Disabled == true end -- popup local itemHeight = H(18) local popup, popupInner = popupPanel(100, 40) local searchBox local list = New('ScrollingFrame', { BackgroundTransparency = 1, ScrollBarThickness = 2, AutomaticCanvasSize = Enum.AutomaticSize.Y, CanvasSize = dim2(0, 0, 0, 0), Size = dim2(1, 0, 1, 0), Active = true, ZIndex = 101, Parent = popupInner, }) Library.Theme(list, 'ScrollBarImageColor3', 'accent') New('UIListLayout', { SortOrder = Enum.SortOrder.LayoutOrder, Parent = list }) if info.Searchable then local searchRow = New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, H(20)), ZIndex = 102, Parent = popupInner }) local _, searchField = Panel(searchRow, { 'd', 'b' }, { Size = dim2(1, -4, 1, -4), Position = dim2(0, 2, 0, 2) }) searchBox = New('TextBox', { Text = '', PlaceholderText = 'Search', BackgroundTransparency = 1, Size = dim2(1, -8, 1, 0), Position = dim2(0, 4, 0, 0), TextSize = Library.TextSize, TextXAlignment = Enum.TextXAlignment.Left, ClearTextOnFocus = false, ZIndex = 103, Parent = searchField }) searchBox.PlaceholderColor3 = Palette.dim list.Position, list.Size = dim2(0, 0, 0, H(20)), dim2(1, 0, 1, -H(20)) end local buttons = {} local isOpen = false local function active() return dropdown.Multi and dropdown.Value or (dropdown.Value and { [dropdown.Value] = true } or {}) end local function activeCount() local count = 0 for _ in pairs(active()) do count += 1 end return count end function dropdown:GetActiveValues(returnCount) local out = {} for value in pairs(active()) do table.insert(out, value) end return returnCount == true and #out or out end function dropdown:Display() if Library.Unloaded then return end local text = '' if dropdown.Multi then local parts = {} for _, value in ipairs(dropdown.Values) do if dropdown.Value[value] then table.insert(parts, info.FormatDisplayValue and tostring(info.FormatDisplayValue(value)) or tostring(value)) end end text = table.concat(parts, ', ') else text = dropdown.Value and tostring(dropdown.Value) or '' if text ~= '' and info.FormatDisplayValue then text = tostring(info.FormatDisplayValue(text)) end end if #text > 30 then text = text:sub(1, 27) .. '...' end display.Text = text == '' and '---' or text display.TextColor3 = (text == '' or dropdown.Disabled) and Palette.dim or Palette.text end local function refreshButtons() local chosen = active() for _, entry in ipairs(buttons) do local selected = chosen[entry.Value] == true entry.Selected = selected local key = entry.Disabled and 'dim' or (selected and 'accent' or 'text') Library.Registry[entry.Button] = { TextColor3 = key } entry.Button.TextColor3 = Palette[key] entry.Mark.Visible = selected end end local function heightFor(count) return min(max(count, 1), info.MaxVisibleDropdownItems or 8) * itemHeight + 2 + (searchBox and H(20) or 0) end function dropdown:BuildDropdownList() for _, entry in ipairs(buttons) do entry.Holder:Destroy() end table.clear(buttons) local needle = searchBox and searchBox.Text:lower() or '' local shown = 0 for _, value in ipairs(dropdown.Values) do local text = tostring(info.FormatListValue and info.FormatListValue(value) or value) if needle == '' or text:lower():find(needle, 1, true) then shown += 1 local disabled = table.find(dropdown.DisabledValues or {}, value) ~= nil local holder = New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, itemHeight), LayoutOrder = disabled and 1 or 0, ZIndex = 101, Parent = list }) local mark = New('Frame', { Size = dim2(0, 2, 1, -4), Position = dim2(0, 2, 0, 2), ZIndex = 103, Visible = false, Parent = holder }, { BackgroundColor3 = 'accent' }) local button = New('TextButton', { Text = text, AutoButtonColor = false, BackgroundTransparency = 1, Size = dim2(1, 0, 1, 0), TextSize = Library.TextSize, TextXAlignment = Enum.TextXAlignment.Left, ZIndex = 102, Parent = holder }) New('UIPadding', { PaddingLeft = dim(0, 8), Parent = button }) local entry = { Value = value, Button = button, Mark = mark, Holder = holder, Disabled = disabled } table.insert(buttons, entry) Connect(button.MouseEnter, function() if not disabled then Tween(holder, { BackgroundTransparency = 0.82 }, 0.08) end holder.BackgroundColor3 = Palette.g end) Connect(button.MouseLeave, function() Tween(holder, { BackgroundTransparency = 1 }, 0.1) end) if not disabled then Connect(button.MouseButton1Click, function() local selected = active()[value] == true local try = not selected if activeCount() == 1 and not try and not info.AllowNull then -- the last selected value stays selected elseif dropdown.Multi then dropdown.Value[value] = try and true or nil else dropdown.Value = try and value or nil end dropdown:Display() refreshButtons() Library:UpdateDependencyBoxes() dropdown:RunChanged() if not dropdown.Multi then Library:ClosePopup() end end) end end end refreshButtons() if isOpen then local current = popup.Size popup.Size = UDim2.fromOffset(current.X.Offset, heightFor(shown)) end return shown end function dropdown:RunChanged() Library:SafeCallback(dropdown.Callback, dropdown.Value) Library:SafeCallback(dropdown.Changed, dropdown.Value) end function dropdown:OnChanged(callback) dropdown.Changed = callback end function dropdown:SetValue(value) if dropdown.Multi then local map = {} for key, on in pairs(value or {}) do if type(on) ~= 'boolean' then map[on] = true elseif on and table.find(dropdown.Values, key) then map[key] = true end end dropdown.Value = map else if table.find(dropdown.Values, value) then dropdown.Value = value elseif not value then dropdown.Value = nil end end dropdown:Display() refreshButtons() if not dropdown.Disabled then Library:UpdateDependencyBoxes() dropdown:RunChanged() end end function dropdown:SetValues(values) dropdown.Values = values dropdown:BuildDropdownList() dropdown:Display() end function dropdown:AddValues(values) if type(values) == 'table' then for _, value in ipairs(values) do table.insert(dropdown.Values, value) end elseif type(values) == 'string' then table.insert(dropdown.Values, values) else return end dropdown:BuildDropdownList() end function dropdown:SetDisabledValues(values) dropdown.DisabledValues = values dropdown:BuildDropdownList() end function dropdown:AddDisabledValues(values) if type(values) == 'table' then for _, value in ipairs(values) do table.insert(dropdown.DisabledValues, value) end elseif type(values) == 'string' then table.insert(dropdown.DisabledValues, values) end dropdown:BuildDropdownList() end function dropdown:SetDisabled(disabled) dropdown.Disabled = disabled if dropdown.TooltipTable then dropdown.TooltipTable.Disabled = disabled end Library:ClosePopup() dropdown:Display() title.TextTransparency = disabled and 0.5 or 0 end function dropdown:SetVisible(visible) dropdown.Visible = visible row.Visible = visible end function dropdown:SetText(text) dropdown.Text = text title.Text = text or '' title.Visible = text ~= nil row.Size = dim2(1, 0, 0, text and (H(20) + 18) or (H(20) + 2)) end local function close() isOpen = false arrow.Text = 'v' Tween(popup, { Size = UDim2.fromOffset(popup.Size.X.Offset, 0) }, 0.1) task.delay(0.11, function() if not isOpen then popup.Visible = false end end) if searchBox then searchBox.Text = '' end end local handle = { Frame = popup, Anchor = frame, Close = close } local function open() if dropdown.Disabled then return end if info.SpecialType == 'Player' then dropdown.Values = playersList(info.ExcludeLocalPlayer) elseif info.SpecialType == 'Team' then dropdown.Values = teamsList() end local shown = dropdown:BuildDropdownList() Library:OpenPopup(handle) isOpen = true arrow.Text = '^' local width = frame.AbsoluteSize.X / Library.GetScale() local height = heightFor(shown) placePopup(popup, frame, width, height, true) popup.Visible = true if Animating() then popup.Size = UDim2.fromOffset(width, 0) Tween(popup, { Size = UDim2.fromOffset(width, height) }, 0.14, Enum.EasingStyle.Quint) end if searchBox then searchBox:CaptureFocus() end end Connect(display.MouseButton1Click, function() if Library.Popup == handle and isOpen then Library:ClosePopup() else open() end end) dropdown.Open = function() if not (Library.Popup == handle and isOpen) then open() end end dropdown.Close = function() Library:ClosePopup() end if searchBox then Connect(searchBox:GetPropertyChangedSignal('Text'), function() dropdown:BuildDropdownList() end) end Library:OnUnload(function() popup:Destroy() end) -- defaults: a value, a list of values, or an index into Values local defaults = {} if type(info.Default) == 'string' then local at = table.find(dropdown.Values, info.Default) if at then table.insert(defaults, at) end elseif type(info.Default) == 'table' then for _, value in pairs(info.Default) do local at = table.find(dropdown.Values, value) if at then table.insert(defaults, at) end end elseif info.Default ~= nil and dropdown.Values[info.Default] ~= nil then table.insert(defaults, info.Default) end for _, at in ipairs(defaults) do if dropdown.Multi then dropdown.Value[dropdown.Values[at]] = true else dropdown.Value = dropdown.Values[at] break end end dropdown:Display() dropdown:BuildDropdownList() dropdown.Default, dropdown.DefaultValues = defaults, dropdown.Values if dropdown.Disabled then dropdown:SetDisabled(true) end function dropdown:Destroy() dropdown.Destroyed = true popup:Destroy() row:Destroy() if Library.Options[idx] == dropdown then Library.Options[idx] = nil end local at = table.find(owner.Elements or {}, dropdown) if at then table.remove(owner.Elements, at) end end table.insert(self.Elements, dropdown) if idx ~= nil then Library.Options[idx] = dropdown end return dropdown end --// colour picker (addon) \\-- local HUE_KEYS = {} for index = 0, 6 do table.insert(HUE_KEYS, ColorSequenceKeypoint.new(index / 6, hsv(index / 6, 1, 1))) end local HUE_SEQUENCE = ColorSequence.new(HUE_KEYS) function Addons:AddColorPicker(idx, info) info = Validate(info, Templates.ColorPicker) local parent = self local host = parent.RightHolder if not host then return parent end local picker = { Value = info.Default, Transparency = type(info.Transparency) == 'number' and info.Transparency or 0, Title = info.Title, Callback = info.Callback, Changed = info.Changed, Type = 'ColorPicker', Library = Library, HasAlpha = info.Transparency ~= nil and info.Transparency ~= false, } picker.Hue, picker.Sat, picker.Vib = info.Default:ToHSV() local swatchOuter, swatch = Panel(host, { 'a', 'g' }, { Size = UDim2.fromOffset(H(20), H(10)) }) swatchOuter.LayoutOrder = 10 + #parent.Addons Library.Registry[swatch] = nil -- the swatch shows the picked colour; the theme registry must not repaint it local swatchButton = New('TextButton', { Text = '', AutoButtonColor = false, BackgroundTransparency = 1, Size = dim2(1, 0, 1, 0), ZIndex = 4, Parent = swatch }) swatch.BackgroundColor3 = picker.Value local width, svH = 190, 110 local popup, inner = popupPanel(width, svH + 76 + (picker.HasAlpha and 16 or 0)) New('UIPadding', { PaddingTop = dim(0, 6), PaddingLeft = dim(0, 6), PaddingRight = dim(0, 6), PaddingBottom = dim(0, 6), Parent = inner }) local heading = New('TextLabel', { Text = (picker.Title or 'Color'), BackgroundTransparency = 1, Size = dim2(1, 0, 0, 14), TextSize = Library.TextSize, TextXAlignment = Enum.TextXAlignment.Left, ZIndex = 102, Parent = inner }, { TextColor3 = 'dim' }) local svOuter, svBase = Panel(inner, { 'd' }, { Size = UDim2.fromOffset(width - 12 - 22, svH), Position = dim2(0, 0, 0, 18) }) svOuter.ZIndex = 101 local sv = New('TextButton', { Text = '', AutoButtonColor = false, Size = dim2(1, 0, 1, 0), BackgroundColor3 = hsv(picker.Hue, 1, 1), ZIndex = 102, Parent = svBase }) local white = New('Frame', { Size = dim2(1, 0, 1, 0), BackgroundColor3 = rgb(255, 255, 255), ZIndex = 103, Parent = sv }) New('UIGradient', { Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 0), NumberSequenceKeypoint.new(1, 1) }), Parent = white }) local black = New('Frame', { Size = dim2(1, 0, 1, 0), BackgroundColor3 = rgb(0, 0, 0), ZIndex = 104, Parent = sv }) New('UIGradient', { Rotation = 90, Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 1), NumberSequenceKeypoint.new(1, 0) }), Parent = black }) local knob = New('Frame', { Size = UDim2.fromOffset(7, 7), AnchorPoint = vec2(0.5, 0.5), BackgroundTransparency = 1, ZIndex = 106, Parent = sv }) New('UIStroke', { Color = rgb(255, 255, 255), Thickness = 1, Parent = knob }) local hueOuter, hueBase = Panel(inner, { 'd' }, { Size = UDim2.fromOffset(16, svH), Position = dim2(1, -16, 0, 18) }) hueOuter.ZIndex = 101 local hue = New('TextButton', { Text = '', AutoButtonColor = false, Size = dim2(1, 0, 1, 0), BackgroundColor3 = rgb(255, 255, 255), ZIndex = 102, Parent = hueBase }) New('UIGradient', { Color = HUE_SEQUENCE, Rotation = 90, Parent = hue }) local hueMark = New('Frame', { Size = dim2(1, 0, 0, 2), AnchorPoint = vec2(0, 0.5), BackgroundColor3 = rgb(255, 255, 255), ZIndex = 106, Parent = hue }) local alphaBase, alpha, alphaMark, alphaGradient if picker.HasAlpha then local alphaOuter alphaOuter, alphaBase = Panel(inner, { 'd' }, { Size = UDim2.new(1, 0, 0, 12), Position = dim2(0, 0, 0, 18 + svH + 6) }) alphaOuter.ZIndex = 101 alpha = New('TextButton', { Text = '', AutoButtonColor = false, Size = dim2(1, 0, 1, 0), BackgroundColor3 = rgb(255, 255, 255), ZIndex = 102, Parent = alphaBase }) alphaGradient = New('UIGradient', { Color = ColorSequence.new(picker.Value), Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 0), NumberSequenceKeypoint.new(1, 1) }), Parent = alpha }) alphaMark = New('Frame', { Size = dim2(0, 2, 1, 0), AnchorPoint = vec2(0.5, 0), BackgroundColor3 = rgb(255, 255, 255), ZIndex = 106, Parent = alpha }) end local hexRow = New('Frame', { BackgroundTransparency = 1, Position = dim2(0, 0, 0, 18 + svH + 6 + (picker.HasAlpha and 18 or 0)), Size = dim2(1, 0, 0, 20), ZIndex = 101, Parent = inner }) local _, hexField = Panel(hexRow, { 'd', 'b' }, { Size = dim2(1, 0, 1, 0) }) local hexBox = New('TextBox', { Text = '', PlaceholderText = 'hex', BackgroundTransparency = 1, Size = dim2(1, -8, 1, 0), Position = dim2(0, 5, 0, 0), TextSize = Library.TextSize, TextXAlignment = Enum.TextXAlignment.Left, ClearTextOnFocus = false, ZIndex = 103, Parent = hexField }) hexBox.PlaceholderColor3 = Palette.dim function picker:Display() picker.Value = hsv(picker.Hue, picker.Sat, picker.Vib) swatch.BackgroundColor3 = picker.Value sv.BackgroundColor3 = hsv(picker.Hue, 1, 1) knob.Position = dim2(picker.Sat, 0, 1 - picker.Vib, 0) hueMark.Position = dim2(0, 0, picker.Hue, 0) if alphaGradient then alphaGradient.Color = ColorSequence.new(picker.Value) alphaMark.Position = dim2(1 - picker.Transparency, 0, 0, 0) end if not hexBox:IsFocused() then hexBox.Text = picker.Value:ToHex():upper() end end function picker:OnChanged(callback) picker.Changed = callback end function picker:RunChanged() Library:SafeCallback(picker.Callback, picker.Value) Library:SafeCallback(picker.Changed, picker.Value) end -- Obsidian's contract: SetValue takes {h, s, v} or a Color3; the transparency only counts when the picker has alpha function picker:SetValue(hsvTable, transparency) if typeof(hsvTable) == 'Color3' then picker:SetValueRGB(hsvTable, transparency) return end picker.Hue, picker.Sat, picker.Vib = hsvTable[1], hsvTable[2], hsvTable[3] picker.Transparency = picker.HasAlpha and (transparency or 0) or 0 picker:Update() end function picker:SetValueRGB(color, transparency) -- Library.ColorMigrations[id] = { from = Color3, to = Color3 }: a saved value that equals an old stock colour is shown as its replacement local migration = idx ~= nil and Library.ColorMigrations and Library.ColorMigrations[idx] if migration and math.abs(color.R - migration.from.R) < 0.004 and math.abs(color.G - migration.from.G) < 0.004 and math.abs(color.B - migration.from.B) < 0.004 then color = migration.to end picker.Hue, picker.Sat, picker.Vib = color:ToHSV() picker.Transparency = picker.HasAlpha and (transparency or 0) or 0 picker:Update() end function picker:Update() picker:Display() picker:RunChanged() end function picker:Refresh() swatchOuter.Visible = parent.Visible ~= false end local dragging = nil local function drag() local mouse = MouseAbs() if dragging == 'sv' then picker.Sat = clamp((mouse.X - sv.AbsolutePosition.X) / max(sv.AbsoluteSize.X, 1), 0, 1) picker.Vib = 1 - clamp((mouse.Y - sv.AbsolutePosition.Y) / max(sv.AbsoluteSize.Y, 1), 0, 1) elseif dragging == 'hue' then picker.Hue = clamp((mouse.Y - hue.AbsolutePosition.Y) / max(hue.AbsoluteSize.Y, 1), 0, 1) elseif dragging == 'alpha' and alpha then picker.Transparency = 1 - clamp((mouse.X - alpha.AbsolutePosition.X) / max(alpha.AbsoluteSize.X, 1), 0, 1) end picker:Display() picker:RunChanged() end local moveHandle, endHandle, renderHandle, moved local function stopDrag() dragging = nil for _, listener in ipairs({ moveHandle, endHandle, renderHandle }) do listener.Disconnect() end moveHandle, endHandle, renderHandle = nil, nil, nil end for name, target in pairs({ sv = sv, hue = hue, alpha = alpha }) do if target then Connect(target.InputBegan, function(input) if (input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch) and not moveHandle then dragging = name drag() moveHandle = Library:OnInput('Changed', function(changed) if changed.UserInputType == Enum.UserInputType.MouseMovement or changed.UserInputType == Enum.UserInputType.Touch then moved = true end end) renderHandle = Library:OnInput('Render', function() if moved then moved = false drag() end end) endHandle = Library:OnInput('Ended', function(ended) if ended.UserInputType == Enum.UserInputType.MouseButton1 or ended.UserInputType == Enum.UserInputType.Touch then stopDrag() end end) end end) end end Connect(hexBox.FocusLost, function() local text = hexBox.Text:gsub('#', '') if #text == 6 and text:match('^%x+$') then picker:SetValueRGB(Color3.fromHex(text), picker.Transparency) else picker:Display() end end) local isOpen = false local function close() isOpen = false popup.Visible = false end local handle = { Frame = popup, Anchor = swatchOuter, Close = close } local function togglePopup() if isOpen then Library:ClosePopup() return end Library:OpenPopup(handle) isOpen = true placePopup(popup, swatchOuter, width, popup.Size.Y.Offset, true) popup.Visible = true picker:Display() end picker.TogglePopup = togglePopup Connect(swatchButton.MouseButton1Click, togglePopup) Library:OnUnload(function() popup:Destroy() end) picker:Display() picker.Default = picker.Value function picker:Destroy() picker.Destroyed = true popup:Destroy() swatchOuter:Destroy() local at = table.find(parent.Addons, picker) if at then table.remove(parent.Addons, at) end if Library.Options[idx] == picker then Library.Options[idx] = nil end end table.insert(parent.Addons, picker) if idx ~= nil then Library.Options[idx] = picker end return parent end --// key picker (addon) \\-- local SPECIAL_KEYS = { MB1 = Enum.UserInputType.MouseButton1, MB2 = Enum.UserInputType.MouseButton2, MB3 = Enum.UserInputType.MouseButton3 } local SPECIAL_INPUT = { [Enum.UserInputType.MouseButton1] = 'MB1', [Enum.UserInputType.MouseButton2] = 'MB2', [Enum.UserInputType.MouseButton3] = 'MB3' } local MODIFIERS = { LAlt = Enum.KeyCode.LeftAlt, RAlt = Enum.KeyCode.RightAlt, LCtrl = Enum.KeyCode.LeftControl, RCtrl = Enum.KeyCode.RightControl, LShift = Enum.KeyCode.LeftShift, RShift = Enum.KeyCode.RightShift, Tab = Enum.KeyCode.Tab, CapsLock = Enum.KeyCode.CapsLock, } local MODIFIER_INPUT = {} for name, code in pairs(MODIFIERS) do MODIFIER_INPUT[code] = name end local SHORT = { LeftShift = 'LShift', RightShift = 'RShift', LeftControl = 'LCtrl', RightControl = 'RCtrl', LeftAlt = 'LAlt', RightAlt = 'RAlt', Backspace = 'BkSp', Return = 'Enter', Insert = 'Ins', Delete = 'Del', PageUp = 'PgUp', PageDown = 'PgDn', Backquote = '`', CapsLock = 'Caps', } Library.KeybindToggles = Library.KeybindToggles or {} Library.IsPicking = false function Addons:AddKeyPicker(idx, info) info = Validate(info, Templates.KeyPicker) local parent = self local isButton = parent.Type == 'Button' or parent.Type == 'SubButton' local host = parent.RightHolder if isButton then assert(info.Mode == 'Press', "KeyPicker on Buttons can only be applied with the 'Press' mode.") host = parent.Row end if not host then return parent end local picker = { Text = info.Text, Value = info.Default, Modifiers = info.DefaultModifiers, DisplayValue = info.Default, Blacklisted = info.Blacklisted, BlacklistedModifiers = info.BlacklistedModifiers, Whitelisted = info.Whitelisted, WhitelistedModifiers = info.WhitelistedModifiers, Toggled = false, Mode = info.Mode, SyncToggleState = info.SyncToggleState, Callback = info.Callback, ChangedCallback = info.ChangedCallback, Changed = info.Changed, Clicked = info.Clicked, Type = 'KeyPicker', Library = Library, Connections = {}, } local modes = info.Modes if picker.Mode == 'Press' then assert(parent.Type == 'Label' or isButton, "KeyPicker with the mode 'Press' can be only applied on Labels and Buttons.") picker.SyncToggleState, modes, info.Mode = false, { 'Press' }, 'Press' end if picker.SyncToggleState then modes = { 'Toggle', 'Hold' } if not table.find(modes, info.Mode) then picker.Mode = 'Toggle' end end -- the checkbox is the source of truth in Toggle mode (pickers that already sync it are left alone) local function drivesToggle() return parent.Type == 'Toggle' and picker.Mode == 'Toggle' and not picker.SyncToggleState end local function flipToggled() if drivesToggle() then picker.Toggled = not parent.Value else picker.Toggled = not picker.Toggled end end local function mirrorToggled() if drivesToggle() and parent.Value ~= picker.Toggled then parent:SetValue(picker.Toggled) picker.Toggled = parent.Value end end local function activeModifiers() local out = {} for name, code in pairs(MODIFIERS) do if UserInputService:IsKeyDown(code) then table.insert(out, name) end end return out end local function modifiersHeld(required) if type(required) ~= 'table' or #required == 0 then return true end local held = activeModifiers() for _, name in ipairs(required) do if not table.find(held, name) then return false end end return true end local function verifyModifiers(list) local out = {} if type(list) ~= 'table' then return out end for _, name in ipairs(list) do if MODIFIERS[name] then table.insert(out, name) end end return out end picker.Modifiers = verifyModifiers(picker.Modifiers) -- the key button local buttonOuter, buttonInner = Panel(host, { 'a', 'c' }, { Size = UDim2.fromOffset(H(40), H(14)) }) buttonOuter.LayoutOrder = 20 + #parent.Addons if isButton then buttonOuter.Size = UDim2.fromOffset(44, H(20)) buttonOuter.LayoutOrder = 1000 end local keyButton = New('TextButton', { Text = '', AutoButtonColor = false, BackgroundTransparency = 1, Size = dim2(1, 0, 1, 0), TextSize = Library.TextSize, ZIndex = 4, Parent = buttonInner }) -- row in the keybind list local listHolder = New('TextButton', { Text = '', AutoButtonColor = false, Active = Library.IsMobile, BackgroundTransparency = 1, Size = dim2(0, 0, 0, Library.IsMobile and 24 or 14), AutomaticSize = Enum.AutomaticSize.X, ClipsDescendants = true, Visible = false, Parent = Library.KeybindContainer }) local listLabel = New('TextLabel', { Text = '', BackgroundTransparency = 1, Size = dim2(0, 0, 1, 0), AutomaticSize = Enum.AutomaticSize.X, TextSize = Library.TextSize, TextXAlignment = Enum.TextXAlignment.Left, Parent = listHolder }, { TextColor3 = 'dim' }) if Library.IsMobile then Connect(listHolder.MouseButton1Click, function() if picker.Mode == 'Toggle' then flipToggled() picker:DoClick() mirrorToggled() elseif picker.Mode == 'Press' then picker:DoClick() end picker:Update() end) end -- mode menu (right click) local menu, menuInner = popupPanel(70, #modes * 18 + 4) New('UIListLayout', { SortOrder = Enum.SortOrder.LayoutOrder, Parent = menuInner }) local modeButtons = {} for index, mode in ipairs(modes) do local button = New('TextButton', { Text = mode, AutoButtonColor = false, BackgroundTransparency = 1, Size = dim2(1, 0, 0, 18), LayoutOrder = index, TextSize = Library.TextSize, ZIndex = 102, Parent = menuInner }, { TextColor3 = 'dim' }) modeButtons[mode] = button Connect(button.MouseButton1Click, function() picker:SetMode(mode) Library:ClosePopup() end) end function picker:SetMode(mode) picker.Mode = mode for name, button in pairs(modeButtons) do ThemeSet(button, 'TextColor3', name == mode and 'accent' or 'dim') end picker:Update() end local picking = false local lastShown function picker:Display(text) if Library.Unloaded then return end local shown = text or picker.DisplayValue shown = shown:gsub('%a+', function(word) return SHORT[word] or word end) if picker.Value == 'None' or picker.Value == 'Unknown' then shown = text or 'none' end if shown ~= lastShown then lastShown = shown keyButton.Text = shown buttonOuter.Size = UDim2.fromOffset(TextWidth(shown, Library.TextSize) + 12, isButton and H(20) or H(14)) end end table.insert(Library.FontListeners, function() lastShown = nil picker:Display() end) function picker:GetState() if picker.Mode == 'Always' then return true end if picker.Mode == 'Hold' then local key = picker.Value if key == 'None' or key == 'Unknown' then return false end if not modifiersHeld(picker.Modifiers) or picking then return false end if SPECIAL_KEYS[key] then if Library.Toggled then return false end return UserInputService:IsMouseButtonPressed(SPECIAL_KEYS[key]) and not UserInputService:GetFocusedTextBox() end local ok, code = pcall(function() return Enum.KeyCode[key] end) return ok and code ~= nil and UserInputService:IsKeyDown(code) and not UserInputService:GetFocusedTextBox() or false end return picker.Toggled end local function listText() local key = picker.DisplayValue:gsub('%a+', function(word) return SHORT[word] or word end) if picker.Value == 'None' or picker.Value == 'Unknown' then key = 'Tap' end return ('[%s] %s (%s)'):format(key, picker.Text, picker.Mode) end function picker:Update() picker:Display() if info.NoUI then return end if picker.Mode == 'Toggle' and parent.Type == 'Toggle' and parent.Disabled then listHolder.Visible = false return end local state = picker:GetState() if picker.SyncToggleState and parent.Value ~= state then parent:SetValue(state) end -- only keys that are bound are listed: the row pops in when a key gets bound and leaves when it is cleared local bound = picker.Value ~= 'None' and picker.Value ~= 'Unknown' -- a phone has no keys: there every togglable bind is listed, and tapping its row flips it if not bound and not (Library.IsMobile and (picker.Mode == 'Toggle' or picker.Mode == 'Press')) then listHolder.Visible = false return end listLabel.Text = listText() ThemeSet(listLabel, 'TextColor3', state and 'accent' or 'dim') if not listHolder.Visible then listHolder.Visible = true if Animating() then listHolder.Size = dim2(0, 0, 0, 0) Tween(listHolder, { Size = dim2(0, 0, 0, Library.IsMobile and 24 or 14) }, 0.22, Enum.EasingStyle.Back) FX.Scramble(listLabel, listLabel.Text, 0.3) end end end function picker:OnChanged(callback) picker.Changed = callback end function picker:OnClick(callback) picker.Clicked = callback end function picker:DoClick() if picking then return end if picker.Mode == 'Press' then if picker.Toggled and info.WaitForCallback == true then return end picker.Toggled = true end Library:SafeCallback(picker.Callback, picker.Toggled) Library:SafeCallback(picker.Clicked, picker.Toggled) if isButton then Library:SafeCallback(parent.Func, picker.Toggled) end if Library.ToggleKeybind == picker and Library.Toggle then Library:Toggle() end if picker.Mode == 'Press' then picker.Toggled = false end end function picker:RunChanged(valid, code) if valid == nil or code == nil then valid, code = pcall(function() if picker.Value == 'None' then return nil end if SPECIAL_KEYS[picker.Value] == nil then return Enum.KeyCode[picker.Value] end return SPECIAL_KEYS[picker.Value] end) end local converted = {} for _, name in ipairs(picker.Modifiers) do table.insert(converted, MODIFIERS[name]) end Library:SafeCallback(picker.ChangedCallback, code, converted) Library:SafeCallback(picker.Changed, code, converted) end function picker:SetValue(data) local key, mode, modifiers = data[1], data[2], data[3] local valid, code = pcall(function() if key == 'None' then key = nil return nil end if SPECIAL_KEYS[key] == nil then return Enum.KeyCode[key] end return SPECIAL_KEYS[key] end) if key == nil then picker.Value = 'None' elseif valid then picker.Value = key else picker.Value = 'Unknown' end picker.Modifiers = verifyModifiers(type(modifiers) == 'table' and modifiers or picker.Modifiers) picker.DisplayValue = #picker.Modifiers > 0 and (table.concat(picker.Modifiers, ' + ') .. ' + ' .. picker.Value) or picker.Value if mode and modeButtons[mode] then picker:SetMode(mode) end picker:Update() picker:RunChanged(valid, code) end function picker:SetText(text) picker.Text = text picker:Update() end local function setPicking(state) picking = state Library.IsPicking = state parent.AnyKeyPickerPicking = state picker:Update() end local function startPicking() if picking or Library.IsPicking then return end setPicking(true) keyButton.Text, buttonOuter.Size = '...', UDim2.fromOffset(28, isButton and H(20) or H(14)) local current local function valid(input) if input.KeyCode == Enum.KeyCode.Escape then return true end local isModifier = input.UserInputType == Enum.UserInputType.Keyboard and MODIFIER_INPUT[input.KeyCode] ~= nil local name if SPECIAL_INPUT[input.UserInputType] then name = SPECIAL_INPUT[input.UserInputType] elseif input.UserInputType == Enum.UserInputType.Keyboard then name = isModifier and MODIFIER_INPUT[input.KeyCode] or input.KeyCode.Name end if name then if isModifier then if picker.WhitelistedModifiers and #picker.WhitelistedModifiers > 0 and not table.find(picker.WhitelistedModifiers, name) then return false end if picker.BlacklistedModifiers and table.find(picker.BlacklistedModifiers, name) then return false end else if picker.Whitelisted and #picker.Whitelisted > 0 and not table.find(picker.Whitelisted, name) then return false end if picker.Blacklisted and table.find(picker.Blacklisted, name) then return false end end end return true end while true do local input = UserInputService.InputBegan:Wait() if UserInputService:GetFocusedTextBox() ~= nil or Library.Unloaded then setPicking(false) return end if valid(input) then current = input break end end local key = 'Unknown' if SPECIAL_INPUT[current.UserInputType] then key = SPECIAL_INPUT[current.UserInputType] elseif current.UserInputType == Enum.UserInputType.Keyboard then key = current.KeyCode == Enum.KeyCode.Escape and 'None' or current.KeyCode.Name end picker.Toggled = parent.Type == 'Toggle' and parent.Value or false picker:SetValue({ key, picker.Mode, {} }) -- Always ignores its key, so a key bound in Always mode would never do anything: binding one to a checkbox switches the picker to Toggle if picker.Mode == 'Always' and parent.Type == 'Toggle' and key ~= 'None' and key ~= 'Unknown' and modeButtons.Toggle then picker:SetMode('Toggle') picker.Toggled = parent.Value picker:Update() Library:Notify({ Title = picker.Text, Description = picker.DisplayValue .. ' now toggles it. Right-click the key to use Hold or Always instead.', Time = 4 }) end -- a key still reported as held (alt-tab while binding, a lost key-up) must not keep picking mode on forever local deadline = os.clock() + 1.5 repeat task.wait() local down = false if SPECIAL_INPUT[current.UserInputType] then down = UserInputService:IsMouseButtonPressed(current.UserInputType) elseif current.UserInputType == Enum.UserInputType.Keyboard then down = UserInputService:IsKeyDown(current.KeyCode) end until not down or os.clock() > deadline or Library.Unloaded setPicking(false) end Connect(keyButton.MouseButton1Click, function() if picking or Library.IsPicking then return end local ok, err = pcall(startPicking) if picking then setPicking(false) end if not ok then warn('[ZkxVader] KeyPicker: ' .. tostring(err)) end end) local menuOpen = false local menuHandle = { Frame = menu, Anchor = keyButton, Close = function() menuOpen = false menu.Visible = false end } local function toggleMenu() if menuOpen then Library:ClosePopup() return end Library:OpenPopup(menuHandle) menuOpen = true placePopup(menu, buttonOuter, 70, #modes * 18 + 4, true) menu.Visible = true end picker.ToggleMenu = toggleMenu Connect(keyButton.MouseButton2Click, toggleMenu) local function relevant(input) -- a key press only matters to a picker when it is that picker's key, a modifier of its combo, or the picker holds (state follows the key) if SPECIAL_INPUT[input.UserInputType] == picker.Value then return true end if input.UserInputType ~= Enum.UserInputType.Keyboard then return false end return input.KeyCode.Name == picker.Value or (#picker.Modifiers > 0 and MODIFIER_INPUT[input.KeyCode] ~= nil) end Library:OnInput('Began', function(input) if Library.Unloaded then return end local isMouse = SPECIAL_INPUT[input.UserInputType] ~= nil if picker.Mode == 'Always' or picker.Value == 'Unknown' or picker.Value == 'None' or picking or Library.IsPicking or UserInputService:GetFocusedTextBox() or (isMouse and Library.Toggled) then return end local holding = modifiersHeld(picker.Modifiers) and (SPECIAL_INPUT[input.UserInputType] == picker.Value or (input.UserInputType == Enum.UserInputType.Keyboard and input.KeyCode.Name == picker.Value)) if picker.Mode == 'Toggle' then if holding then flipToggled() picker:DoClick() mirrorToggled() end elseif picker.Mode == 'Press' then if holding then picker:DoClick() end end if holding or (picker.Mode == 'Hold' and relevant(input)) then picker:Update() end end) Library:OnInput('Ended', function(input) if Library.Unloaded or picker.Mode ~= 'Hold' then return end local isMouse = SPECIAL_INPUT[input.UserInputType] ~= nil if picker.Value == 'Unknown' or picker.Value == 'None' or picking or Library.IsPicking or UserInputService:GetFocusedTextBox() or (isMouse and Library.Toggled) then return end if relevant(input) then picker:Update() end end) picker:SetMode(picker.Mode) Library:OnUnload(function() menu:Destroy() end) picker.Default, picker.DefaultModifiers = picker.Value, table.clone(picker.Modifiers or {}) function picker:Destroy() picker.Destroyed = true menu:Destroy() buttonOuter:Destroy() listHolder:Destroy() local at = table.find(parent.Addons, picker) if at then table.remove(parent.Addons, at) end if Library.Options[idx] == picker then Library.Options[idx] = nil end end table.insert(parent.Addons, picker) if idx ~= nil then Library.Options[idx] = picker end picker:Update() return parent end end -- ==== 42_viewport.lua ==== -- ZkxVader viewport element (the ESP tab's live preview): a ViewportFrame inside a Vaderhaxx panel, Obsidian's Viewport contract. do local Container = Classes.Container function Container:AddViewport(idx, info) info = Library:Validate(info, { Object = nil, Camera = nil, Clone = true, AutoFocus = true, Interactive = false, Height = 200, Visible = true }) local owner = self local object = info.Object if info.Clone and typeof(object) == 'Instance' then local wasArchivable = object.Archivable object.Archivable = true object = object:Clone() object.Archivable = wasArchivable end local viewport = { Destroyed = false, Object = object, Camera = info.Camera or Instance.new('Camera'), Interactive = info.Interactive, AutoFocus = info.AutoFocus, Visible = info.Visible, Type = 'Viewport', Library = Library, } assert(typeof(viewport.Object) == 'Instance' and (viewport.Object:IsA('BasePart') or viewport.Object:IsA('Model')), 'Instance must be a BasePart or Model.') assert(typeof(viewport.Camera) == 'Instance' and viewport.Camera:IsA('Camera'), 'Camera must be a valid Camera instance.') local holder = New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, info.Height), LayoutOrder = self:NextOrder(), Visible = viewport.Visible, Parent = self.Container }) local _, box = Panel(holder, { 'a', 'b', 'd' }, { Size = dim2(1, 0, 1, 0) }) local frame = New('ViewportFrame', { BackgroundTransparency = 1, Size = dim2(1, 0, 1, 0), CurrentCamera = viewport.Camera, Active = viewport.Interactive, Parent = box }) viewport.Camera.Parent = frame viewport.Object.Parent = frame viewport.Holder, viewport.Frame = holder, frame local function focus() local object = viewport.Object local size = object:IsA('BasePart') and object.Size or select(2, object:GetBoundingBox()) local extent = math.max(size.X, size.Y, size.Z) local position = object:GetPivot().Position viewport.Camera.CFrame = CFrame.new(position + Vector3.new(0, extent / 2, extent * 2), position) end if viewport.AutoFocus then focus() end -- the interactive orbit (right mouse drag, wheel zoom) of the stock widget local dragging, last = false, nil Connect(frame.InputBegan, function(input) if viewport.Interactive and input.UserInputType == Enum.UserInputType.MouseButton2 then dragging, last = true, input.Position end end) Library:OnInput('Ended', function(input) if input.UserInputType == Enum.UserInputType.MouseButton2 then dragging = false end end) Library:OnInput('Changed', function(input) if Library.Unloaded or not viewport.Interactive or not dragging or input.UserInputType ~= Enum.UserInputType.MouseMovement then return end local delta = input.Position - last last = input.Position local position = viewport.Object:GetPivot().Position local camera = viewport.Camera camera.CFrame = CFrame.new(position) * CFrame.fromAxisAngle(Vector3.new(0, 1, 0), -delta.X * 0.01) * CFrame.new(-position) * camera.CFrame local pitched = CFrame.new(position) * CFrame.fromAxisAngle(camera.CFrame.RightVector, -delta.Y * 0.01) * CFrame.new(-position) * camera.CFrame if pitched.UpVector.Y > 0.1 then camera.CFrame = pitched end end) Connect(frame.InputChanged, function(input) if viewport.Interactive and input.UserInputType == Enum.UserInputType.MouseWheel then viewport.Camera.CFrame += viewport.Camera.CFrame.LookVector * (input.Position.Z * 2) end end) function viewport:SetObject(newObject, clone) assert(newObject, 'Object cannot be nil.') if clone then newObject = newObject:Clone() end if viewport.Object then viewport.Object:Destroy() end viewport.Object = newObject viewport.Object.Parent = frame end function viewport:SetHeight(height) holder.Size = dim2(1, 0, 0, height) end function viewport:Focus() if viewport.Object then focus() end end function viewport:SetCamera(camera) viewport.Camera = camera frame.CurrentCamera = camera end function viewport:SetInteractive(interactive) viewport.Interactive = interactive frame.Active = interactive end function viewport:SetVisible(visible) viewport.Visible = visible holder.Visible = visible end function viewport:Destroy() viewport.Destroyed = true holder:Destroy() local at = table.find(owner.Elements, viewport) if at then table.remove(owner.Elements, at) end if idx ~= nil and Library.Options[idx] == viewport then Library.Options[idx] = nil end end table.insert(self.Elements, viewport) viewport.Idx = idx if idx ~= nil then Library.Options[idx] = viewport end return viewport end end -- ==== 43_search.lua ==== -- ZkxVader search: a box in the header; typing lists every matching control of every tab ("tab > box > name"); a click jumps there and flashes the row. -- (The real rows are never hidden or moved, so searching can not disturb layouts or dependency boxes.) do local Window = Classes.Window local function plain(text) return (tostring(text or ''):gsub('<[^>]->', '')) end local function walk(container, visit) for _, element in ipairs(container.Elements or {}) do if element.Type == 'DependencyBox' then walk(element, visit) else visit(element) end end end -- every searchable element of the menu: { tab, box, boxName, element } local function collect() local found = {} for _, tab in ipairs(Library.Window and Library.Window.Tabs or {}) do for name, box in pairs(tab.Groupboxes) do walk(box, function(element) table.insert(found, { tab = tab, box = box, boxName = name, element = element }) end) end for _, tabbox in pairs(tab.Tabboxes) do for _, sub in ipairs(tabbox.Tabs or {}) do walk(sub, function(element) table.insert(found, { tab = tab, box = sub, boxName = tostring(tabbox.Name or 'tabs') .. ' / ' .. tostring(sub.Name), element = element, sub = sub }) end) end end end return found end local SEARCHABLE = { Toggle = true, Slider = true, Dropdown = true, Input = true, Label = true, Button = true } local function flash(element) local holder = element.Holder if not (holder and holder.Parent) then return end local glow = New('Frame', { Size = dim2(1, 8, 1, 4), Position = dim2(0, -4, 0, -2), BackgroundTransparency = 0.7, ZIndex = holder.ZIndex + 5, Active = false, Parent = holder }, { BackgroundColor3 = 'accent' }) Tween(glow, { BackgroundTransparency = 1 }, 1.4, Enum.EasingStyle.Quad) task.delay(1.5, function() glow:Destroy() end) end local function jump(entry) entry.tab:Show(true) local root = entry.box.Root or entry.box if root.SetCollapsed then root:SetCollapsed(false) end if entry.sub then entry.sub:Show() end task.delay(0.12, function() local holder = entry.element.Holder local column = entry.box.Container and entry.box.Container:FindFirstAncestorWhichIsA('ScrollingFrame') if holder and column then local target = holder.AbsolutePosition.Y - column.AbsolutePosition.Y + column.CanvasPosition.Y - 24 column.CanvasPosition = Vector2.new(0, max(0, target)) end flash(entry.element) end) end function Window:BuildSearch() local header = self.TitleHolder local footerWidth = self.FooterLabel.Visible and (TextWidth(self.FooterLabel.Text, Library.TextSize) + 16) or 8 local outer, field = Panel(header, { 'a', 'd' }, { Size = UDim2.fromOffset(Library.IsMobile and 130 or 170, H(20)), AnchorPoint = vec2(1, 0.5), Position = UDim2.new(1, -footerWidth, 0.5, 0) }) outer.ZIndex = 5 local box = New('TextBox', { Text = '', PlaceholderText = 'Search', BackgroundTransparency = 1, Size = dim2(1, -10, 1, 0), Position = dim2(0, 6, 0, 0), TextSize = Library.TextSize, TextXAlignment = Enum.TextXAlignment.Left, ClearTextOnFocus = false, ZIndex = 6, Parent = field, }) box.PlaceholderColor3 = Palette.dim self.SearchBox = box -- results page local page = New('Frame', { Name = 'SearchResults', BackgroundTransparency = 1, Size = dim2(1, 0, 1, 0), Visible = false, ZIndex = 30, Parent = self.Pages }) New('Frame', { Size = dim2(1, 0, 1, 0), ZIndex = 30, Parent = page }, { BackgroundColor3 = 'e' }) local list = New('ScrollingFrame', { BackgroundTransparency = 1, Size = dim2(1, -24, 1, -24), Position = dim2(0, 12, 0, 12), ScrollBarThickness = 2, AutomaticCanvasSize = Enum.AutomaticSize.Y, CanvasSize = dim2(0, 0, 0, 0), ZIndex = 31, Active = true, Parent = page, }) Library.Theme(list, 'ScrollBarImageColor3', 'accent') New('UIListLayout', { Padding = dim(0, 2), SortOrder = Enum.SortOrder.LayoutOrder, Parent = list }) local heading = New('TextLabel', { Text = '', BackgroundTransparency = 1, Size = dim2(1, 0, 0, 16), TextSize = Library.TextSize, TextXAlignment = Enum.TextXAlignment.Left, LayoutOrder = 0, ZIndex = 32, Parent = list }, { TextColor3 = 'dim' }) local rows = {} local token = 0 local function close() page.Visible = false if self.ActiveTab then self.ActiveTab.Page.Visible = true end end local function refresh() token += 1 local mine = token task.delay(0.12, function() if mine ~= token or Library.Unloaded then return end local needle = plain(box.Text):lower() for _, row in ipairs(rows) do row:Destroy() end table.clear(rows) if needle == '' then close() return end local count = 0 for _, entry in ipairs(collect()) do local element = entry.element local text = plain(element.Text) if SEARCHABLE[element.Type] and text ~= '' and text:lower():find(needle, 1, true) then count += 1 if count <= 80 then local row = New('TextButton', { Text = '', AutoButtonColor = false, BackgroundTransparency = 1, Size = dim2(1, 0, 0, 20), LayoutOrder = count, ZIndex = 32, Parent = list }) local crumb = string.format('%s > %s', entry.tab.Name, tostring(entry.boxName)) New('TextLabel', { Text = text, BackgroundTransparency = 1, Size = dim2(0.5, 0, 1, 0), Position = dim2(0, 8, 0, 0), TextSize = Library.TextSize, TextXAlignment = Enum.TextXAlignment.Left, ZIndex = 33, Parent = row }) New('TextLabel', { Text = crumb, BackgroundTransparency = 1, Size = dim2(0.5, -16, 1, 0), Position = dim2(0.5, 8, 0, 0), TextSize = Library.TextSize, TextXAlignment = Enum.TextXAlignment.Right, ZIndex = 33, Parent = row, }, { TextColor3 = 'dim' }) Connect(row.MouseEnter, function() row.BackgroundColor3 = Palette.accent Tween(row, { BackgroundTransparency = 0.85 }, 0.08) end) Connect(row.MouseLeave, function() Tween(row, { BackgroundTransparency = 1 }, 0.1) end) Connect(row.MouseButton1Click, function() box.Text = '' close() jump(entry) end) table.insert(rows, row) end end end heading.Text = count == 0 and 'no matches' or (count > 80 and ('showing 80 of ' .. count .. ' matches') or (count .. ' match' .. (count == 1 and '' or 'es'))) if self.ActiveTab then self.ActiveTab.Page.Visible = false end page.Visible = true end) end Connect(box:GetPropertyChangedSignal('Text'), refresh) Connect(box.FocusLost, function(entered) if entered and box.Text == '' then close() end end) -- changing tab leaves the search local previousHook = Library.OnTabChanged Library.OnTabChanged = function(tab) if page.Visible and not box:IsFocused() then page.Visible = false box.Text = '' end if previousHook then previousHook(tab) end end end end -- ==== 44_watermark.lua ==== -- ZkxVader watermark (Vaderhaxx's): "[ ZkxVader ] fps: 144 ping: 32ms" - the name in a flowing rainbow gradient, the details after it, draggable. -- The details are a list of fields the user picks (Library:SetWatermarkFields): Username, Executor, FPS, Ping, Map, Server, Time. Values show in the accent colour. -- Library:SetWatermarkProvider(id, getter, interval) swaps how one field is read (the hub gives Map and Server their Rivals lookups); a getter returns a string, or nil to leave the field out. -- It stays on screen while enabled (also with the menu closed). Library:SetWatermarkVisible(false) hides it and stops its counter. do local Stats = svc('Stats') local watermark = { Frame = nil, Name = nil, Info = nil, Visible = true, Selected = { FPS = true, Ping = true } } local renderHandle local liveFps, livePing = 0, 0 local function readPing() local ok, value = pcall(function() return Stats.Network.ServerStatsItem['Data Ping']:GetValue() end) if ok and type(value) == 'number' then return floor(value + 0.5) end local okPing, ping = pcall(function() return LocalPlayer:GetNetworkPing() * 1000 end) return okPing and floor(ping + 0.5) or 0 end local executorName local function readExecutor() if executorName == nil then executorName = 'unknown' if type(identifyexecutor) == 'function' then local ok, name = pcall(identifyexecutor) if ok and type(name) == 'string' and name ~= '' then executorName = name end end end return executorName end -- display order is the order of this list; Interval = seconds a value is kept before the getter runs again (none = every refresh) local FIELD_ORDER = { 'Username', 'Executor', 'FPS', 'Ping', 'Map', 'Server', 'Time' } local fields = { Username = { Label = 'user', Get = function() return LocalPlayer.Name end }, Executor = { Label = 'exec', Get = readExecutor }, FPS = { Label = 'fps', Get = function() return tostring(liveFps) end }, Ping = { Label = 'ping', Get = function() return livePing .. 'ms' end }, Map = { Label = 'map', Get = function() return nil end, Interval = 2 }, Server = { Label = 'server', Get = function() return nil end, Interval = 5 }, Time = { Label = 'time', Get = function() return os.date('%H:%M:%S') end }, } local function readField(field, now) if field.Interval and field.ReadAt and now - field.ReadAt < field.Interval then return field.Value end field.ReadAt = now local ok, value = pcall(field.Get) field.Value = (ok and type(value) == 'string' and value ~= '') and value or nil return field.Value end local function escapeRich(text) return (text:gsub('&', '&'):gsub('<', '<'):gsub('>', '>')) end local function renderInfo() if not watermark.Info then return end local now = os.clock() local color = Palette.accent:ToHex() local parts = {} for _, id in ipairs(FIELD_ORDER) do if watermark.Selected[id] then local field = fields[id] local value = readField(field, now) if value then parts[#parts + 1] = ('%s: %s'):format(field.Label, color, escapeRich(value)) end end end watermark.Info.Text = #parts > 0 and (' ] ' .. table.concat(parts, ' ')) or ' ]' end -- notifications start below the watermark while it is up in the same corner local function moveNotifications() Library.WatermarkOffset = (watermark.Frame and watermark.Visible) and 30 or 0 if Library.LayoutNotifications then Library:LayoutNotifications() end end local function stopCounter() if renderHandle then renderHandle.Disconnect() renderHandle = nil end end local function startCounter() if renderHandle or not watermark.Info then return end local frames, elapsed = 0, 0 renderHandle = Library:OnInput('Render', function(dt) frames += 1 elapsed += dt if elapsed >= 0.5 then liveFps, livePing = floor(frames / elapsed + 0.5), readPing() frames, elapsed = 0, 0 renderInfo() end end) end function Library:AddWatermark(info) info = info or {} Library:EnsureGui() if watermark.Frame and watermark.Frame.Parent then return watermark end local outer = New('Frame', { Name = 'Watermark', AutomaticSize = Enum.AutomaticSize.XY, Size = dim2(0, 0, 0, 0), Position = info.Position or dim2(0, 14, 0, 14), ZIndex = 45, Parent = Library.Main, }, { BackgroundColor3 = 'c' }) New('UIPadding', { PaddingTop = dim(0, 1), PaddingBottom = dim(0, 1), PaddingLeft = dim(0, 1), PaddingRight = dim(0, 1), Parent = outer }) local inner = New('Frame', { AutomaticSize = Enum.AutomaticSize.XY, Size = dim2(0, 0, 0, 0), Parent = outer }, { BackgroundColor3 = 'e' }) New('UIPadding', { PaddingLeft = dim(0, 6), PaddingRight = dim(0, 6), PaddingTop = dim(0, 3), PaddingBottom = dim(0, 4), Parent = inner }) New('UIListLayout', { FillDirection = Enum.FillDirection.Horizontal, SortOrder = Enum.SortOrder.LayoutOrder, VerticalAlignment = Enum.VerticalAlignment.Center, Parent = inner }) local function piece(text, order) return New('TextLabel', { Text = text, BackgroundTransparency = 1, AutomaticSize = Enum.AutomaticSize.X, Size = dim2(0, 0, 0, 14), TextSize = Library.TextSize, LayoutOrder = order, ZIndex = 46, Parent = inner }) end piece('[ ', 1) watermark.Name = piece(tostring(info.Name or 'ZkxVader'), 2) watermark.Info = piece(' ]', 3) watermark.Info.RichText = true FX.Rainbow(watermark.Name, { always = true }) watermark.Frame = outer Library:ApplyLayout('watermark', outer) Library.MakeDraggable(outer, outer, nil, 'watermark') watermark.Visible = info.Visible ~= false outer.Visible = watermark.Visible renderInfo() if watermark.Visible then startCounter() end moveNotifications() return watermark end function Library:SetWatermarkVisible(visible) watermark.Visible = visible ~= false if watermark.Frame then watermark.Frame.Visible = watermark.Visible end if watermark.Visible then startCounter() else stopCounter() end moveNotifications() end -- the ids of the fields in display order (the hub builds its picker from this) function Library:GetWatermarkFieldIds() return table.clone(FIELD_ORDER) end -- list of ids ({ 'FPS', 'Ping' }) or a multi-dropdown value ({ FPS = true }); unknown ids are ignored, an empty list shows the name only function Library:SetWatermarkFields(list) local selected = {} for key, value in pairs(type(list) == 'table' and list or {}) do local id = type(key) == 'number' and value or (value == true and key or nil) if type(id) == 'string' and fields[id] then selected[id] = true end end watermark.Selected = selected renderInfo() end function Library:SetWatermarkProvider(id, getter, interval) local field = fields[id] if not field or type(getter) ~= 'function' then return false end field.Get, field.Interval, field.ReadAt, field.Value = getter, interval or field.Interval, nil, nil renderInfo() return true end -- Obsidian's name for the text: here it is the part inside the brackets function Library:SetWatermark(text) if watermark.Name then watermark.Name.Text = tostring(text) end end Library.Watermark = watermark end -- ==== 45_layout.lua ==== -- ZkxVader layout memory: where the movable windows (main window, keybind panel, watermark, floating labels) were left, kept across unload / reload. -- One small JSON file (/layout.json), written a moment after a drag or resize ends and when the library unloads. Positions are stored as -- UDim2 parts (scale + offset), so they survive a different screen size; a saved spot that would be off the screen is ignored. do local path = Library.StorageDir .. '/layout.json' local data, dirty = nil, false local saveToken = 0 local function canFile() return type(isfile) == 'function' and type(readfile) == 'function' and type(writefile) == 'function' end local function load() if data then return data end data = {} if canFile() then local ok, decoded = pcall(function() if isfile(path) then return HttpService:JSONDecode(readfile(path)) end return nil end) if ok and type(decoded) == 'table' then data = decoded end end return data end local function flush() if not dirty or not canFile() then return end dirty = false pcall(function() if type(isfolder) == 'function' and type(makefolder) == 'function' and not isfolder(Library.StorageDir) then makefolder(Library.StorageDir) end writefile(path, HttpService:JSONEncode(data)) end) end Library:OnUnload(flush) local function number(value) return type(value) == 'number' and value == value and math.abs(value) < 1e6 end local function udim2(parts) if type(parts) == 'table' and number(parts[1]) and number(parts[2]) and number(parts[3]) and number(parts[4]) then return UDim2.new(parts[1], parts[2], parts[3], parts[4]) end return nil end -- is the saved spot still reachable on this screen? (a window left in the corner of a bigger monitor must not vanish on a smaller one) local function reachable(frame, position) local main = Library.Main if not main or main.AbsoluteSize.X <= 0 then return true end local scale = max(Library.GetScale(), 0.01) local view = main.AbsoluteSize / scale local size = frame.AbsoluteSize / scale if size.X <= 0 then size = Vector2.new(120, 24) end local left = position.X.Scale * view.X + position.X.Offset - frame.AnchorPoint.X * size.X local top = position.Y.Scale * view.Y + position.Y.Offset - frame.AnchorPoint.Y * size.Y return left + size.X > 60 and left < view.X - 60 and top + size.Y > 24 and top < view.Y - 40 end function Library:ApplyLayout(id, frame, withSize) local entry = id and frame and load()[id] if type(entry) ~= 'table' then return false end local position = udim2(entry.p) if not position or not reachable(frame, position) then return false end frame.Position = position if withSize then local size = udim2(entry.s) local minimumWidth, minimumHeight = Library.IsMobile and 320 or 560, Library.IsMobile and 200 or 380 local screen = Library.Viewport() if size and size.X.Offset >= minimumWidth and size.Y.Offset >= minimumHeight and size.X.Scale == 0 and size.Y.Scale == 0 then frame.Size = UDim2.fromOffset(min(size.X.Offset, screen.X - 16), min(size.Y.Offset, screen.Y - 16)) end end return true end function Library:SaveLayout(id, frame, withSize) if not id or not frame or not frame.Parent then return end local position = frame.Position local entry = { p = { position.X.Scale, position.X.Offset, position.Y.Scale, position.Y.Offset } } if withSize then local size = frame.Size entry.s = { size.X.Scale, size.X.Offset, size.Y.Scale, size.Y.Offset } end load()[id] = entry dirty = true saveToken += 1 local mine = saveToken task.delay(0.4, function() if mine == saveToken then flush() end end) end end -- ==== 46_mobile.lua ==== -- ZkxVader mobile: an on-screen button that opens / closes the menu (a phone has no menu key). Draggable, remembered, only built on touch devices. do local Window = Classes.Window function Window:BuildMobileButton() if not Library.IsMobile then return end local size = H(44) local outer, inner = Panel(Library.Main, { 'a', 'b', 'e' }, { Size = UDim2.fromOffset(size, size), Position = dim2(0, 14, 0, 70) }) outer.Name = 'MobileButton' outer.ZIndex = 300 Library:ApplyLayout('mobile', outer) local icon if self.Info and self.Info.Icon then icon = New('ImageLabel', { BackgroundTransparency = 1, Image = tostring(self.Info.Icon), Size = dim2(1, -10, 1, -10), Position = dim2(0, 5, 0, 5), ZIndex = 301, Parent = inner }) else New('TextLabel', { Text = 'Z', BackgroundTransparency = 1, Size = dim2(1, 0, 1, 0), TextSize = Library.TextSize + 6, ZIndex = 301, Parent = inner }, { TextColor3 = 'accent' }) end local button = New('TextButton', { Text = '', AutoButtonColor = false, BackgroundTransparency = 1, Size = dim2(1, 0, 1, 0), ZIndex = 302, Active = true, Parent = inner }) Library.MakeDraggable(outer, button, nil, 'mobile') -- a tap opens or closes the menu; a drag only moves the button local pressedAt Connect(button.InputBegan, function(input) if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then pressedAt = outer.AbsolutePosition end end) Connect(button.MouseButton1Click, function() if pressedAt and (outer.AbsolutePosition - pressedAt).Magnitude > 8 then return end Library:Toggle() end) -- the button shows the state: accent outline while the menu is open Library.OnToggled = (function(previous) return function(open) outer.BackgroundColor3 = open and Palette.accent or Palette.a if previous then previous(open) end end end)(Library.OnToggled) self.MobileButton = outer end end -- ==== 47_list.lua ==== -- ZkxVader list: a scrolling single-select list. The chosen row is outlined in green (outline only). Same contract as a dropdown: -- Value (the chosen row text), SetValues, SetValue, OnChanged, SetText, SetVisible, SetDisabled. Row text may use rich text. -- Serverhop uses it; the Obsidian build has no such widget and the hub falls back to a dropdown there. do local Container = Classes.Container local function noop() end local SELECT_COLOR = rgb(60, 220, 110) function Container:AddList(idx, info) info = type(info) == 'table' and info or {} local owner = self local list = { Type = 'List', Text = type(info.Text) == 'string' and info.Text or nil, Values = {}, Value = nil, Tooltip = info.Tooltip, Callback = info.Callback or noop, Changed = info.Changed or noop, Disabled = info.Disabled == true, Visible = info.Visible ~= false, Library = Library, } local height = tonumber(info.Height) or 160 local hasLabel = list.Text ~= nil local row = New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, height + (hasLabel and 18 or 2)), LayoutOrder = self:NextOrder(), Visible = list.Visible, Parent = self.Container }) local title = New('TextLabel', { Text = list.Text or '', BackgroundTransparency = 1, Size = dim2(1, 0, 0, 14), TextSize = Library.TextSize, TextXAlignment = Enum.TextXAlignment.Left, RichText = true, Visible = hasLabel, Parent = row }) local frame = New('Frame', { BackgroundTransparency = 1, AnchorPoint = vec2(0, 1), Position = dim2(0, 0, 1, 0), Size = dim2(1, 0, 0, height), Parent = row }) local _, field = Panel(frame, { 'd', 'b' }, { Size = dim2(1, 0, 1, 0), Clip = true }) local scroller = New('ScrollingFrame', { BackgroundTransparency = 1, ScrollBarThickness = 3, AutomaticCanvasSize = Enum.AutomaticSize.Y, CanvasSize = dim2(0, 0, 0, 0), Size = dim2(1, 0, 1, 0), Active = true, Parent = field, }) Library.Theme(scroller, 'ScrollBarImageColor3', 'accent') New('UIListLayout', { SortOrder = Enum.SortOrder.LayoutOrder, Padding = dim(0, 2), Parent = scroller }) New('UIPadding', { PaddingLeft = dim(0, 3), PaddingRight = dim(0, 5), PaddingTop = dim(0, 3), PaddingBottom = dim(0, 3), Parent = scroller }) local empty = New('TextLabel', { Text = type(info.Empty) == 'string' and info.Empty or 'Nothing to show', BackgroundTransparency = 1, Size = dim2(1, 0, 0, 24), TextSize = Library.TextSize, ZIndex = 2, Parent = field, }, { TextColor3 = 'dim' }) list.Holder, list.Container = row, self.Container local buttons = {} local function paint() for _, entry in ipairs(buttons) do local chosen = entry.Value == list.Value entry.Stroke.Enabled = chosen entry.Button.BackgroundTransparency = chosen and 0.86 or 1 entry.Button.BackgroundColor3 = chosen and SELECT_COLOR or Palette.g end end local function build() for _, entry in ipairs(buttons) do entry.Button:Destroy() end table.clear(buttons) for index, value in ipairs(list.Values) do local button = New('TextButton', { Text = tostring(value), AutoButtonColor = false, BackgroundTransparency = 1, Size = dim2(1, 0, 0, H(20)), TextSize = Library.TextSize, TextXAlignment = Enum.TextXAlignment.Left, RichText = true, LayoutOrder = index, Parent = scroller, }) New('UIPadding', { PaddingLeft = dim(0, 6), Parent = button }) local stroke = New('UIStroke', { Thickness = 1, Color = SELECT_COLOR, ApplyStrokeMode = Enum.ApplyStrokeMode.Border, Enabled = false, Parent = button }) local entry = { Value = value, Button = button, Stroke = stroke } table.insert(buttons, entry) Connect(button.MouseEnter, function() if entry.Value ~= list.Value then Tween(button, { BackgroundTransparency = 0.9 }, 0.08) end end) Connect(button.MouseLeave, function() if entry.Value ~= list.Value then Tween(button, { BackgroundTransparency = 1 }, 0.1) end end) Connect(button.MouseButton1Click, function() if list.Disabled then return end list:SetValue(entry.Value) end) end empty.Visible = #list.Values == 0 paint() end function list:RunChanged() Library:SafeCallback(list.Callback, list.Value) Library:SafeCallback(list.Changed, list.Value) end function list:OnChanged(callback) list.Changed = callback end function list:SetValue(value) if value ~= nil and not table.find(list.Values, value) then return end if list.Value == value then return end list.Value = value paint() list:RunChanged() end function list:SetValues(values) list.Values = type(values) == 'table' and values or {} -- the pick survives a refresh when its row is still there if list.Value ~= nil and not table.find(list.Values, list.Value) then list.Value = nil build() list:RunChanged() return end build() end function list:GetValue() return list.Value end function list:SetText(text) list.Text = text title.Text = text or '' title.Visible = text ~= nil row.Size = dim2(1, 0, 0, height + (text and 18 or 2)) end function list:SetVisible(visible) list.Visible = visible row.Visible = visible end function list:SetDisabled(disabled) list.Disabled = disabled scroller.Active = not disabled title.TextTransparency = disabled and 0.5 or 0 end function list:Destroy() list.Destroyed = true row:Destroy() if Library.Options[idx] == list then Library.Options[idx] = nil end local at = table.find(owner.Elements or {}, list) if at then table.remove(owner.Elements, at) end end if type(info.Values) == 'table' then list.Values = info.Values end build() if info.Default ~= nil and table.find(list.Values, info.Default) then list.Value = info.Default; paint() end if list.Disabled then list:SetDisabled(true) end table.insert(self.Elements, list) if idx ~= nil then Library.Options[idx] = list end return list end end -- ==== 50_misc.lua ==== -- ZkxVader misc: notifications, dialog, draggable label. do local Window, Dialog, Container = Classes.Window, Classes.Dialog, Classes.Container --// notifications \\-- local holder local counter = 0 -- where the stack lives (Trello #32) and how a notification looks (Trello #33); both are remembered by UI Settings local PLACEMENTS = { ['Bottom Left'] = { Left = true, Vertical = Enum.VerticalAlignment.Bottom }, ['Bottom Right'] = { Left = false, Vertical = Enum.VerticalAlignment.Bottom }, ['Top Left'] = { Left = true, Vertical = Enum.VerticalAlignment.Top }, ['Top Right'] = { Left = false, Vertical = Enum.VerticalAlignment.Top }, ['Middle Left'] = { Left = true, Vertical = Enum.VerticalAlignment.Center }, ['Middle Right'] = { Left = false, Vertical = Enum.VerticalAlignment.Center }, } Library.NotifyPlacementNames = { 'Bottom Left', 'Bottom Right', 'Top Left', 'Top Right', 'Middle Left', 'Middle Right' } Library.NotifyStyleNames = { 'Premium', 'Classic' } Library.NotifyPlacement = PLACEMENTS[Library.NotifyPlacement] and Library.NotifyPlacement or 'Top Right' Library.NotifyStyle = Library.NotifyStyle == 'Classic' and 'Classic' or 'Premium' local function placement() return PLACEMENTS[Library.NotifyPlacement] or PLACEMENTS['Top Right'] end -- the stack sits below the watermark while that is up in the top-left corner function Library:LayoutNotifications() local frame = Library.NotifyHolder if not (frame and frame.Parent) then return end local info = placement() local top = Library.NotifyPlacement == 'Top Left' and (Library.WatermarkOffset or 0) or 0 frame.AnchorPoint = vec2(info.Left and 0 or 1, 0) frame.Position = dim2(info.Left and 0 or 1, info.Left and 14 or -14, 0, 14 + top) frame.Size = dim2(0, min(300, Library.Viewport().X - 28), 1, -(28 + top)) local layout = frame:FindFirstChildOfClass('UIListLayout') if layout then layout.HorizontalAlignment = info.Left and Enum.HorizontalAlignment.Left or Enum.HorizontalAlignment.Right layout.VerticalAlignment = info.Vertical end end function Library:SetNotifyPlacement(name) if PLACEMENTS[name] == nil then return false end Library.NotifyPlacement = name Library:LayoutNotifications() return true end function Library:SetNotifyStyle(name) if name ~= 'Premium' and name ~= 'Classic' then return false end Library.NotifyStyle = name return true end local function ensureHolder() Library:EnsureGui() if holder and holder.Parent then return holder end holder = New('Frame', { Name = 'Notifications', BackgroundTransparency = 1, Size = dim2(0, 300, 1, -28), ZIndex = 200, Parent = Library.Main }) New('UIListLayout', { Padding = dim(0, 6), SortOrder = Enum.SortOrder.LayoutOrder, Parent = holder }) Library.NotifyHolder = holder Library:LayoutNotifications() return holder end -- numbers read in the accent colour, like the coloured values of a hit log (only plain text; markup is left alone) local function highlight(text) if #text > 160 or text:find('<', 1, true) then return text end local hex = Palette.accent:ToHex() return (text:gsub('%f[%w]%d[%d%.,]*%%?', function(number) return ('%s'):format(hex, number) end)) end -- the old look: three nested outlines, scrambled title, dim description, countdown bar local function buildClassic(data, wrapper) local panel, inner = Panel(wrapper, { 'a', 'b', 'e' }, { Size = dim2(1, 0, 0, 0), Auto = true }) New('UIListLayout', { Padding = dim(0, 3), SortOrder = Enum.SortOrder.LayoutOrder, Parent = inner }) New('UIPadding', { PaddingTop = dim(0, 6), PaddingBottom = dim(0, 8), PaddingLeft = dim(0, 8), PaddingRight = dim(0, 8), Parent = inner }) local parts = { Panel = panel } if data.Title then parts.Title = New('TextLabel', { Text = data.Title, BackgroundTransparency = 1, Size = dim2(1, 0, 0, 14), TextSize = Library.TextSize, TextXAlignment = Enum.TextXAlignment.Left, LayoutOrder = 1, RichText = true, ZIndex = 201, Parent = inner, }, { TextColor3 = 'accent' }) end parts.Body = New('TextLabel', { Text = data.Description, BackgroundTransparency = 1, Size = dim2(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, TextSize = Library.TextSize, TextWrapped = true, TextXAlignment = Enum.TextXAlignment.Left, TextYAlignment = Enum.TextYAlignment.Top, LayoutOrder = 2, RichText = true, ZIndex = 201, Parent = inner, }, { TextColor3 = data.Title and 'dim' or 'text' }) parts.BarHolder = New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, 2), LayoutOrder = 3, Parent = inner }) parts.Bar = New('Frame', { Size = dim2(1, 0, 1, 0), Parent = parts.BarHolder }, { BackgroundColor3 = 'accent' }) parts.Hidden = function(left) return { Position = dim2(left and -1.15 or 1.15, 0, 0, 0) } end parts.Shown = { Position = dim2(0, 0, 0, 0) } parts.Scramble = true return parts end -- premium: one translucent card, a 2 px accent edge on the screen side, accent header over a hairline, white text with -- coloured numbers, a 1 px countdown line, fades and slides in as one piece (CanvasGroup) local function buildPremium(data, wrapper, left) local card = New('CanvasGroup', { BackgroundTransparency = 1, BorderSizePixel = 0, Size = dim2(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, GroupTransparency = 0, ZIndex = 201, Parent = wrapper, }) local _, face = Panel(card, { 'b', 'd' }, { Size = dim2(1, 0, 0, 0), Auto = true, InnerTransparency = 0.08 }) local stripe = New('Frame', { Size = dim2(0, 2, 1, 0), Position = dim2(left and 0 or 1, left and 0 or -2, 0, 0), ZIndex = 3, Parent = face }, { BackgroundColor3 = 'accent' }) local inner = New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, ZIndex = 2, Parent = face }) New('UIListLayout', { Padding = dim(0, 4), SortOrder = Enum.SortOrder.LayoutOrder, Parent = inner }) New('UIPadding', { PaddingTop = dim(0, 6), PaddingBottom = dim(0, 7), PaddingLeft = dim(0, left and 11 or 9), PaddingRight = dim(0, left and 9 or 11), Parent = inner, }) local parts = { Panel = card, Stripe = stripe } if data.Title then parts.Title = New('TextLabel', { Text = data.Title, BackgroundTransparency = 1, Size = dim2(1, 0, 0, 13), TextSize = Library.TextSize + 1, TextXAlignment = Enum.TextXAlignment.Left, LayoutOrder = 1, RichText = true, ZIndex = 4, Parent = inner, }, { TextColor3 = 'accent' }) New('Frame', { Size = dim2(1, 0, 0, 1), LayoutOrder = 2, BackgroundTransparency = 0.35, ZIndex = 3, Parent = inner }, { BackgroundColor3 = 'b' }) end parts.Body = New('TextLabel', { Text = highlight(data.Description), BackgroundTransparency = 1, Size = dim2(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, TextSize = Library.TextSize, TextWrapped = true, TextXAlignment = Enum.TextXAlignment.Left, TextYAlignment = Enum.TextYAlignment.Top, LayoutOrder = 3, RichText = true, ZIndex = 4, Parent = inner, }, { TextColor3 = 'text' }) parts.BarHolder = New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, 1), LayoutOrder = 4, ZIndex = 3, Parent = inner }) parts.Bar = New('Frame', { Size = dim2(1, 0, 1, 0), BackgroundTransparency = 0.2, ZIndex = 4, Parent = parts.BarHolder }, { BackgroundColor3 = 'accent' }) parts.Hidden = function(isLeft) return { Position = dim2(0, isLeft and -26 or 26, 0, 0), GroupTransparency = 1 } end parts.Shown = { Position = dim2(0, 0, 0, 0), GroupTransparency = 0 } parts.Highlight = true return parts end function Library:Notify(...) if Library.Unloaded then return nil end local data = {} local first = select(1, ...) if type(first) == 'table' then data.Title = first.Title ~= nil and tostring(first.Title) or nil data.Description = first.Description ~= nil and tostring(first.Description) or '' data.Time = first.Time or 5 data.Persist = first.Persist data.TitleColor, data.DescriptionColor = first.TitleColor, first.DescriptionColor else data.Description = tostring(first) data.Time = select(2, ...) or 5 end local left = placement().Left local parent = ensureHolder() counter += 1 local wrapper = New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, LayoutOrder = counter, Parent = parent }) local parts = Library.NotifyStyle == 'Classic' and buildClassic(data, wrapper) or buildPremium(data, wrapper, left) local panel, titleLabel, body, barHolder, bar = parts.Panel, parts.Title, parts.Body, parts.BarHolder, parts.Bar if data.TitleColor and titleLabel then titleLabel.TextColor3 = data.TitleColor end if data.DescriptionColor then body.TextColor3 = data.DescriptionColor end local notification = { Destroyed = false } local dismissed = false function notification:Destroy() if dismissed then return end dismissed = true notification.Destroyed = true if not wrapper.Parent then return end local height = wrapper.AbsoluteSize.Y / max(Library.GetScale(), 0.01) if Animating() then wrapper.AutomaticSize = Enum.AutomaticSize.None wrapper.Size = dim2(1, 0, 0, height) Tween(panel, parts.Hidden(placement().Left), 0.28, Enum.EasingStyle.Quint, Enum.EasingDirection.In) task.delay(0.22, function() if wrapper.Parent then Tween(wrapper, { Size = dim2(1, 0, 0, 0) }, 0.18, Enum.EasingStyle.Quint) end end) task.delay(0.45, function() wrapper:Destroy() end) else wrapper:Destroy() end end function notification:ChangeTitle(text) if titleLabel then titleLabel.Text = tostring(text) end end function notification:ChangeDescription(text) text = tostring(text) body.Text = parts.Highlight and highlight(text) or text end -- slide in, count down if Animating() then for property, value in pairs(parts.Hidden(left)) do panel[property] = value end Tween(panel, parts.Shown, parts.Scramble and 0.45 or 0.32, Enum.EasingStyle.Quint) if parts.Scramble then if titleLabel then FX.Scramble(titleLabel, data.Title, 0.35) FX.Glitch(titleLabel, 0.2) elseif #data.Description <= 70 and not data.Description:find('<') then FX.Scramble(body, data.Description, 0.3) end end else for property, value in pairs(parts.Shown) do panel[property] = value end end local duration = tonumber(data.Time) if data.Persist or not duration then barHolder.Visible = false if typeof(data.Time) == 'Instance' then data.Time.Destroying:Once(function() notification:Destroy() end) end else Tween(bar, { Size = dim2(0, 0, 1, 0) }, duration, Enum.EasingStyle.Linear) task.delay(duration, function() notification:Destroy() end) end return notification end --// dialog \\-- local VARIANTS = { Primary = 'accent', Destructive = 'risky', Ghost = 'text' } function Window:AddDialog(idx, info) info = info or {} local settings = { Title = info.Title or 'Dialog', Description = info.Description or '', AutoDismiss = info.AutoDismiss ~= false, OutsideClickDismiss = info.OutsideClickDismiss ~= false, FooterButtons = info.FooterButtons or {}, } Library:EnsureGui() Library.Dialogs = Library.Dialogs or {} local existing = Library.Dialogs[idx] if existing then existing:Dismiss(true) end local layer = New('TextButton', { Text = '', AutoButtonColor = false, Size = dim2(1, 0, 1, 0), BackgroundColor3 = rgb(0, 0, 0), BackgroundTransparency = 1, ZIndex = 150, Active = true, Parent = Library.Main, }) Tween(layer, { BackgroundTransparency = 0.45 }, 0.2) local panel, inner = Panel(layer, { 'a', 'b', 'c', 'e' }, { Size = dim2(0, min(440, Library.Viewport().X - 24), 0, 0), Auto = true, AnchorPoint = vec2(0.5, 0.5), Position = dim2(0.5, 0, 0.5, 0) }) panel.ZIndex = 151 panel.AutomaticSize = Enum.AutomaticSize.Y New('UIListLayout', { Padding = dim(0, 8), SortOrder = Enum.SortOrder.LayoutOrder, Parent = inner }) New('UIPadding', { PaddingTop = dim(0, 12), PaddingBottom = dim(0, 12), PaddingLeft = dim(0, 14), PaddingRight = dim(0, 14), Parent = inner }) local title = New('TextLabel', { Text = settings.Title, BackgroundTransparency = 1, Size = dim2(1, 0, 0, 16), TextSize = 14, TextXAlignment = Enum.TextXAlignment.Left, LayoutOrder = 1, ZIndex = 152, Parent = inner }, { TextColor3 = 'accent' }) New('TextLabel', { Text = settings.Description, BackgroundTransparency = 1, Size = dim2(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, TextSize = Library.TextSize, TextWrapped = true, TextXAlignment = Enum.TextXAlignment.Left, TextYAlignment = Enum.TextYAlignment.Top, LayoutOrder = 2, ZIndex = 152, Visible = settings.Description ~= '', Parent = inner, }, { TextColor3 = 'dim' }) local content = New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, LayoutOrder = 3, ZIndex = 152, Parent = inner }) New('UIListLayout', { Padding = dim(0, 6), SortOrder = Enum.SortOrder.LayoutOrder, Parent = content }) local footer = New('Frame', { BackgroundTransparency = 1, Size = dim2(1, 0, 0, H(22)), LayoutOrder = 4, ZIndex = 152, Parent = inner }) New('UIListLayout', { FillDirection = Enum.FillDirection.Horizontal, HorizontalAlignment = Enum.HorizontalAlignment.Right, Padding = dim(0, 6), SortOrder = Enum.SortOrder.LayoutOrder, Parent = footer }) local dialog = setmetatable({ Library = Library, Container = content, Elements = {}, Type = 'Dialog', Index = idx, Dismissed = false, Panel = panel, Layer = layer }, Dialog) function dialog:Dismiss(instant) if dialog.Dismissed then return end dialog.Dismissed = true Library:ClosePopup() if Library.Dialogs[idx] == dialog then Library.Dialogs[idx] = nil end for index = #dialog.Elements, 1, -1 do local element = dialog.Elements[index] if element and element.Destroy then pcall(element.Destroy, element) end end if instant ~= true and Animating() then Tween(layer, { BackgroundTransparency = 1 }, 0.15) task.delay(0.16, function() layer:Destroy() end) else layer:Destroy() end end local ordered = {} for name, button in pairs(settings.FooterButtons) do table.insert(ordered, { Name = name, Info = button }) end table.sort(ordered, function(a, b) return (a.Info.Order or 0) < (b.Info.Order or 0) end) for index, entry in ipairs(ordered) do local variantKey = VARIANTS[entry.Info.Variant or 'Ghost'] or 'text' local text = tostring(entry.Info.Title or entry.Name) local outer, face = Panel(footer, { variantKey == 'text' and 'a' or 'a', 'c' }, { Size = UDim2.fromOffset(TextWidth(text, Library.TextSize) + 22, H(22)) }) outer.LayoutOrder, outer.ZIndex = index, 153 local button = New('TextButton', { Text = text, AutoButtonColor = false, BackgroundTransparency = 1, Size = dim2(1, 0, 1, 0), TextSize = Library.TextSize, ZIndex = 154, Parent = face }) Library.Registry[button] = { TextColor3 = variantKey } button.TextColor3 = Palette[variantKey] Connect(button.MouseEnter, function() Tween(face, { BackgroundColor3 = Palette.g }, 0.1) end) Connect(button.MouseLeave, function() Tween(face, { BackgroundColor3 = Palette.c }, 0.15) end) Connect(button.MouseButton1Click, function() Library:SafeCallback(entry.Info.Callback, dialog) if settings.AutoDismiss and not dialog.Dismissed then dialog:Dismiss() end end) end Connect(layer.MouseButton1Click, function() if settings.OutsideClickDismiss and not IsMouseOver(panel) then dialog:Dismiss() end end) if Animating() then panel.Position = dim2(0.5, 0, 0.5, 14) Tween(panel, { Position = dim2(0.5, 0, 0.5, 0) }, 0.28, Enum.EasingStyle.Quint) FX.Scramble(title, settings.Title, 0.3) end Library.Dialogs[idx] = dialog return dialog end --// draggable label (a small movable text panel) \\-- function Library:AddDraggableLabel(text) Library:EnsureGui() -- the hub restyles `.Label` (TextSize, AnchorPoint, Position) and expects it to float on the screen by itself, as Obsidian's did local label = New('TextLabel', { Name = 'DraggableLabel', Text = tostring(text or ''), BackgroundTransparency = 0.2, AutomaticSize = Enum.AutomaticSize.XY, Size = dim2(0, 0, 0, 0), AnchorPoint = vec2(0.5, 0), Position = dim2(0.5, 0, 0, 90), TextSize = Library.TextSize + 2, RichText = true, ZIndex = 45, Active = true, Parent = Library.Main, }, { BackgroundColor3 = 'e' }) New('UIPadding', { PaddingLeft = dim(0, 12), PaddingRight = dim(0, 12), PaddingTop = dim(0, 6), PaddingBottom = dim(0, 7), Parent = label }) New('UIStroke', { Thickness = 1, ApplyStrokeMode = Enum.ApplyStrokeMode.Border, Parent = label }, { Color = 'c' }) local layoutId = 'label:' .. (tostring(text or ''):match('%a+') or 'label'):lower() Library.MakeDraggable(label, label, nil, layoutId) task.defer(function() if label.Parent then Library:ApplyLayout(layoutId, label) end end) local object = { Frame = label, Label = label, Type = 'DraggableLabel', Visible = true } function object:SetText(value) label.Text = tostring(value) end function object:SetVisible(visible) object.Visible = visible label.Visible = visible end function object:Destroy() label:Destroy() end return object end end -- ==== 99_end.lua ==== -- like Obsidian, publish the instance (the hub's unload path and external tooling look for getgenv().Library) pcall(function() local env = (type(getgenv) == 'function' and getgenv()) or _G env.Library = Library end) -- done: the Library table is the module return Library end -- ====================================================================== -- UI library: config save / load (SaveManager) -- ====================================================================== __ZkhHub_shared.obsidian_save_manager = function() local cloneref = (cloneref or clonereference or function(instance) return instance end) local clonefunction = (clonefunction or copyfunction or function(func) return func end) local HttpService = cloneref(game:GetService('HttpService')) local isfolder, isfile, listfiles = isfolder, isfile, listfiles if typeof(clonefunction) == 'function' then local isfolder_copy, isfile_copy, listfiles_copy = clonefunction(isfolder), clonefunction(isfile), clonefunction(listfiles) local isfolder_success, isfolder_error = pcall( function() return isfolder_copy('test' .. tostring(math.random(1000000, 9999999))) end ) if isfolder_success == false or typeof(isfolder_error) ~= 'boolean' then isfolder = function(folder) local success, data = pcall(isfolder_copy, folder) return (if success then data else false) end isfile = function(file) local success, data = pcall(isfile_copy, file) return (if success then data else false) end listfiles = function(folder) local success, data = pcall(listfiles_copy, folder) return (if success then data else {}) end end end local SaveManager = { Library = nil, Folder = 'ObsidianLibSettings', SubFolder = '', Ignore = {}, LoadingOrder = {}, UseLoadingOrder = false, AutoloadConfig = nil, } function SaveManager:SetLibrary(Library) SaveManager.Library = Library end --// Element Parser \\-- local SpecialValueParser = { UDim2 = { Encode = function(Value) return { X = { Scale = Value.X.Scale, Offset = Value.X.Offset }, Y = { Scale = Value.Y.Scale, Offset = Value.Y.Offset }, } end, Decode = function(Data) local DataType = typeof(Data) if DataType == 'table' then return UDim2.new(Data.X.Scale, Data.X.Offset, Data.Y.Scale, Data.Y.Offset) elseif DataType == 'UDim2' then return Data end return nil end, }, } local ElementParser = {} do local function CreateParser(ElementType, LibaryIndex, Save, Load, CustomElementFetcher) ElementParser[ElementType] = { Save = function(Index, Element, ...) local Data = Save(Index, Element, ...) Data.type = ElementType Data.idx = Index return Data end, Load = function(Index, Data) if CustomElementFetcher == true then return Load(nil, Data) end local Elements = SaveManager.Library and SaveManager.Library[LibaryIndex] local Element = Elements and Elements[Index] return Load(Element, Data) end, } end CreateParser('Toggle', 'Toggles', function(Index, Toggle) return { value = Toggle.Value } end, function(Element, Data) if not Element then return end if Element.Value == Data.value then Element:RunChanged() return end Element:SetValue(Data.value) end) CreateParser('Slider', 'Options', function(Index, Slider) return { value = tostring(Slider.Value) } end, function(Element, Data) if not Element then return end if Element.Value == Data.value then Element:RunChanged() return end Element:SetValue(Data.value) end) CreateParser( 'Dropdown', 'Options', function(Index, Dropdown) return { value = Dropdown.Value, multi = Dropdown.Multi } end, function(Element, Data) if not Element then return end if Element.Value == Data.value then Element:RunChanged() return end Element:SetValue(Data.value) end ) CreateParser( 'ColorPicker', 'Options', function(Index, ColorPicker) return { value = ColorPicker.Value:ToHex(), transparency = ColorPicker.Transparency } end, function(Element, Data) if not Element then return end Element:SetValueRGB(Color3.fromHex(Data.value), Data.transparency) end ) CreateParser( 'KeyPicker', 'Options', function(Index, KeyPicker) return { mode = KeyPicker.Mode, key = KeyPicker.Value, modifiers = KeyPicker.Modifiers, toggled = KeyPicker.Toggled } end, function(Element, Data) if not Element then return end Element:SetValue({ Data.key, Data.mode, Data.modifiers }) if Data.mode == 'Toggle' and Data.toggled ~= nil then Element.Toggled = Data.toggled Element:Update() end end ) CreateParser('Input', 'Options', function(Index, Input) return { text = Input.Value } end, function(Element, Data) if not Element then return end if typeof(Data.text) ~= 'string' then return end if Element.Value == Data.text then Element:RunChanged() return end Element:SetValue(Data.text) end) CreateParser( 'Groupbox', 'Tabs', function(Index, Groupbox, TabIndex) return { collapsed = Groupbox.Collapsed, tabIdx = TabIndex } end, function(_, Data) local TabIndex, Index = Data.tabIdx, Data.idx if typeof(TabIndex) ~= 'string' or typeof(Index) ~= 'string' then return end local Tabs = SaveManager.Library and SaveManager.Library.Tabs local Tab = Tabs and Tabs[TabIndex] if not Tab then return end local Groupbox = Tab.Groupboxes[Index] if not Groupbox or Groupbox.Collapsed == Data.collapsed then return end Groupbox:SetCollapsed(Data.collapsed == true) end, true ) end --// Helpers \\-- local function Trim(Text) return Text:match('^%s*(.-)%s*$') end local function IsStringEmpty(String) return if typeof(String) == 'string' then Trim(String) == '' else true end local function IsValidFolderPath(Name) return typeof(Name) == 'string' and (Trim(Name) ~= '' and not Name:match('^%s*$') and not Name:find('[<>:"|%?%*%z]')) end --// Folder helper \\-- local function SplitPath(Path) local Result = {} local Current = '' for Part in string.gmatch(Path, '[^/]+') do Current = if Current == '' then Part else (Current .. '/' .. Part) table.insert(Result, Current) end return Result end local function GetFolderPath() if IsStringEmpty(SaveManager.Folder) then return false end return string.format('%s/settings', SaveManager.Folder) end local function GetSubFolderPath() if IsStringEmpty(SaveManager.Folder) or IsStringEmpty(SaveManager.SubFolder) then return false end return string.format('%s/settings/%s', SaveManager.Folder, SaveManager.SubFolder) end local function GetCurrentSettingsPath() local SubFolderPath = GetSubFolderPath() return if SubFolderPath == false then GetFolderPath() else SubFolderPath end --// Files helper \\-- local function GetConfigPath(ConfigName) local CurrentSettingsPath = GetCurrentSettingsPath() return if CurrentSettingsPath == false then false else string.format('%s/%s.json', CurrentSettingsPath, ConfigName) end local function DoesConfigExist(ConfigName) local ConfigPath = GetConfigPath(ConfigName) return if ConfigPath == false then false else isfile(ConfigPath) end local function GetAutoloadPath() local CurrentSettingsPath = GetCurrentSettingsPath() return if CurrentSettingsPath == false then false else string.format('%s/autoload.txt', CurrentSettingsPath) end --// Indexes \\-- function SaveManager:SetLoadingOrder(Enabled, Order) SaveManager.UseLoadingOrder = Enabled == true SaveManager.LoadingOrder = typeof(Order) == 'table' and Order or SaveManager.LoadingOrder end function SaveManager:SetIgnoreIndexes(Indexes) assert(typeof(Indexes) == 'table', 'Expected table, got ' .. typeof(Indexes)) for _, Index in Indexes do SaveManager.Ignore[Index] = true end end function SaveManager:IgnoreThemeSettings() SaveManager:SetIgnoreIndexes({ 'BackgroundColor', 'MainColor', 'AccentColor', 'OutlineColor', 'FontColor', 'FontFace', 'BackgroundImage', 'ThemeManager_ThemeList', 'ThemeManager_CustomThemeList', 'ThemeManager_CustomThemeName', }) end --// Folders \\-- function SaveManager:GetPaths() local SubFolderPath = GetSubFolderPath() if SubFolderPath == false then local FolderPath = GetFolderPath() return if FolderPath == false then {} else SplitPath(FolderPath) end return SplitPath(SubFolderPath) end function SaveManager:BuildFolderTree(SkipWhenCreated) local Paths = SaveManager:GetPaths() if #Paths == 0 then return false end if SkipWhenCreated == true then if isfolder(Paths[1]) then return true end end for _, Path in Paths do if isfolder(Path) then continue end makefolder(Path) end return true end function SaveManager:CheckFolderTree() return SaveManager:BuildFolderTree(true) end function SaveManager:CheckSubFolder(CreateFolder) local SubFolderPath = GetSubFolderPath() if SubFolderPath == false then return false end local FolderExists = isfolder(SubFolderPath) if not CreateFolder then return FolderExists end makefolder(SubFolderPath) return true end function SaveManager:SetFolder(Folder) assert(IsValidFolderPath(Folder), 'Invalid path provided') SaveManager.Folder = Folder SaveManager:BuildFolderTree() end function SaveManager:SetSubFolder(SubFolder) assert(IsValidFolderPath(SubFolder), 'Invalid path provided') SaveManager.SubFolder = SubFolder SaveManager:BuildFolderTree() end --// Config Management \\-- function SaveManager:RefreshConfigList() local SettingsPath = GetCurrentSettingsPath() if SettingsPath == false then return {} end local SuccessList, Files = pcall(listfiles, SettingsPath) if not (SuccessList and typeof(Files) == 'table') then SaveManager.Library:Notify(string.format('Failed to load config list: %s', tostring(Files))) return {} end local FileNames = {} for _, FilePath in Files do local RawFileName = FilePath:match('(.+)%..+$') if not RawFileName then continue end local Position = RawFileName:gsub('\\', '/'):find('/[^/]*$') local FileName = Position and RawFileName:sub(Position + 1) or RawFileName if not FileName or FileName == 'autoload' then continue end table.insert(FileNames, FileName) end return FileNames end function SaveManager:Save(ConfigName) if IsStringEmpty(ConfigName) then return false, 'Invalid config name provided' end if string.lower(ConfigName) == 'autoload' then return false, 'Invalid config name provided' end local ConfigPath = GetConfigPath(ConfigName) if ConfigPath == false then return false, 'Invalid config name provided' end SaveManager:CheckFolderTree() local Library = SaveManager.Library local IgnoreIndexes = SaveManager.Ignore local CurrentData = { timestamp = os.date('%d.%m.%Y %H:%M:%S'), name = ConfigName, objects = {}, keybindMenu = if Library.KeybindFrame then { visible = Library.KeybindFrame.Visible, position = SpecialValueParser.UDim2.Encode(Library.KeybindFrame.Position), } else nil, } --// Toggles for Index, Toggle in Library.Toggles do if not Toggle.Type then continue end if IgnoreIndexes[Index] then continue end local Parser = ElementParser[Toggle.Type] if not Parser then continue end table.insert(CurrentData.objects, Parser.Save(Index, Toggle)) end --// Options for Index, Option in Library.Options do if not Option.Type then continue end if IgnoreIndexes[Index] then continue end local Parser = ElementParser[Option.Type] if not Parser then continue end table.insert(CurrentData.objects, Parser.Save(Index, Option)) end --// Groupboxes for TabIndex, Tab in Library.Tabs do if not Tab.Groupboxes then continue end for Index, Groupbox in Tab.Groupboxes do if IgnoreIndexes[Index] then continue end local Parser = ElementParser.Groupbox if not Parser then continue end table.insert(CurrentData.objects, Parser.Save(Index, Groupbox, TabIndex)) end end local SuccessEncode, EncodedData = pcall(HttpService.JSONEncode, HttpService, CurrentData) if not SuccessEncode then return false, 'Failed to encode data' end local SuccessWrite, ErrorMessage = pcall(writefile, ConfigPath, EncodedData) if not SuccessWrite then return false, 'Failed to write config file: ' .. tostring(ErrorMessage) end return true end function SaveManager:Load(ConfigName) if IsStringEmpty(ConfigName) then return false, 'No config is selected' end local ConfigPath = GetConfigPath(ConfigName) if ConfigPath == false or not isfile(ConfigPath) then return false, 'Config file does not exist' end local SuccessRead, Content = pcall(readfile, ConfigPath) if not SuccessRead then return false, 'Failed to read config file' end local SuccessDecode, Decoded = pcall(HttpService.JSONDecode, HttpService, Content) if not SuccessDecode or typeof(Decoded) ~= 'table' or typeof(Decoded.objects) ~= 'table' then return false, 'Failed to decode config data' end local Library = SaveManager.Library local LoadingOrder = SaveManager.LoadingOrder local IgnoreIndexes = SaveManager.Ignore if SaveManager.UseLoadingOrder == true and typeof(LoadingOrder) == 'table' then table.sort(Decoded.objects, function(a, b) local aIndex = table.find(LoadingOrder, a.type) or math.huge local bIndex = table.find(LoadingOrder, b.type) or math.huge return aIndex < bIndex end) end --// Keybind Menu if Library.KeybindFrame and typeof(Decoded.keybindMenu) == 'table' then local KeybindFrameData = Decoded.keybindMenu local IsVisible = KeybindFrameData.visible == true local Position = SpecialValueParser.UDim2.Decode(KeybindFrameData.position) Library.KeybindFrame.Visible = IsVisible -- (ZkxVader build) the keybind panel position is remembered by the library, not by configs local KeybindMenuToggle = Library.Options and Library.Options.KeybindMenuOpen if KeybindMenuToggle then KeybindMenuToggle:SetValue(IsVisible) end end --// Elements for _, Option in Decoded.objects do if not Option.type then continue end if IgnoreIndexes[Option.idx] then continue end local Parser = ElementParser[Option.type] if not Parser then continue end task.defer(Parser.Load, Option.idx, Option) end return true end function SaveManager:Delete(ConfigName) if IsStringEmpty(ConfigName) then return false, 'No config is selected' end local ConfigPath = GetConfigPath(ConfigName) if ConfigPath == false or not isfile(ConfigPath) then return false, 'Config file does not exist' end local SuccessDelete, ErrorMessage = pcall(delfile, ConfigPath) if not SuccessDelete then return false, 'Failed to delete config file: ' .. tostring(ErrorMessage) end if ConfigName == SaveManager.AutoloadConfig then SaveManager:DeleteAutoLoadConfig() end return true end --// Auto Load Config \\-- function SaveManager:GetAutoloadConfig() SaveManager:CheckFolderTree() local AutoloadPath = GetAutoloadPath() if AutoloadPath == false then return 'none', false, 'Invalid path provided' end if not isfile(AutoloadPath) then return 'none', false, 'Autoload config is not set' end local SuccessRead, AutoloadConfigName = pcall(readfile, AutoloadPath) if not (SuccessRead and typeof(AutoloadConfigName) == 'string') then return 'none', false, AutoloadConfigName end local ConfigExists = DoesConfigExist(AutoloadConfigName) if not ConfigExists then return 'none', false, 'Config file not found' end SaveManager.AutoloadConfig = AutoloadConfigName return AutoloadConfigName, true end function SaveManager:SaveAutoloadConfig(ConfigName) if IsStringEmpty(ConfigName) then return false, 'No config is selected' end SaveManager:CheckFolderTree() local AutoloadPath = GetAutoloadPath() if AutoloadPath == false then return false, 'Invalid path provided' end if not DoesConfigExist(ConfigName) then return false, 'Config does not exist' end local SuccessWrite, ErrorMessage = pcall(writefile, AutoloadPath, ConfigName) if not SuccessWrite then return false, ErrorMessage end SaveManager.AutoloadConfig = ConfigName return true end function SaveManager:LoadAutoloadConfig() local ConfigName, Success, FetchErrorMessage = SaveManager:GetAutoloadConfig() if not Success or FetchErrorMessage then if FetchErrorMessage ~= 'Autoload config is not set' then SaveManager.Library:Notify(string.format('Failed to load autoload config: %s', FetchErrorMessage)) end return end local SuccessLoad, LoadErrorMessage = SaveManager:Load(ConfigName) if not SuccessLoad then SaveManager.Library:Notify(string.format('Failed to load autoload config: %s', LoadErrorMessage)) return end end function SaveManager:DeleteAutoLoadConfig() SaveManager:CheckFolderTree() local AutoloadPath = GetAutoloadPath() if AutoloadPath == false then return false, 'Invalid path provided' end if not isfile(AutoloadPath) then return false, 'Autoload config is not set' end local SuccessDelete, ErrorMessage = pcall(delfile, AutoloadPath) if not SuccessDelete then return false, ErrorMessage end SaveManager.AutoloadConfig = nil return true end --// GUI \\-- local function ShowDialog(Condition, Index, Title, Description, DestructiveText, DestructiveAction) if Condition() == false then return DestructiveAction() end return SaveManager.Library.Window:AddDialog(Index, { Title = Title, Description = Description, AutoDismiss = false, FooterButtons = { Cancel = { Title = 'Cancel', Variant = 'Ghost', Order = 1, Callback = function(Dialog) Dialog:Dismiss() end, }, DestructiveAction = { Title = DestructiveText, Variant = 'Destructive', Order = 2, Callback = function(Dialog) Dialog:Dismiss() DestructiveAction() end, }, }, }) end function SaveManager:BuildConfigSection(Tab, IconName) assert(SaveManager.Library, 'Library is not set, call SaveManager:SetLibrary(Library) first.') local ConfigurationBox = Tab:AddRightGroupbox('Configuration', IconName or 'folder-cog') local ConfigNameInput, ConfigList, AutoloadConfigLabel local function RefreshList() ConfigList:SetValues(SaveManager:RefreshConfigList()) ConfigList:SetValue(nil) end local function RefreshAutoloadConfigLabel() local AutoloadConfigName, _Success, _ErrorMessage = SaveManager:GetAutoloadConfig() AutoloadConfigLabel:SetText(string.format('Current autoload config: %s', AutoloadConfigName)) if ConfigList then RefreshList() end end --// Create ConfigurationBox:AddInput('SaveManager_ConfigName', { Text = 'Config name', }) ConfigurationBox:AddButton('Create config', function() local ConfigName = ConfigNameInput.Value if IsStringEmpty(ConfigName) then SaveManager.Library:Notify('Configuration name cannot be empty.') return end if string.lower(ConfigName) == 'autoload' then SaveManager.Library:Notify('Invalid config name provided.') return end ShowDialog( function() return DoesConfigExist(ConfigName) end, 'SaveManager_CreateConfig', 'Config already exists', string.format('A config named %q already exists. Overwriting will replace it with your current settings.', ConfigName), 'Overwrite', function() local Success, ErrorMessage = SaveManager:Save(ConfigName) if not Success then SaveManager.Library:Notify(string.format('Failed to create config %q: %s', ConfigName, ErrorMessage)) return end SaveManager.Library:Notify(string.format('Successfully created config %q', ConfigName)) RefreshList() end ) end) ConfigurationBox:AddDivider() --// Manage ConfigurationBox:AddDropdown('SaveManager_ConfigList', { Text = 'Config list', Values = SaveManager:RefreshConfigList(), AllowNull = true, Multi = false, FormatDisplayValue = function(Value) if Value == SaveManager.AutoloadConfig then return string.format('%s (autoload)', Value) end return Value end, FormatListValue = function(Value) if Value == SaveManager.AutoloadConfig then return string.format('%s (autoload)', Value) end return Value end, }) ConfigurationBox:AddButton({ Text = 'Load config', DoubleClick = false, Func = function() local ConfigName = ConfigList.Value if IsStringEmpty(ConfigName) then SaveManager.Library:Notify('Please select a config first.') return end local Success, ErrorMessage = SaveManager:Load(ConfigName) if not Success then SaveManager.Library:Notify(string.format('Failed to load config %q: %s', ConfigName, ErrorMessage)) return end SaveManager.Library:Notify(string.format('Successfully loaded config %q', ConfigName)) end, }) ConfigurationBox:AddButton({ Text = 'Overwrite config', DoubleClick = false, Func = function() local ConfigName = ConfigList.Value if IsStringEmpty(ConfigName) then SaveManager.Library:Notify('Please select a config first.') return end ShowDialog( function() return true --// Always show end, 'SaveManager_OverwriteConfig', 'Overwrite config', string.format( 'Are you sure you want to overwrite %q with your current settings? This cannot be undone.', ConfigName ), 'Overwrite', function() local Success, ErrorMessage = SaveManager:Save(ConfigName) if not Success then SaveManager.Library:Notify(string.format('Failed to overwrite config %q: %s', ConfigName, ErrorMessage)) return end SaveManager.Library:Notify(string.format('Successfully overwrote config %q', ConfigName)) end ) end, }) ConfigurationBox:AddButton({ Text = 'Delete config', DoubleClick = false, Func = function() local ConfigName = ConfigList.Value if IsStringEmpty(ConfigName) then SaveManager.Library:Notify('Please select a config first.') return end ShowDialog( function() return true --// Always show end, 'SaveManager_DeleteConfig', 'Delete config', string.format('Are you sure you want to delete %q? This cannot be undone.', ConfigName), 'Delete', function() local Success, ErrorMessage = SaveManager:Delete(ConfigName) if not Success then SaveManager.Library:Notify(string.format('Failed to delete config %q: %s', ConfigName, ErrorMessage)) return end SaveManager.Library:Notify(string.format('Successfully deleted config %q', ConfigName)) RefreshAutoloadConfigLabel() end ) end, }) ConfigurationBox:AddButton('Refresh list', RefreshList) --// Autoload Config ConfigurationBox:AddButton({ Text = 'Set as autoload', DoubleClick = false, Func = function() local ConfigName = ConfigList.Value if IsStringEmpty(ConfigName) then SaveManager.Library:Notify('Please select a config first.') return end local Success, ErrorMessage = SaveManager:SaveAutoloadConfig(ConfigName) if not Success then SaveManager.Library:Notify(string.format('Failed to set autoload config %q: %s', ConfigName, ErrorMessage)) return end SaveManager.Library:Notify(string.format('Successfully set autoload config to %q', ConfigName)) RefreshAutoloadConfigLabel() end, }) ConfigurationBox:AddButton({ Text = 'Reset autoload', DoubleClick = false, Func = function() ShowDialog( function() return true --// Always show end, 'SaveManager_ResetAutoload', 'Reset autoload config', 'Are you sure you want to clear the autoload config? No config will be loaded automatically on next launch.', 'Reset', function() local Success, ErrorMessage = SaveManager:DeleteAutoLoadConfig() if not Success then SaveManager.Library:Notify(string.format('Failed to reset autoload config: %s', ErrorMessage)) return end SaveManager.Library:Notify('Successfully reset autoload config.') RefreshAutoloadConfigLabel() end ) end, }) AutoloadConfigLabel = ConfigurationBox:AddLabel('Current autoload config: ...', true) --// Set variables ConfigNameInput, ConfigList = SaveManager.Library.Options.SaveManager_ConfigName, SaveManager.Library.Options.SaveManager_ConfigList --// Refresh RefreshAutoloadConfigLabel() SaveManager:SetIgnoreIndexes({ 'SaveManager_ConfigList', 'SaveManager_ConfigName' }) return ConfigurationBox end SaveManager:BuildFolderTree() return SaveManager end -- ====================================================================== -- UI library: themes (ThemeManager) -- ====================================================================== __ZkhHub_shared.obsidian_theme_manager = function() local cloneref = (cloneref or clonereference or function(instance) return instance end) local clonefunction = (clonefunction or copyfunction or function(func) return func end) local HttpService = cloneref(game:GetService('HttpService')) local isfolder, isfile, listfiles = isfolder, isfile, listfiles if typeof(clonefunction) == 'function' then local isfolder_copy, isfile_copy, listfiles_copy = clonefunction(isfolder), clonefunction(isfile), clonefunction(listfiles) local isfolder_success, isfolder_error = pcall( function() return isfolder_copy('test' .. tostring(math.random(1000000, 9999999))) end ) if isfolder_success == false or typeof(isfolder_error) ~= 'boolean' then isfolder = function(folder) local success, data = pcall(isfolder_copy, folder) return (if success then data else false) end isfile = function(file) local success, data = pcall(isfile_copy, file) return (if success then data else false) end listfiles = function(folder) local success, data = pcall(listfiles_copy, folder) return (if success then data else {}) end end end local SchemeIndexes = { 'FontColor', 'MainColor', 'AccentColor', 'BackgroundColor', 'OutlineColor' } local ThemeManager = { Library = nil, Folder = 'ObsidianLibSettings', AppliedToTab = false, DefaultThemeName = nil, BuiltInThemes = { ['Default'] = { 1, { FontColor = 'ffffff', MainColor = '2e2e2e', AccentColor = '7d55ff', BackgroundColor = '151515', OutlineColor = '383838', BackgroundImage = '', }, }, ['BBot'] = { 2, { FontColor = 'ffffff', MainColor = '1e1e1e', AccentColor = '7e48a3', BackgroundColor = '232323', OutlineColor = '141414', BackgroundImage = '', }, }, ['Fatality'] = { 3, { FontColor = 'ffffff', MainColor = '1e1842', AccentColor = 'c50754', BackgroundColor = '191335', OutlineColor = '3c355d', BackgroundImage = '', }, }, ['Jester'] = { 4, { FontColor = 'ffffff', MainColor = '242424', AccentColor = 'db4467', BackgroundColor = '1c1c1c', OutlineColor = '373737', BackgroundImage = '', }, }, ['Mint'] = { 5, { FontColor = 'ffffff', MainColor = '242424', AccentColor = '3db488', BackgroundColor = '1c1c1c', OutlineColor = '373737', BackgroundImage = '', }, }, ['Tokyo Night'] = { 6, { FontColor = 'ffffff', MainColor = '191925', AccentColor = '6759b3', BackgroundColor = '16161f', OutlineColor = '323232', BackgroundImage = '', }, }, ['Ubuntu'] = { 7, { FontColor = 'ffffff', MainColor = '3e3e3e', AccentColor = 'e2581e', BackgroundColor = '323232', OutlineColor = '191919', BackgroundImage = '', }, }, ['Quartz'] = { 8, { FontColor = 'ffffff', MainColor = '232330', AccentColor = '426e87', BackgroundColor = '1d1b26', OutlineColor = '27232f', BackgroundImage = '', }, }, ['Nord'] = { 9, { FontColor = 'eceff4', MainColor = '3b4252', AccentColor = '88c0d0', BackgroundColor = '2e3440', OutlineColor = '4c566a', BackgroundImage = '', }, }, ['Dracula'] = { 10, { FontColor = 'f8f8f2', MainColor = '44475a', AccentColor = 'ff79c6', BackgroundColor = '282a36', OutlineColor = '6272a4', BackgroundImage = '', }, }, ['Monokai'] = { 11, { FontColor = 'f8f8f2', MainColor = '272822', AccentColor = 'f92672', BackgroundColor = '1e1f1c', OutlineColor = '49483e', BackgroundImage = '', }, }, ['Gruvbox'] = { 12, { FontColor = 'ebdbb2', MainColor = '3c3836', AccentColor = 'fb4934', BackgroundColor = '282828', OutlineColor = '504945', BackgroundImage = '', }, }, ['Solarized'] = { 13, { FontColor = '839496', MainColor = '073642', AccentColor = 'cb4b16', BackgroundColor = '002b36', OutlineColor = '586e75', BackgroundImage = '', }, }, ['Catppuccin'] = { 14, { FontColor = 'd9e0ee', MainColor = '302d41', AccentColor = 'f5c2e7', BackgroundColor = '1e1e2e', OutlineColor = '575268', BackgroundImage = '', }, }, ['One Dark'] = { 15, { FontColor = 'abb2bf', MainColor = '282c34', AccentColor = 'c678dd', BackgroundColor = '21252b', OutlineColor = '5c6370', BackgroundImage = '', }, }, ['Cyberpunk'] = { 16, { FontColor = 'f9f9f9', MainColor = '262335', AccentColor = '00ff9f', BackgroundColor = '1a1a2e', OutlineColor = '413c5e', BackgroundImage = '', }, }, ['Oceanic Next'] = { 17, { FontColor = 'd8dee9', MainColor = '1b2b34', AccentColor = '6699cc', BackgroundColor = '16232a', OutlineColor = '343d46', BackgroundImage = '', }, }, ['Material'] = { 18, { FontColor = 'eeffff', MainColor = '212121', AccentColor = '82aaff', BackgroundColor = '151515', OutlineColor = '424242', BackgroundImage = '', }, }, }, } function ThemeManager:SetLibrary(Library) ThemeManager.Library = Library end --// Helpers \\-- local function Trim(Text) return Text:match('^%s*(.-)%s*$') end local function IsStringEmpty(String) return if typeof(String) == 'string' then Trim(String) == '' else true end local function IsValidFolderPath(Name) return typeof(Name) == 'string' and (Trim(Name) ~= '' and not Name:match('^%s*$') and not Name:find('[<>:"|%?%*%z]')) end --// Folder helper \\-- local function SplitPath(Path) local Result = {} local Current = '' for Part in string.gmatch(Path, '[^/]+') do Current = if Current == '' then Part else (Current .. '/' .. Part) table.insert(Result, Current) end return Result end local function GetFolderPath() if IsStringEmpty(ThemeManager.Folder) then return false end return string.format('%s/themes', ThemeManager.Folder) end local GetCurrentThemesPath = GetFolderPath --// Files helper \\-- local function GetThemePath(ThemeName) local CurrentThemesPath = GetCurrentThemesPath() return if CurrentThemesPath == false then false else string.format('%s/%s.json', CurrentThemesPath, ThemeName) end local function DoesThemeExist(ThemeName, IncludeBuiltIn) if ThemeManager.BuiltInThemes[ThemeName] then return true end local ThemePath = GetThemePath(ThemeName) return if ThemePath == false then false else isfile(ThemePath) end local function GetDefaultThemePath() local CurrentThemesPath = GetCurrentThemesPath() return if CurrentThemesPath == false then false else string.format('%s/default.txt', CurrentThemesPath) end --// Folders \\-- function ThemeManager:GetPaths() local FolderPath = GetFolderPath() return if FolderPath == false then {} else SplitPath(FolderPath) end function ThemeManager:BuildFolderTree(SkipWhenCreated) local Paths = ThemeManager:GetPaths() if #Paths == 0 then return false end if SkipWhenCreated == true then if isfolder(Paths[1]) then return true end end for _, Path in Paths do if isfolder(Path) then continue end makefolder(Path) end return true end function ThemeManager:CheckFolderTree() return ThemeManager:BuildFolderTree(true) end function ThemeManager:SetFolder(Folder) assert(IsValidFolderPath(Folder), 'Invalid path provided') ThemeManager.Folder = Folder ThemeManager:BuildFolderTree() end --// Theme Management \\-- function ThemeManager:ReloadCustomThemes() local SettingsPath = GetCurrentThemesPath() if SettingsPath == false then return {} end local SuccessList, Files = pcall(listfiles, SettingsPath) if not (SuccessList and typeof(Files) == 'table') then ThemeManager.Library:Notify(string.format('Failed to load theme list: %s', tostring(Files))) return {} end local FileNames = {} for _, FilePath in Files do local RawFileName = FilePath:match('(.+)%..+$') if not RawFileName then continue end local Position = RawFileName:gsub('\\', '/'):find('/[^/]*$') local FileName = Position and RawFileName:sub(Position + 1) or RawFileName if not FileName or FileName == 'default' then continue end table.insert(FileNames, FileName) end return FileNames end function ThemeManager:GetCustomTheme(ThemeName) if IsStringEmpty(ThemeName) then return nil end local ThemePath = GetThemePath(ThemeName) if ThemePath == false or not isfile(ThemePath) then return nil end local SuccessRead, Content = pcall(readfile, ThemePath) if not SuccessRead then return nil end local SuccessDecode, Decoded = pcall(HttpService.JSONDecode, HttpService, Content) if not SuccessDecode or typeof(Decoded) ~= 'table' then return nil end return Decoded end function ThemeManager:SaveCustomTheme(ThemeName) if IsStringEmpty(ThemeName) then return false, 'Invalid theme name provided' end if string.lower(ThemeName) == 'default' then return false, 'Invalid theme name provided' end local ThemePath = GetThemePath(ThemeName) if ThemePath == false then return false, 'Invalid theme name provided' end ThemeManager:CheckFolderTree() local Library = ThemeManager.Library local ThemeData = { FontFace = Library.Options.FontFace.Value, BackgroundImage = Library.Options.BackgroundImage.Value, } for _, SchemeIndex in SchemeIndexes do ThemeData[SchemeIndex] = Library.Options[SchemeIndex].Value:ToHex() end local SuccessEncode, EncodedData = pcall(HttpService.JSONEncode, HttpService, ThemeData) if not SuccessEncode then return false, 'Failed to encode data' end local SuccessWrite, ErrorMessage = pcall(writefile, ThemePath, EncodedData) if not SuccessWrite then return false, 'Failed to write theme file: ' .. tostring(ErrorMessage) end return true end function ThemeManager:Delete(ThemeName) if IsStringEmpty(ThemeName) then return false, 'No theme is selected' end local ThemePath = GetThemePath(ThemeName) if ThemePath == false or not isfile(ThemePath) then return false, 'Theme file does not exist' end local SuccessDelete, ErrorMessage = pcall(delfile, ThemePath) if not SuccessDelete then return false, 'Failed to delete theme file: ' .. tostring(ErrorMessage) end if ThemeName == ThemeManager.DefaultThemeName then ThemeManager:DeleteDefaultTheme() end return true end --// Default Theme \\-- function ThemeManager:GetDefaultTheme() ThemeManager:CheckFolderTree() local DefaultThemePath = GetDefaultThemePath() if DefaultThemePath == false then return 'none', false, 'Invalid path provided' end if not isfile(DefaultThemePath) then return 'none', false, 'Default theme is not set' end local SuccessRead, DefaultThemeName = pcall(readfile, DefaultThemePath) if not (SuccessRead and typeof(DefaultThemeName) == 'string') then return 'none', false, DefaultThemeName end local ConfigExists = DoesThemeExist(DefaultThemeName, true) if not ConfigExists then return 'none', false, 'Theme file not found' end ThemeManager.DefaultThemeName = DefaultThemeName return DefaultThemeName, true end function ThemeManager:SetDefaultTheme(Theme) assert(ThemeManager.Library, 'Library is not set, call ThemeManager:SetLibrary(Library) first.') assert(not ThemeManager.AppliedToTab, 'Cannot set default theme after applying ThemeManager to a tab!') local Library = ThemeManager.Library local DefaultThemeData = ThemeManager.BuiltInThemes['Default'][2] local LibraryScheme = {} local FinalTheme = {} for _, SchemeIndex in SchemeIndexes do local IndexData = Theme[SchemeIndex] local IndexType = typeof(IndexData) if IndexType == 'Color3' then LibraryScheme[SchemeIndex] = IndexData FinalTheme[SchemeIndex] = string.format('#%s', IndexData:ToHex()) elseif IndexType == 'string' then LibraryScheme[SchemeIndex] = Color3.fromHex(IndexData) FinalTheme[SchemeIndex] = if IndexData:sub(1, 1) == '#' then IndexData else string.format('#%s', IndexData) else local Value = DefaultThemeData[SchemeIndex] LibraryScheme[SchemeIndex] = Color3.fromHex(Value) FinalTheme[SchemeIndex] = Value end end --// Font local FontFace = Theme['FontFace'] local FontFaceType = typeof(FontFace) if FontFaceType == 'EnumItem' then LibraryScheme.Font = Font.fromEnum(FontFace) FinalTheme.FontFace = FontFace.Name elseif FontFaceType == 'string' then LibraryScheme.Font = Font.fromEnum(Enum.Font[FontFace]) FinalTheme.FontFace = FontFace else LibraryScheme.Font = Font.fromEnum(Enum.Font.Code) FinalTheme.FontFace = 'Code' end --// Default Scheme Colors for _, DefaultSchemeColor in { 'RedColor', 'DestructiveColor', 'DarkColor', 'WhiteColor' } do LibraryScheme[DefaultSchemeColor] = Library.Scheme[DefaultSchemeColor] end --// Apply Library.Scheme = LibraryScheme ThemeManager.BuiltInThemes['Default'] = { 1, FinalTheme } Library:UpdateColorsUsingRegistry() end function ThemeManager:SaveDefault(ThemeName) if IsStringEmpty(ThemeName) then return false, 'No theme is selected' end ThemeManager:CheckFolderTree() local DefaultThemePath = GetDefaultThemePath() if DefaultThemePath == false then return false, 'Invalid path provided' end if not DoesThemeExist(ThemeName, true) then return false, 'Theme does not exist' end local SuccessWrite, ErrorMessage = pcall(writefile, DefaultThemePath, ThemeName) if not SuccessWrite then return false, ErrorMessage end ThemeManager.DefaultThemeName = ThemeName return true end function ThemeManager:LoadDefault() local ThemeName, Success, FetchErrorMessage = ThemeManager:GetDefaultTheme() if not Success or FetchErrorMessage then if FetchErrorMessage ~= 'Default theme is not set' then ThemeManager.Library:Notify(string.format('Failed to apply default theme: %s', FetchErrorMessage)) end return end if not ThemeManager:GetCustomTheme(ThemeName) then ThemeManager.Library.Options.ThemeManager_ThemeList:SetValue(ThemeName) return end local SuccessLoad, LoadErrorMessage = ThemeManager:ApplyTheme(ThemeName) if not SuccessLoad then ThemeManager.Library:Notify(string.format('Failed to apply default theme: %s', LoadErrorMessage)) return end ThemeManager.Library:Notify(string.format('Successfully applied default theme %q', ThemeName)) end function ThemeManager:DeleteDefaultTheme() ThemeManager:CheckFolderTree() local DefaultThemePath = GetDefaultThemePath() if DefaultThemePath == false then return false, 'Invalid path provided' end if not isfile(DefaultThemePath) then return false, 'Default theme is not set' end local SuccessDelete, ErrorMessage = pcall(delfile, DefaultThemePath) if not SuccessDelete then return false, ErrorMessage end ThemeManager.DefaultThemeName = nil return true end --// Apply Theme \\-- function ThemeManager:ThemeUpdate() local Library = ThemeManager.Library for _, SchemeIndex in SchemeIndexes do local Element = Library.Options[SchemeIndex] if not Element then continue end Library.Scheme[SchemeIndex] = Element.Value end Library:UpdateColorsUsingRegistry() end function ThemeManager:ApplyTheme(ThemeName) if IsStringEmpty(ThemeName) then return false, 'No theme is selected' end local CustomThemeData = ThemeManager:GetCustomTheme(ThemeName) local Data = CustomThemeData or ThemeManager.BuiltInThemes[ThemeName] if not Data then return false, 'Theme not found' end local Library = ThemeManager.Library local SchemeData = Data[2] local ThemeData = CustomThemeData or SchemeData for Index, Value in ThemeData do if Index == 'VideoLink' then continue end local Element = Library.Options[Index] local FinalValue = Value if Index == 'FontFace' then ThemeManager.Library:SetFont(Enum.Font[FinalValue]) elseif Index == 'BackgroundImage' then ThemeManager.Library:SetBackgroundImage(FinalValue) else FinalValue = Color3.fromHex(Value) Library.Scheme[Index] = FinalValue end if Element then Element:SetValue(FinalValue) end end ThemeManager:ThemeUpdate() return true end --// GUI \\-- local function ShowDialog(Condition, Index, Title, Description, DestructiveText, DestructiveAction) if Condition() == false then return DestructiveAction() end return ThemeManager.Library.Window:AddDialog(Index, { Title = Title, Description = Description, AutoDismiss = false, FooterButtons = { Cancel = { Title = 'Cancel', Variant = 'Ghost', Order = 1, Callback = function(Dialog) Dialog:Dismiss() end, }, DestructiveAction = { Title = DestructiveText, Variant = 'Destructive', Order = 2, Callback = function(Dialog) Dialog:Dismiss() DestructiveAction() end, }, }, }) end function ThemeManager:CreateThemeManager(Themesbox) assert(ThemeManager.Library, 'Library is not set, call ThemeManager:SetLibrary(Library) first.') local BuiltInThemesNames = {} for Name, _ThemeData in ThemeManager.BuiltInThemes do table.insert(BuiltInThemesNames, Name) end local CustomThemeList, CustomThemeName, ThemeList, FontFace, BackgroundImage, DefaultThemeLabel local function RefreshList() CustomThemeList:SetValues(ThemeManager:ReloadCustomThemes()) CustomThemeList:SetValue(nil) ThemeList:SetValues(BuiltInThemesNames) end local function RefreshDefaultThemeLabel() local DefaultThemeName, _Success, _ErrorMessage = ThemeManager:GetDefaultTheme() DefaultThemeLabel:SetText(string.format('Current default theme: %s', DefaultThemeName)) if CustomThemeList then RefreshList() end end table.sort( BuiltInThemesNames, function(IndexA, IndexB) return ThemeManager.BuiltInThemes[IndexA][1] < ThemeManager.BuiltInThemes[IndexB][1] end ) local function CreateColorOption(Text, SchemeIndex) Themesbox:AddLabel(Text):AddColorPicker(SchemeIndex, { Default = ThemeManager.Library.Scheme[SchemeIndex], }) return ThemeManager.Library.Options[SchemeIndex] end local BackgroundColor = CreateColorOption('Background color', 'BackgroundColor') local MainColor = CreateColorOption('Main color', 'MainColor') local AccentColor = CreateColorOption('Accent color', 'AccentColor') local OutlineColor = CreateColorOption('Outline color', 'OutlineColor') local FontColor = CreateColorOption('Font color', 'FontColor') Themesbox:AddDropdown('FontFace', { Text = 'Font Face', Default = 'Code', Values = { 'BuilderSans', 'Code', 'Fantasy', 'Gotham', 'Jura', 'Roboto', 'RobotoMono', 'SourceSans' }, AllowNull = false, Multi = false, }) Themesbox:AddInput('BackgroundImage', { Text = 'Background Image', Default = '', Finished = true, ClearTextOnFocus = false, ClearTextOnBlur = false, }) Themesbox:AddDivider() Themesbox:AddDropdown('ThemeManager_ThemeList', { Text = 'Theme list', Values = BuiltInThemesNames, AllowNull = true, Multi = false, FormatDisplayValue = function(Value) if Value ~= 'Default' and Value == ThemeManager.DefaultThemeName then return string.format('%s (default)', Value) end return Value end, FormatListValue = function(Value) if Value ~= 'Default' and Value == ThemeManager.DefaultThemeName then return string.format('%s (default)', Value) end return Value end, }) Themesbox:AddButton('Set as default', function() local ThemeName = ThemeList.Value ThemeManager:SaveDefault(ThemeName) ThemeManager.Library:Notify(string.format('Successfully set default theme to %q', ThemeName)) RefreshDefaultThemeLabel() end) Themesbox:AddDivider() CustomThemeName = Themesbox:AddInput('ThemeManager_CustomThemeName', { Text = 'Custom theme name', }) Themesbox:AddButton('Create theme', function() local Name = CustomThemeName.Value if IsStringEmpty(Name) then ThemeManager.Library:Notify('Theme name cannot be empty.') return end if string.lower(Name) == 'default' then ThemeManager.Library:Notify('Invalid theme name provided.') return end ShowDialog( function() return ThemeManager:GetCustomTheme(Name) ~= nil end, 'ThemeManager_CreateTheme', 'Theme already exists', string.format('A custom theme named %q already exists. Overwriting it will replace it with your current colors.', Name), 'Overwrite', function() local Success, ErrorMessage = ThemeManager:SaveCustomTheme(Name) if not Success then ThemeManager.Library:Notify(string.format('Failed to create theme %q: %s', Name, ErrorMessage)) return end ThemeManager.Library:Notify(string.format('Successfully created theme %q', Name)) RefreshList() end ) end) Themesbox:AddDivider() CustomThemeList = Themesbox:AddDropdown('ThemeManager_CustomThemeList', { Text = 'Custom themes', Values = ThemeManager:ReloadCustomThemes(), AllowNull = true, Multi = false, FormatDisplayValue = function(Value) if Value == ThemeManager.DefaultThemeName then return string.format('%s (default)', Value) end return Value end, FormatListValue = function(Value) if Value == ThemeManager.DefaultThemeName then return string.format('%s (default)', Value) end return Value end, }) Themesbox:AddButton('Load theme', function() local Name = CustomThemeList.Value if IsStringEmpty(Name) then ThemeManager.Library:Notify('Please select a theme first.') return end ThemeManager:ApplyTheme(Name) ThemeManager.Library:Notify(string.format('Successfully loaded theme %q', Name)) end) Themesbox:AddButton('Overwrite theme', function() local Name = CustomThemeList.Value if IsStringEmpty(Name) then ThemeManager.Library:Notify('Please select a theme first.') return end ShowDialog( function() return true end, 'ThemeManager_OverwriteTheme', 'Overwrite theme', string.format('Are you sure you want to overwrite %q with your current colors? This cannot be undone.', Name), 'Overwrite', function() ThemeManager:SaveCustomTheme(Name) ThemeManager.Library:Notify(string.format('Successfully overwrote theme %q', Name)) end ) end) Themesbox:AddButton('Delete theme', function() local Name = CustomThemeList.Value if IsStringEmpty(Name) then ThemeManager.Library:Notify('Please select a theme first.') return end ShowDialog( function() return true end, 'ThemeManager_DeleteTheme', 'Delete theme', string.format('Are you sure you want to delete %q? This cannot be undone.', Name), 'Delete', function() local Success, ErrorMessage = ThemeManager:Delete(Name) if not Success then ThemeManager.Library:Notify(string.format('Failed to delete theme: %s', ErrorMessage)) return end ThemeManager.Library:Notify(string.format('Successfully deleted theme %q', Name)) RefreshDefaultThemeLabel() end ) end) Themesbox:AddButton('Refresh list', RefreshList) Themesbox:AddButton('Set as default', function() local Name = CustomThemeList.Value if IsStringEmpty(Name) then ThemeManager.Library:Notify('Please select a theme first.') return end ThemeManager:SaveDefault(Name) ThemeManager.Library:Notify(string.format('Successfully set default theme to %q', Name)) RefreshDefaultThemeLabel() end) Themesbox:AddButton('Reset default', function() ShowDialog( function() return true end, 'ThemeManager_ResetDefault', 'Reset default theme', 'Are you sure you want to clear the default theme? The library will revert to its built-in default on next load.', 'Reset', function() local Success, ErrorMessage = ThemeManager:DeleteDefaultTheme() if not Success then ThemeManager.Library:Notify(string.format('Failed to reset default theme: %s', ErrorMessage)) return end ThemeManager.Library:Notify('Successfully reset default theme.') RefreshDefaultThemeLabel() end ) end) DefaultThemeLabel = Themesbox:AddLabel('Current default theme: ...', true) --// Set Variables CustomThemeList, CustomThemeName, ThemeList, FontFace, BackgroundImage = ThemeManager.Library.Options.ThemeManager_CustomThemeList, ThemeManager.Library.Options.ThemeManager_CustomThemeName, ThemeManager.Library.Options.ThemeManager_ThemeList, ThemeManager.Library.Options.FontFace, ThemeManager.Library.Options.BackgroundImage --// Handlers ThemeList:OnChanged(function() ThemeManager:ApplyTheme(ThemeList.Value) end) local function UpdateTheme() ThemeManager:ThemeUpdate() end BackgroundColor:OnChanged(UpdateTheme) MainColor:OnChanged(UpdateTheme) AccentColor:OnChanged(UpdateTheme) OutlineColor:OnChanged(UpdateTheme) FontColor:OnChanged(UpdateTheme) FontFace:OnChanged(function(Value) ThemeManager.Library:SetFont(Enum.Font[Value]) end) BackgroundImage:OnChanged(function(Value) ThemeManager.Library:SetBackgroundImage(Value) end) --// Load default ThemeManager:LoadDefault() ThemeManager.AppliedToTab = true RefreshDefaultThemeLabel() return Themesbox end function ThemeManager:CreateGroupBox(Tab, IconName) return Tab:AddLeftGroupbox('Themes', IconName or 'paintbrush') end function ThemeManager:ApplyToTab(Tab, IconName) local Groupbox = ThemeManager:CreateGroupBox(Tab, IconName) return ThemeManager:CreateThemeManager(Groupbox) end function ThemeManager:ApplyToGroupbox(Groupbox) return ThemeManager:CreateThemeManager(Groupbox) end getgenv().ObsidianThemeManager = ThemeManager return ThemeManager end -- ====================================================================== -- Script paths and storage folders -- ====================================================================== __ZkhHub_shared.script_paths = function() local DisplayName = 'ZkxHub' local StorageRoot = 'ZkxHub' -- Auto Execute re-runs this file after every teleport (lobby <-> match). It is only a one-line -- loader for the hosted script (LoaderUrl); the script itself is never written to the workspace. local SourcePath = StorageRoot .. '/Rivals.txt' local LoaderUrl = 'https://raw.githubusercontent.com/ZkxrI/ZkxHub/refs/heads/main/source' local LoaderSource = string.format('loadstring(game:HttpGet(%q))()', LoaderUrl) local RivalsGameId = 6035872082 -- Older builds saved the whole script here; ensure_runnable_copy removes it so nobody can read it. local FullCopyPath = StorageRoot .. '/Rivals.luau' -- Left behind by older builds; removed on startup so nothing runs it by mistake. local LegacyRunnablePath = StorageRoot .. '/ZkxHub_Source_Runnable.lua' local AutoShowPath = StorageRoot .. '/AutoShow.txt' local UiSettingsPath = StorageRoot .. '/UISettings.json' -- Handed to queue_on_teleport; runs once in the next server. Used to queue the remote -- loader, which only handles the lobby PlaceId and silently did nothing in matches. -- NOTE: the file it re-runs is a loader stub for the hosted script again, so Auto Execute -- only works in matches if the hosted script itself runs there. local QueuePayloadFormat = [==[ local env = (type(getgenv) == 'function' and getgenv()) or _G local deadline = os.clock() + 30 while game.GameId == 0 and os.clock() < deadline do task.wait(0.1) end if game.GameId ~= %s then return end if not game:IsLoaded() then game.Loaded:Wait() end local job = tostring(game.JobId) if job ~= '' then if env.ZkxHubChainJob == job then return end env.ZkxHubChainJob = job end task.wait(8) local ok, ui = pcall(function() return game:GetService('HttpService'):JSONDecode(readfile(%s)) end) if ok and type(ui) == 'table' and ui.auto_execute_on_teleport == false then return end local path = %s if not isfile(path) then warn('[ZkxHub] Auto Execute: workspace/' .. path .. ' is missing') return end local chunk, err = loadstring(readfile(path), '@' .. path) if not chunk then warn('[ZkxHub] Auto Execute: ' .. tostring(err)) return end chunk() ]==] local HttpService = game:GetService('HttpService') local DefaultUiSettings = { auto_show = true, show_notification = true, show_keybind_states = false, auto_execute_on_teleport = false, auto_load_config = true, auto_save_config = true, load_config_name = 'Legit', menu_keybind = 'RightControl', font_face = 'Gotham Heavy', theme_fade = true, animations = true, ui_scale = '100%', custom_cursor = true, splash = true, watermark = true, watermark_fields = 'FPS,Ping', notify_placement = 'Top Right', notify_style = 'Premium', } local module = {} pcall(function() if not isfolder(StorageRoot) then makefolder(StorageRoot) end end) local function clone_table(source) local copy = {} for key, value in pairs(source) do copy[key] = value end return copy end local function apply_ui_setting_overrides(defaults, overrides) if type(overrides) ~= 'table' then return defaults end for key, value in pairs(overrides) do if DefaultUiSettings[key] ~= nil and value ~= nil then defaults[key] = value end end return defaults end local function sanitize_ui_settings(settings, defaults) local data = clone_table(defaults) if type(settings) ~= 'table' then return data end for key, fallback in pairs(defaults) do local value = settings[key] if type(fallback) == 'boolean' then if type(value) == 'boolean' then data[key] = value end elseif type(fallback) == 'string' then if type(value) == 'string' and value ~= '' then data[key] = value end end end return data end local function read_legacy_auto_show(default_value) local path = module.auto_show_path() if not isfile(path) then return default_value end local success, value = pcall(readfile, path) if not success then return default_value end value = string.lower(tostring(value)) if value == 'true' then return true end if value == 'false' then return false end return default_value end function module.storage_root() return StorageRoot end function module.auto_show_path() return AutoShowPath end function module.ui_settings_path() return UiSettingsPath end function module.default_ui_settings(overrides) return apply_ui_setting_overrides(clone_table(DefaultUiSettings), overrides) end function module.config_path(config_name) return StorageRoot .. '/settings/' .. config_name .. '.json' end function module.load_ui_settings(overrides) local defaults = module.default_ui_settings(overrides) if isfile(UiSettingsPath) then local read_ok, raw = pcall(readfile, UiSettingsPath) if read_ok then local decode_ok, decoded = pcall(function() return HttpService:JSONDecode(raw) end) if decode_ok then return sanitize_ui_settings(decoded, defaults) end end end defaults.auto_show = read_legacy_auto_show(defaults.auto_show) return defaults end function module.save_ui_settings(settings, overrides) local data = sanitize_ui_settings(settings, module.default_ui_settings(overrides)) writefile(UiSettingsPath, HttpService:JSONEncode(data)) writefile(module.auto_show_path(), data.auto_show and 'true' or 'false') return data end function module.ensure_auto_show_file() local path = module.auto_show_path() local ui_settings = module.load_ui_settings() writefile(path, ui_settings.auto_show and 'true' or 'false') return path end function module.source_path() return SourcePath end function module.queue_on_teleport_script() return string.format( QueuePayloadFormat, tostring(RivalsGameId), string.format('%q', UiSettingsPath), string.format('%q', SourcePath) ) end function module.ensure_runnable_copy() if type(isfile) == 'function' and type(delfile) == 'function' then pcall(function() if isfile(LegacyRunnablePath) then delfile(LegacyRunnablePath) end end) end if type(isfile) == 'function' and type(readfile) == 'function' then pcall(function() if not isfile(FullCopyPath) then return end local ok, existing = pcall(readfile, FullCopyPath) -- only a full-script copy from an older build; a small file is someone's own, leave it alone if not (ok and type(existing) == 'string' and #existing > 4096) then return end if type(delfile) == 'function' then delfile(FullCopyPath) elseif type(writefile) == 'function' then writefile(FullCopyPath, LoaderSource) end end) end if type(writefile) == 'function' then pcall(function() local current = nil if type(isfile) == 'function' and isfile(SourcePath) then local ok, existing = pcall(readfile, SourcePath) current = ok and existing or nil end if current ~= LoaderSource then writefile(SourcePath, LoaderSource) end end) end return module.has_runnable_copy() end function module.has_runnable_copy() local ok, exists = pcall(isfile, SourcePath) return ok and exists == true end function module.format_title() return DisplayName end return module end -- ====================================================================== -- Connection registry -- ====================================================================== __ZkhHub_shared.connection_registry = function() local module = {} local function disconnect_handle(handle) if handle == nil then return end if type(handle) == 'function' then pcall(handle) return end if type(handle) == 'table' then local disconnect_method = handle.Disconnect if type(disconnect_method) == 'function' then pcall(disconnect_method, handle) end return end pcall(function() handle:Disconnect() end) end function module.new() local handles = {} local registry = {} function registry:register(name, handle) if handles[name] ~= nil then disconnect_handle(handles[name]) end handles[name] = handle return handle end function registry:get(name) return handles[name] end function registry:disconnect(names) for _, name in ipairs(names) do if handles[name] ~= nil then disconnect_handle(handles[name]) handles[name] = nil end end end function registry:disconnect_all() for name, handle in pairs(handles) do disconnect_handle(handle) handles[name] = nil end end setmetatable(registry, { __newindex = function(_, name, handle) registry:register(name, handle) end, __index = function(_, name) return handles[name] end, }) return registry end return module end -- ====================================================================== -- Menu settings (UI Settings tab) -- ====================================================================== __ZkhHub_shared.ui_settings = function() local module = {} local DEFAULT_CONFIG_NAMES = { 'Legit', 'Rage' } local AUTO_SAVE_DEBOUNCE_SECONDS = 0.35 local AUTO_SAVE_SUPPORTED_TYPES = { Toggle = true, Slider = true, Dropdown = true, ColorPicker = true, KeyPicker = true, Input = true, } function module.new(args) local window = args.window local library = args.library local theme_manager = args.theme_manager local script_paths = args.script_paths local toggles = args.toggles local options = args.options local local_player = args.local_player local queue_loader = args.queue_loader local on_load_default = args.on_load_default local on_save = args.on_save local is_queue_on_teleport_forced = args.is_queue_on_teleport_forced local save_manager = args.save_manager local current_config_name = args.current_config_name local function AppendConfigName(list, seen, config_name) if type(config_name) ~= 'string' or config_name == '' or string.lower(config_name) == 'none' or seen[config_name] then return end seen[config_name] = true table.insert(list, config_name) end local function BuildConfigNames() local list = {} local seen = {} local source_config_names = type(args.config_names) == 'table' and args.config_names or DEFAULT_CONFIG_NAMES for _, config_name in ipairs(source_config_names) do AppendConfigName(list, seen, config_name) end if #list == 0 then AppendConfigName(list, seen, current_config_name) end return list end local config_names = BuildConfigNames() local default_config_selection = config_names[1] or current_config_name local config_dropdown_values = {} for _, config_name in ipairs(config_names) do table.insert(config_dropdown_values, config_name) end local function ResolveConfigFileName(config_name) if type(args.resolve_config_name) == 'function' then local resolved = args.resolve_config_name(config_name) if type(resolved) == 'string' and resolved ~= '' then return resolved end end if args.use_global_config_names == true then return config_name end if config_name == current_config_name then return config_name end if type(current_config_name) == 'string' and current_config_name ~= '' then return current_config_name .. ' ' .. config_name end return config_name end local ids = args.ids or { auto_show = 'P3S1T1', show_notification = 'P3S1T2', show_keybind_states = 'P3S1T3', auto_execute_on_teleport = 'P3S1T4', auto_load_config = 'P3S1T5', auto_save_config = 'UIAutoSaveConfig', load_config_name = 'UILoadConfigName', } local defaults = args.defaults or {} local persisted_defaults = script_paths.load_ui_settings(defaults) local persist_menu_keybind = args.persist_menu_keybind ~= false -- Only a real keyboard key may be saved as the menu bind. Unbound (None/Unknown), Escape, a mouse button or a stale key name -- would leave the menu unreachable on the next launch, so those fall back to the default instead. local function NormalizeMenuKeybind(value, fallback) if type(value) == 'string' and value ~= '' and value ~= 'None' and value ~= 'Unknown' then local ok, key_code = pcall(function() return Enum.KeyCode[value] end) if ok and key_code and key_code ~= Enum.KeyCode.Unknown and key_code ~= Enum.KeyCode.Escape then return value end end return fallback or 'RightControl' end local default_menu_keybind = NormalizeMenuKeybind(defaults.menu_keybind, 'RightControl') local settings = {} local queue_on_teleport_connection = nil local queued_for_teleport = false local shutting_down = false local config_load_suppression_count = 0 local auto_save_token = 0 local auto_save_wrappers = setmetatable({}, { __mode = 'k' }) local auto_save_binding_loop_started = false local preserved_ids = {} preserved_ids[ids.auto_show] = true preserved_ids[ids.show_notification] = true preserved_ids[ids.show_keybind_states] = true preserved_ids[ids.auto_execute_on_teleport] = true preserved_ids[ids.auto_load_config] = true preserved_ids[ids.auto_save_config] = true for _, toggle_id in ipairs(RequireSharedModule('ui_extras').ToggleIds) do preserved_ids[toggle_id] = true end local function IsConfigSelectionValue(value) if type(value) ~= 'string' then return false end for _, config_name in ipairs(config_dropdown_values) do if value == config_name then return true end end return false end local function NormalizeConfigSelectionValue(value, fallback) if IsConfigSelectionValue(value) then return value end if IsConfigSelectionValue(fallback) then return fallback end return default_config_selection end local function ResolveConfigSelectionValue(option_id, fallback) local option = options[option_id] if option and IsConfigSelectionValue(option.Value) then return option.Value end return NormalizeConfigSelectionValue(fallback, default_config_selection) end if save_manager and type(save_manager.SetIgnoreIndexes) == 'function' then local ignore_list = { ids.auto_show, ids.show_notification, ids.show_keybind_states, ids.auto_execute_on_teleport, ids.auto_load_config, ids.auto_save_config, ids.load_config_name, 'MenuKeybind', } if type(args.extra_ignore_ids) == 'table' then for _, extra_id in ipairs(args.extra_ignore_ids) do if type(extra_id) == 'string' and extra_id ~= '' then table.insert(ignore_list, extra_id) end end end for _, extra_id in ipairs(RequireSharedModule('ui_extras').AllIds) do table.insert(ignore_list, extra_id) end save_manager:SetIgnoreIndexes(ignore_list) end local function disconnect_queue_on_teleport() if queue_on_teleport_connection then queue_on_teleport_connection:Disconnect() queue_on_teleport_connection = nil end end local tab = window:AddTab('UI Settings', 'settings') local group = tab:AddLeftGroupbox(args.groupbox_title or 'Menu Options') local load_config_name_default = NormalizeConfigSelectionValue(persisted_defaults.load_config_name, defaults.load_config_name or default_config_selection) group:AddToggle(ids.auto_show, { Text = 'Silent Execute', Default = not persisted_defaults.auto_show, Tooltip = 'Keeps the UI hidden on execute. Use your menu keybind to open it.', }) group:AddToggle(ids.show_notification, { Text = 'Load Notification', Default = persisted_defaults.show_notification, Tooltip = 'Will show "Successfully loaded" notification on execute', }) group:AddToggle(ids.show_keybind_states, { Text = 'Show keybind states', Default = persisted_defaults.show_keybind_states, Tooltip = 'Shows a menu of your active keybind toggles. On mobile, tap a keybind to toggle it.', }) group:AddToggle(ids.auto_execute_on_teleport, { Text = 'Auto Execute', Default = persisted_defaults.auto_execute_on_teleport, Tooltip = 'Will execute your script automatically when teleporting to a new area', }) group:AddToggle(ids.auto_save_config, { Text = 'Auto Save Config', Default = persisted_defaults.auto_save_config, Tooltip = 'Automatically saves changed toggles, sliders, dropdowns, colors, keybinds, and inputs to the selected config.', }) group:AddToggle(ids.auto_load_config, { Text = 'Auto Load Config', Default = persisted_defaults.auto_load_config, Tooltip = 'Automatically loads your saved config on execute', }) group:AddDropdown(ids.load_config_name, { Text = 'Selected Config', Values = config_dropdown_values, Default = load_config_name_default, Tooltip = 'Selected config used by Auto Load Config, Auto Save Config, Save Config, and Load Selected Config.', }) group:AddButton('Save Config', function() if on_save then local ok, err = on_save(settings:resolve_save_config_name()) if ok == false then library:Notify({ Title = 'Config', Description = 'Failed to save config: ' .. tostring(err), Time = 3 }) return end end library:Notify({ Title = 'Config', Description = 'Config saved', Time = 3 }) end) group:AddButton('Load Selected Config', function() if args.on_load_saved then args.on_load_saved() library:Notify({ Title = 'Config', Description = 'Selected config loaded', Time = 3 }) end end) group:AddButton('Reset To Default', function() on_load_default() end) group:AddButton({ Text = 'Unload', Func = function() settings:shutdown_for_unload() __ZkhHub_genv.Executed = nil pcall(function() _G._ZkxHubExecuted = nil end) library:Unload() end, DoubleClick = true, }) group:AddLabel('Menu bind'):AddKeyPicker('MenuKeybind', { Default = persist_menu_keybind and NormalizeMenuKeybind(persisted_defaults.menu_keybind, default_menu_keybind) or default_menu_keybind, -- Always/Hold make the picker ignore its own key, so the toggle mode is the only one on offer Mode = 'Toggle', Modes = { 'Toggle' }, NoUI = true, Text = 'Menu keybind', }) library.ToggleKeybind = options.MenuKeybind local theme_group = tab:AddRightGroupbox('Themes') theme_group:AddLabel('Background color'):AddColorPicker('BackgroundColor', { Default = library.Scheme.BackgroundColor }) theme_group:AddLabel('Main color'):AddColorPicker('MainColor', { Default = library.Scheme.MainColor }) theme_group:AddLabel('Accent color'):AddColorPicker('AccentColor', { Default = library.Scheme.AccentColor }) theme_group:AddLabel('Outline color'):AddColorPicker('OutlineColor', { Default = library.Scheme.OutlineColor }) local ThemesArray = {} for Name in pairs(theme_manager.BuiltInThemes) do table.insert(ThemesArray, Name) end table.sort(ThemesArray, function(a, b) return theme_manager.BuiltInThemes[a][1] < theme_manager.BuiltInThemes[b][1] end) theme_group:AddDivider() theme_group:AddDropdown('ThemeManager_ThemeList', { Text = 'Theme list', Values = ThemesArray, Default = 1 }) theme_group:AddButton('Set as default', function() theme_manager:SaveDefault(options.ThemeManager_ThemeList.Value) library:Notify({ Title = 'Theme', Description = string.format('Set default theme to %q', options.ThemeManager_ThemeList.Value), Time = 3, }) end) options.ThemeManager_ThemeList:OnChanged(function() theme_manager:ApplyTheme(options.ThemeManager_ThemeList.Value) end) local extras = nil local persist_ui_settings = nil local function UpdateTheme() theme_manager:ThemeUpdate() end options.BackgroundColor:OnChanged(UpdateTheme) options.MainColor:OnChanged(UpdateTheme) options.AccentColor:OnChanged(UpdateTheme) options.OutlineColor:OnChanged(UpdateTheme) extras = RequireSharedModule('ui_extras').build({ library = library, window = window, theme_manager = theme_manager, tab = tab, group = group, theme_group = theme_group, persisted = persisted_defaults, options = options, toggles = toggles, preserved_ids = preserved_ids, save = function() if persist_ui_settings then persist_ui_settings() end end, }) theme_manager:LoadDefault() extras.Finish() theme_manager.AppliedToTab = true if type(args.after_build) == 'function' then args.after_build(tab, group) end local function ResolveMenuKeybindValue() local option = options.MenuKeybind local fallback = NormalizeMenuKeybind(persisted_defaults.menu_keybind, default_menu_keybind) return NormalizeMenuKeybind(option and option.Value, fallback) end persist_ui_settings = function() local payload = { auto_show = not toggles[ids.auto_show].Value, show_notification = toggles[ids.show_notification].Value, show_keybind_states = toggles[ids.show_keybind_states].Value, auto_execute_on_teleport = toggles[ids.auto_execute_on_teleport].Value, auto_load_config = toggles[ids.auto_load_config].Value, auto_save_config = toggles[ids.auto_save_config].Value, load_config_name = ResolveConfigSelectionValue(ids.load_config_name, persisted_defaults.load_config_name), menu_keybind = persist_menu_keybind and ResolveMenuKeybindValue() or defaults.menu_keybind or 'RightControl', } if extras then for key, value in pairs(extras.Snapshot()) do payload[key] = value end end script_paths.save_ui_settings(payload, defaults) end local function sync_keybind_visibility() library.KeybindFrame.Visible = toggles[ids.show_keybind_states].Value end local function should_queue_on_teleport() if toggles[ids.auto_execute_on_teleport].Value then return true end if type(is_queue_on_teleport_forced) == 'function' then local ok, forced = pcall(is_queue_on_teleport_forced) if ok and forced then return true end end return false end local function sync_queue_on_teleport() if should_queue_on_teleport() then if not __ZkhHub_genv.ZkxHubCaps.gate('Auto Execute', 'queue_on_teleport') then return end if queue_on_teleport_connection then return end -- Queue on the first teleport state instead of waiting for InProgress (the last one -- before the client leaves). Failed re-arms for the next attempt; the payload dedupes. queue_on_teleport_connection = local_player.OnTeleport:Connect(function(state) if state == Enum.TeleportState.Failed then queued_for_teleport = false return end if queued_for_teleport then return end queued_for_teleport = true queue_loader() end) else disconnect_queue_on_teleport() end end local function BindAutoSaveObject(id, object) if type(object) ~= 'table' or not AUTO_SAVE_SUPPORTED_TYPES[object.Type] then return end if save_manager and save_manager.Ignore and save_manager.Ignore[id] then return end if auto_save_wrappers[object] == object.Changed then return end local previous_changed = object.Changed object.Changed = function(...) if type(previous_changed) == 'function' then previous_changed(...) end settings:schedule_auto_save(id) end auto_save_wrappers[object] = object.Changed end function settings:get_config_names() local copy = {} for _, config_name in ipairs(config_names) do table.insert(copy, ResolveConfigFileName(config_name)) end return copy end function settings:resolve_save_config_name() return ResolveConfigFileName(ResolveConfigSelectionValue(ids.load_config_name, persisted_defaults.load_config_name)) end function settings:resolve_load_config_name() return settings:resolve_save_config_name() end function settings:should_auto_save_config() return toggles[ids.auto_save_config].Value and settings:resolve_save_config_name() ~= nil end function settings:schedule_auto_save(id) if shutting_down or config_load_suppression_count > 0 or not settings:should_auto_save_config() then return end auto_save_token = auto_save_token + 1 local token = auto_save_token task.delay(AUTO_SAVE_DEBOUNCE_SECONDS, function() if shutting_down or token ~= auto_save_token or config_load_suppression_count > 0 or not settings:should_auto_save_config() then return end if on_save then on_save(settings:resolve_save_config_name()) end end) end function settings:refresh_auto_save_bindings() for id, toggle in pairs(toggles) do BindAutoSaveObject(id, toggle) end for id, option in pairs(options) do BindAutoSaveObject(id, option) end end function settings:is_config_loading() return config_load_suppression_count > 0 end function settings:with_config_load_suspended(callback) config_load_suppression_count = config_load_suppression_count + 1 local ok, result, err = pcall(callback) task.delay(1, function() config_load_suppression_count = math.max(0, config_load_suppression_count - 1) end) if not ok then error(result, 2) end return result, err end function settings:register_callbacks() toggles[ids.auto_show]:OnChanged(persist_ui_settings) toggles[ids.show_notification]:OnChanged(persist_ui_settings) toggles[ids.show_keybind_states]:OnChanged(function() persist_ui_settings() sync_keybind_visibility() end) toggles[ids.auto_execute_on_teleport]:OnChanged(function() persist_ui_settings() sync_queue_on_teleport() end) toggles[ids.auto_load_config]:OnChanged(persist_ui_settings) toggles[ids.auto_save_config]:OnChanged(persist_ui_settings) options[ids.load_config_name]:OnChanged(persist_ui_settings) if persist_menu_keybind then options.MenuKeybind:OnChanged(persist_ui_settings) end settings:refresh_auto_save_bindings() if not auto_save_binding_loop_started then auto_save_binding_loop_started = true task.spawn(function() while not shutting_down do task.wait(1) settings:refresh_auto_save_bindings() end end) end end function settings:sync_queue_on_teleport() sync_queue_on_teleport() end function settings:should_auto_load_config() return toggles[ids.auto_load_config].Value and settings:resolve_load_config_name() ~= nil end function settings:shutdown_for_unload() if shutting_down then return end shutting_down = true for toggle_id, toggle in pairs(toggles) do if not preserved_ids[toggle_id] and type(toggle) == 'table' and type(toggle.SetValue) == 'function' and type(toggle.Value) == 'boolean' and toggle.Value == true then pcall(toggle.SetValue, toggle, false) end end disconnect_queue_on_teleport() end function settings:reconcile_loaded_ui_settings() persist_ui_settings() sync_keybind_visibility() sync_queue_on_teleport() settings:refresh_auto_save_bindings() theme_manager:LoadDefault() if extras then extras.Reapply() end end function settings:notify_loaded(duration) if toggles[ids.show_notification].Value then local menuKeybindValue = ResolveMenuKeybindValue() library:Notify({ Title = 'Loaded', Description = 'Loaded - ' .. menuKeybindValue .. ' to toggle menu', Time = duration or 5, }) end end return settings end return module end -- ====================================================================== -- Game bootstrap -- ====================================================================== __ZkhHub_shared.game_bootstrap = function() local module = {} local function elevate_ui_identity() if type(__ZkxHubElevateIdentity) == 'function' then __ZkxHubElevateIdentity() elseif type(setthreadidentity) == 'function' then pcall(setthreadidentity, 8) end end function module.create_window(args) elevate_ui_identity() local ui_settings = args.script_paths.load_ui_settings(args.ui_settings_defaults) elevate_ui_identity() local Kit = RequireSharedModule('zkx_ui_kit') local window = args.library:CreateWindow({ Position = args.position, Title = args.title, Center = args.center, AutoShow = false, -- the menu opens when loading is done (auto_show is read again then) DisableSearch = false, GlobalSearch = true, Footer = args.footer or '.gg/bxu2WMjNjN', Icon = args.icon or Kit.LogoAsset, IconSize = args.icon_size or UDim2.fromOffset(36, 36), Font = Kit.ResolveFont(ui_settings.font_face), ShowCustomCursor = ui_settings.custom_cursor ~= false, }) -- logo + title at the left of the sidebar header, the title in Heavy Italic local okTitle, titleError = pcall(Kit.TitleRow, window, { Library = args.library, TitleSize = 22, Padding = 14 }) if not okTitle then warn('[ZkxHub] title row: ' .. tostring(titleError)) end return window end function module.create_standard_shared_ui(args) elevate_ui_identity() local shared_ui local defaults = args.defaults or { auto_show = false, show_notification = true, show_keybind_states = false, auto_execute_on_teleport = false, auto_load_config = true, auto_save_config = true, load_config_name = 'Legit', } local function reconcile_loaded_state() if args.reconcile_loaded_state then args.reconcile_loaded_state() end end elevate_ui_identity() shared_ui = args.ui_settings.new({ window = args.window, library = args.library, theme_manager = args.theme_manager, script_paths = args.script_paths, save_manager = args.save_manager, current_config_name = args.current_config_name, config_names = args.config_names, use_global_config_names = args.use_global_config_names, resolve_config_name = args.resolve_config_name, toggles = args.toggles, options = args.options, local_player = args.local_player, defaults = defaults, persist_menu_keybind = args.persist_menu_keybind, extra_ignore_ids = args.extra_ignore_ids, queue_loader = function() if __ZkhHub_genv.ZkxHubCaps.gate('Auto Execute', 'queue_on_teleport') then args.queue_on_teleport(args.script_paths.queue_on_teleport_script()) end end, on_save = function(config_name) local save_name = shared_ui and shared_ui.resolve_save_config_name and shared_ui:resolve_save_config_name() or args.current_config_name if config_name then save_name = config_name end if args.before_save then args.before_save(shared_ui, save_name) end local ok, err = args.save_manager:Save(save_name) if args.on_save then args.on_save(shared_ui, save_name) end return ok, err end, on_load_saved = function() local load_name = shared_ui and shared_ui.resolve_load_config_name and shared_ui:resolve_load_config_name() or args.current_config_name if shared_ui and shared_ui.with_config_load_suspended then shared_ui:with_config_load_suspended(function() args.save_manager:Load(load_name) end) else args.save_manager:Load(load_name) end shared_ui:reconcile_loaded_ui_settings() reconcile_loaded_state() if args.on_load_saved then args.on_load_saved(shared_ui, load_name) end end, on_load_default = function() local save_name = shared_ui and shared_ui.resolve_save_config_name and shared_ui:resolve_save_config_name() or args.current_config_name if shared_ui and shared_ui.with_config_load_suspended then shared_ui:with_config_load_suspended(function() if type(args.reset_config_to_default) == 'function' then args.save_manager:Load(args.default_config_name) args.reset_config_to_default(shared_ui, save_name) args.save_manager:Load(save_name) else args.save_manager:Load(args.default_config_name) args.save_manager:Save(save_name) end end) else if type(args.reset_config_to_default) == 'function' then args.save_manager:Load(args.default_config_name) args.reset_config_to_default(shared_ui, save_name) args.save_manager:Load(save_name) else args.save_manager:Load(args.default_config_name) args.save_manager:Save(save_name) end end shared_ui:reconcile_loaded_ui_settings() reconcile_loaded_state() if args.on_load_default then args.on_load_default(shared_ui, save_name) end end, }) return shared_ui end function module.prepare_setup(args) elevate_ui_identity() local sm = args.save_manager local default_name = args.default_config_name if not isfile(args.script_paths.config_path(default_name)) then sm:Save(default_name) end if args.shared_ui and args.shared_ui.get_config_names then for _, config_name in ipairs(args.shared_ui:get_config_names()) do if not isfile(args.script_paths.config_path(config_name)) then local ok = nil if type(args.reset_config_to_default) == 'function' then ok = args.reset_config_to_default(args.shared_ui, config_name) end if ok == false or ok == nil then sm:Save(config_name) end end end end if not args.defer_load then module.load_config(args) end args.shared_ui:register_callbacks() args.shared_ui:reconcile_loaded_ui_settings() end function module.load_config(args) elevate_ui_identity() local sm = args.save_manager local current = args.current_config_name local default_name = args.default_config_name local shared_ui = args.shared_ui local function load_config_name(config_name) if shared_ui and shared_ui.with_config_load_suspended then return shared_ui:with_config_load_suspended(function() return sm:Load(config_name) end) end return sm:Load(config_name) end if shared_ui and shared_ui.should_auto_load_config and shared_ui:should_auto_load_config() then local load_name = shared_ui and shared_ui.resolve_load_config_name and shared_ui:resolve_load_config_name() or current local ok = load_config_name(load_name) if not ok then load_config_name(default_name) end else load_config_name(default_name) end end return module end -- ====================================================================== -- Ragebot (ZHub): lifted modules and standalone boot -- ====================================================================== __ZkhHub_shared.ZHub_ragebot = function() getgenv().ZkxhubRagebotManualStart = true --[[ ZHub ragebot - standalone -------------------------- The Ragebot of "ZHub prem.luau" (Zkxhub rebuild, Rivals), cut out on its own. Runs without the hub: no menu, no cosmetics, no other combat features. Game: Rivals (PlayerScripts.Controllers.FighterController must exist) Needs: an executor with getgenv, cloneref, hookfunction-style helpers, setfflag Run: execute this file once. It waits for the controllers, then boots. Do NOT run it together with the full ZHub hub (both hook the same game modules). API (getgenv().ZkxhubRagebot): Rage.Set(path, value) write a setting, e.g. Rage.Set({"Ragebot","Evasion","Mode"}, "Translocate") Rage.Get(path) read a setting Rage.Config live table = store.Data.Ragebot (read-only view, write through Set) Rage.SetEnabled(bool) Enabled = bool, Keybind.Kind = "Always" Rage.SetBind(keyEnum, kind) kind: "Toggle" | "Hold" | "Always"; keyEnum: Enum.KeyCode.X Rage.Tags player tag registry: Tags:Add(player, "Hacker") feeds PrioritizeHackers Rage.IsReady true once it has booted Rage.Start() boot now (automatic unless getgenv().ZkxhubRagebotManualStart == true was set before running) Rage.Status short text: Starting / Waiting ... / Ready / Aborted ... / Boot failed ... Rage.Unload() tear everything down (restores FallenPartsDestroyHeight / fflags) Every setting and what it does: see "ZHub ragebot - features.md" next to this file. ]] if getgenv().ZkxhubRagebot and getgenv().ZkxhubRagebot.Unload then pcall(getgenv().ZkxhubRagebot.Unload) end if not game:IsLoaded() then game.Loaded:Wait() end local Zkxhub: { [string]: any } = { connections = {}, cleanups = {}, destroyed = false } getgenv().ZkxhubRagebot = Zkxhub local Players = game:GetService('Players') local _ReplicatedStorage = game:GetService('ReplicatedStorage') local _RunService = game:GetService('RunService') local UserInputService = game:GetService('UserInputService') local _CollectionService = game:GetService('CollectionService') local _HttpService = game:GetService('HttpService') local _SoundService = game:GetService('SoundService') local _Lighting = game:GetService('Lighting') local _player = Players.LocalPlayer local rawget, rawset = rawget, rawset function Zkxhub.fn(name) local v pcall(function() v = getgenv()[name] end) if type(v) ~= 'function' then pcall(function() v = getgenv()[name] end) end return type(v) == 'function' and v or nil end local getthreadidentity_ = Zkxhub.fn('getthreadidentity') or Zkxhub.fn('getidentity') local setthreadidentity_ = Zkxhub.fn('setthreadidentity') or Zkxhub.fn('setidentity') local _sethiddenproperty = Zkxhub.fn('sethiddenproperty') local gethui_ = Zkxhub.fn('gethui') local function identity(n) if not setthreadidentity_ then return function() end end local old = getthreadidentity_ and getthreadidentity_() or nil pcall(setthreadidentity_, n) return function() if old then pcall(setthreadidentity_, old) end end end Zkxhub.identity = identity local function hudParent() local ok, h = pcall(function() return gethui_ and gethui_() end) if ok and h then return h end return game:GetService('CoreGui') end Zkxhub.hudParent = hudParent function Zkxhub.track(c) Zkxhub.connections[#Zkxhub.connections + 1] = c return c end function Zkxhub.onUnload(f) Zkxhub.cleanups[#Zkxhub.cleanups + 1] = f end --========================================================================== -- Signal / Trove (Zkxhub's own small versions) --========================================================================== local Signal = {} Signal.__index = Signal function Signal.new() return setmetatable({ _handlers = {} }, Signal) end function Signal:Connect(fn) local h = { fn = fn, connected = true } table.insert(self._handlers, h) local sig = self return { Connected = true, Disconnect = function(c) if not h.connected then return end h.connected = false c.Connected = false local i = table.find(sig._handlers, h) if i then table.remove(sig._handlers, i) end end, } end function Signal:Once(fn) local c c = self:Connect(function(...) c:Disconnect() fn(...) end) return c end function Signal:Fire(...) local list = table.clone(self._handlers) for _, h in ipairs(list) do if h.connected then task.spawn(h.fn, ...) end end end function Signal:FireSync(...) for _, h in ipairs(table.clone(self._handlers)) do if h.connected then h.fn(...) end end end function Signal:Destroy() table.clear(self._handlers) end Zkxhub.Signal = Signal local Trove = {} Trove.__index = Trove function Trove.new(name) return setmetatable({ _name = name, _items = {} }, Trove) end local function cleanItem(o) local t = typeof(o) if t == 'RBXScriptConnection' then o:Disconnect() elseif t == 'Instance' then pcall(function() o:Destroy() end) elseif t == 'thread' then pcall(task.cancel, o) elseif type(o) == 'function' then pcall(o) elseif type(o) == 'table' then -- some objects (Zkxhub's signal connections) error on unknown fields local function member(k) local ok, v = pcall(function() return o[k] end) return ok and type(v) == 'function' and v or nil end local cancel, getStatus = member('cancel'), member('getStatus') if cancel and getStatus then pcall(cancel, o) return end local destroy = member('Destroy') if destroy then pcall(destroy, o) return end local disconnect = member('Disconnect') if disconnect then pcall(disconnect, o) end end end function Trove:Add(o) table.insert(self._items, o) return o end function Trove:Connect(sig, fn) return self:Add(sig:Connect(fn)) end function Trove:Extend() return self:Add(Trove.new(self._name)) end function Trove:Remove(o, keep) local i = table.find(self._items, o) if i then table.remove(self._items, i) if not keep then cleanItem(o) end end end -- Promise held until it settles; cancelled if the trove is cleaned first. function Trove:AddPromise(promise) if tostring(promise:getStatus()) == 'Started' then self:Add(promise) promise:finally(function() if not self._cleaning then self:Remove(promise, true) end end) end return promise end -- Sub-trove that cleans itself up when `instance` is destroyed. function Trove:AttachExtend(instance) local sub = self:Extend() sub:Connect(instance.Destroying, function() self:Remove(sub) end) return sub end function Trove:Clean() local items = self._items self._items = {} self._cleaning = true for i = #items, 1, -1 do cleanItem(items[i]) end self._cleaning = false end Trove.Destroy = Trove.Clean Zkxhub.Trove = Trove --========================================================================== -- Zkxhub's constant pool (v86[...]) as recovered from usage in the dump --========================================================================== local constantsByIndex = { [3] = 'Toggle', [9] = 20, [12] = 12, [18] = '0', [26] = 4, [34] = true, [44] = 'Enabled', [45] = 90, [48] = 2, [53] = 20, [55] = 'Mode', [56] = 2, [62] = 0.1, [63] = 1, [68] = 'table', [75] = 'None', [83] = 64, [91] = 100, [95] = 'number', [101] = 0.5, [108] = 255, [116] = 'boolean', [118] = 16, [122] = 8, [126] = 0.15, [127] = 'CFrame', [128] = 'Frame', [133] = 10, [137] = 256, [147] = 0.1, [149] = 1, [153] = false, [155] = 3, [160] = 50, [162] = 8, [165] = 'string', [170] = 9, [173] = 0.88, [175] = 5, [186] = 0, [192] = 30, [195] = 0.7, } Zkxhub.C = constantsByIndex --========================================================================== -- Zkxhub's own UI, settings store and Combat menu (lifted from the dump) --========================================================================== local constantPool = { [2] = 'ReactiveStore', [3] = 'Toggle', [4] = 'UIListLayout', [5] = 'BackgroundColor3', [6] = 0, [7] = 60, [8] = 60, [9] = 20, [12] = 12, [13] = 14, [14] = 'Settings', [15] = 120, [16] = 'min', [17] = 200, [18] = '0', [19] = 200, [20] = 'Vector3', [21] = 'Always', [22] = '', [23] = 1.4707963267948965, [24] = 'Roboto', [25] = 24, [26] = 4, [27] = 'Color', [28] = 0.2, [29] = 'Size', [30] = 0.35, [31] = 29, [32] = 24, [33] = 'Button', [34] = true, [35] = 'ConfigManager', [36] = 'ApplyMigrations', [37] = 'TextBounds', [38] = 'DeleteFile', [39] = 'Slider', [40] = '...', [42] = 'left', [43] = 'Proggy Clean', [44] = 'Enabled', [45] = 90, [46] = 'UIGradient', [47] = 'CanvasGroup', [48] = 0.3, [49] = 2, [50] = 'Side', [51] = ' ', [52] = 'BottomRight', [53] = 92, [54] = 6, [55] = 'Mode', [56] = 2, [57] = 'TextColor', [58] = 80, [59] = 'Font', [60] = 29, [61] = 20, [62] = 0.1, [63] = 1, [64] = 'ScrollingFrame', [65] = 200, [66] = 0.5, [67] = 100, [68] = 'table', [69] = 'Catalog unavailable', [70] = 'GradientDark', [71] = 10, [72] = 12, [73] = 'Keybind', [74] = 0.9, [75] = 'None', [76] = 'LoadFromFile', [77] = 'FireServer', [78] = 720, [79] = 255, [80] = 'Text', [81] = 1, [82] = 'Realize() can only be called on the root', [83] = 64, [84] = 0.5, [85] = 'UIStroke', [87] = 0.8, [88] = 'TabHighlight', [89] = 'Loading', [90] = 'Gradient', [91] = 100, [92] = 'TextButton', [93] = 'ColorSequence', [94] = 'Options', [95] = 'number', [96] = 'Unselected', [97] = 'Mode', [98] = 'UICorner', [99] = 'ExportToJson', [100] = 14, [101] = 0.5, [102] = 231, [103] = 52, [104] = 'Fonts', [105] = 0.85, [106] = 'Menu Keybind', [107] = 40, [108] = 255, [109] = 77, [110] = 70, [111] = 'Fetching items...', [112] = 'Invisible', [113] = 'UIPadding', [114] = 'primary', [115] = 'Color3', [116] = 'boolean', [118] = 16, [119] = 159, [120] = 'Outline', [121] = 'ImageButton', [122] = 8, [123] = 19, [124] = 'X', [125] = 'Proggy Tiny', [126] = 0.15, [127] = 'CFrame', [128] = 'Frame', [129] = 'Show Watermark', [131] = 'AbsoluteSize', [132] = 'Breathing', [133] = 10, [134] = 'GradientTop', [135] = 'TextLabel', [136] = 'Unselected Text', [137] = 256, [138] = 0.85, [139] = 'Accent', [140] = 'danger', [141] = 'GradientDeep', [142] = 'Silent Load', [143] = 32, [144] = 20, [145] = 'Dialog has been destroyed', [146] = 'ImageLabel', [147] = 0.1, [148] = 'TextBox', [149] = 1, [150] = 'stop', [151] = 'family', [153] = false, [154] = 'ScrollBarImageColor3', [155] = 3, [156] = 'ImageColor3', [157] = 20, [158] = 'secondary', [159] = 20, [160] = 50, [161] = 0.5, [162] = 8, [163] = 80, [164] = 'hue', [165] = 'string', [166] = 'family', [167] = 'TextColor3', [168] = 'ElementBackground', [169] = 'FromJson', [170] = 9, [172] = 'none', [173] = 0.88, [174] = 'Unselected', [175] = 5, [176] = 'UISizeConstraint', [177] = 'Try a different search.', [178] = 'ElementBackground', [181] = 10, [182] = 'HttpService', [183] = 72, [184] = 'Color', [186] = 0, [187] = 'GradientMid', [189] = 'Position', [190] = 'Search...', [191] = 'TabShadow', [192] = 30, [193] = 0, [194] = 'Hold', [195] = 0.7, [196] = 'Viewport', [197] = 'Y', [198] = 400, [199] = 'Unload', [200] = 26, } -- Environment Zkxhub's modules expect: the module table and the aliases its -- loader set up before them (v102 = rawget, v103 = rawset, v107 = pcall). local modules: { [string]: any } = { cache = {} } local rawGet, rawSet, protectedCall = rawget, rawset, pcall -- The obfuscator's integrity counters. Every check in the lifted code has one -- branch that hangs and one that runs the real code; these values sit inside -- the window where all of them take the real branch (n26 in [4790, 4809), -- n25 in [3866, 3887]). local integrityCounterA, integrityCounterB = 3870, 4800 local integrityFlagA, integrityFlagB = true, true local function integrityProbeA(...) return 0 end local cloneref = Zkxhub.fn('cloneref') or function(x) return x end local gethui = Zkxhub.fn('gethui') or function() return game:GetService('CoreGui') end local getthreadidentity = Zkxhub.fn('getthreadidentity') or Zkxhub.fn('getidentity') or function() return 8 end local setthreadidentity = Zkxhub.fn('setthreadidentity') or Zkxhub.fn('setidentity') or function() end -- The rest of the loader's locals (clonefunction'd so hooks on them can't see us). local function clonefn(f) local c = Zkxhub.fn('clonefunction') if c and f then local ok, r = pcall(c, f) if ok and r then return r end end return f end local fireServer = clonefn(Instance.new('RemoteEvent').FireServer) local fireServer2 = clonefn(Instance.new('UnreliableRemoteEvent').FireServer) local setHiddenProperty = clonefn(Zkxhub.fn('sethiddenproperty')) local setFFlag = clonefn(Zkxhub.fn('setfflag')) or function() end local isExecutorClosure = clonefn(Zkxhub.fn('isexecutorclosure')) or function() return false end local setRawMetatable = clonefn(Zkxhub.fn('setrawmetatable')) or setmetatable local getRawMetatable = clonefn(Zkxhub.fn('getrawmetatable')) or getmetatable local gameNewIndex = clonefn(getRawMetatable(game).__newindex) local gameIndex = clonefn(getRawMetatable(game).__index) local findFirstChildOfClass = clonefn(game.FindFirstChildOfClass) local function _integrityProbeB() return 0 end local firetouchinterest = Zkxhub.fn('firetouchinterest') or function() end local getconnections = Zkxhub.fn('getconnections') or function() return {} end local setclipboard = Zkxhub.fn('setclipboard') or Zkxhub.fn('toclipboard') or function() end local InstanceHandle pcall(function() InstanceHandle = getgenv().InstanceHandle end) if InstanceHandle == nil then pcall(function() InstanceHandle = getgenv().InstanceHandle end) end if InstanceHandle == nil then InstanceHandle = { new = function(x) return x end } end -- The modules the decompiler lost (their bodies are `fn35(...) end` in -- the dump), rebuilt from how the rest of Zkxhub's code calls them. -- k: Trove modules.k = function() return { new = function(name) return Trove.new(name) end } end -- Zkxhub's own modules, lifted from the dump (extract.py). Do not edit by hand. do -- bG (standalone: ragebot-only config store, replaces the hub's full schema) local function buildRagebotConfig() local ReactiveStore = modules.l() local ValueCodec = modules.m() local GlobalTrove = modules.n() local atomic = modules.i().atomic local DefaultConfig = { Ragebot = { Enabled = false, Keybind = { State = false, Kind = 'Always', Bind = nil, ShowInList = true, Invisible = false }, Stability = 0.15, ShootFrames = 1, PrioritizeHackers = false, Weapons = { Priority = atomic({ 'Primary', 'Secondary', 'Melee' }), Enabled = { Primary = true, Secondary = true, Melee = true }, OnEmpty = 'SwapOrReload', }, Evasion = { Mode = 'Random', Random = { AnchorFromCharacter = false, BaseRadius = 100, RadiusRandomFactor = 0.5 }, ProjectileBreaker = { DepthForward = { Min = 0, Max = 4 }, DepthForwardFrequency = 5, DepthUp = { Min = 0, Max = 5.5 }, DepthUpFrequency = 5, RepositionInterval = 0.3, FallbackAnchorFromCharacter = false, FallbackBaseRadius = 100, FallbackRadiusRandomFactor = 0.5, }, Translocate = { Offset = -5 }, }, UtilizeHealthLead = false, }, } return ( GlobalTrove:Add(ReactiveStore.new({ DefaultConfig = DefaultConfig, CurrentVersion = 1, LegacyVersion = 1, SavePath = '', Migrations = {}, Serialize = ValueCodec.normalize, Deserialize = ValueCodec.parse, })) ) end modules.bG = function() local cached = modules.cache.bG if not cached then cached = { c = buildRagebotConfig() } modules.cache.bG = cached end return cached.c end end do -- a Result: ok / err results (source, stage, detail) local function buildResult() return { ok = function(value) return { Ok = true, Value = value } end, err = function(source, stage, detail) return { Ok = false, Error = { Source = source, Stage = stage, Detail = detail, Timestamp = os.clock() } } end, formatError = function(failure) local toStr = tostring local detail = failure.Detail return string.format( "'%s' @ %s failed during stage '%s'; %s", tostring(failure.Source), tostring(failure.Timestamp), tostring(failure.Stage), toStr(detail) ) end, VoidOk = table.freeze({ Ok = true, Value = nil }), } end modules.a = function() local cached = modules.cache.a if not cached then cached = { c = buildResult() } modules.cache.a = cached end return cached.c end end do -- b errorSink: holder of the active error reporter local function buildErrorSink() modules.a() local sink = nil return { use = function(value) sink = value end, get = function() assert(sink) return sink end, } end modules.b = function() local cached = modules.cache.b if not cached then cached = { c = buildErrorSink() } modules.cache.b = cached end return cached.c end end do -- d deepCopy: recursive table copy local function buildDeepCopy() local function copy(t) if type(t) ~= 'table' then return t end local o = {} for k, v in next, t do o[k] = copy(v) end return setmetatable(o, getmetatable(t)) end return copy end modules.d = function() local cached = modules.cache.d if not cached then cached = { c = buildDeepCopy() } modules.cache.d = cached end return cached.c end end do -- e ensureFolderPath: create nested folders local function buildEnsureFolderPath() local Result = modules.a() return function(path, builtPath) local parts = path:split('/') builtPath = builtPath or '' for k, part in parts, nil, nil do if part == '' then continue end local str7 = k == #parts and '' or '/' local toStr = tostring builtPath ..= string.format('%s%s', tostring(part), toStr(str7)) if isfile(builtPath) then return Result.err( 'ensureFolderPath', 'collision', string.format('expected `%s` to be non-existant or a folder, found a file instead.', tostring(builtPath)) ) end if not isfolder(builtPath) then makefolder(builtPath) end end return Result.VoidOk end end modules.e = function() local cached = modules.cache.e if not cached then cached = { c = buildEnsureFolderPath() } modules.cache.e = cached end return cached.c end end do -- f ConfigManager: config files (JSON, versions, migrations) local function buildConfigManager() local Result = modules.a() local deepCopy = modules.d() local ensureFolderPath = modules.e() local httpService = cloneref(game:GetService(constantPool[182])) local ConfigManager = {} ConfigManager.__index = ConfigManager local function cloneIfTable(value) local tableType = constantPool[68] if type(value) == tableType then return deepCopy(value) end return value end local function ensureTrailingSlash(path) if path == nil or path == '' then return '' end if path:sub(-constantPool[63]) == '/' then return path end return path .. '/' end local function validateConfigName(name) if name == '' then return Result.err('ConfigManager', 'validateConfigName', 'config name cannot be empty') end if name:find('/') or name:find('\\') then return Result.err('ConfigManager', 'validateConfigName', 'expected no directory traversal') end return Result.VoidOk end ConfigManager.new = function(options) assert(type(options.CurrentVersion) == 'number' and options.CurrentVersion >= constantPool[63], 'expected `CurrentVersion` >= 1') local savePath = ensureTrailingSlash(options.SavePath) if savePath ~= '' then local folderResult = ensureFolderPath(savePath) if not folderResult.Ok then error(folderResult.Error.Detail, 2) end end local serialize = options.Serialize or function(value) return value end local deserialize = options.Deserialize or function(value) return value end local defaultConfig = cloneIfTable(options.DefaultConfig) local manager = { Data = cloneIfTable(defaultConfig), _defaultConfig = defaultConfig, _currentVersion = options.CurrentVersion, _legacyVersion = options.LegacyVersion or 1, _savePath = savePath, _migrations = options.Migrations or {}, _serialize = serialize, _deserialize = deserialize, } setmetatable(manager, ConfigManager) return manager end ConfigManager._ConfigPath = function(self, name) if name:match('%.json$') then return self._savePath .. name end local toStr = tostring return string.format('%s%s.json', tostring(self._savePath), toStr(name)) end ConfigManager._ApplyMigrations = function(self, fileVersion, data) if fileVersion < 1 then return Result.err('ConfigManager', 'ApplyMigrations', string.format('invalid file version %s', tostring(fileVersion))) end if self._currentVersion < fileVersion then local toStr = tostring local currentVersion = self._currentVersion return Result.err( 'ConfigManager', 'ApplyMigrations', string.format('file version %s is newer than current version %s', tostring(fileVersion), toStr(currentVersion)) ) end local migrated = cloneIfTable(data) while fileVersion < self._currentVersion do local migration = self._migrations[fileVersion] if migration == nil then local toStr = tostring return Result.err( 'ConfigManager', constantPool[36], string.format('missing migration for version %s -> %s', tostring(fileVersion), toStr(fileVersion + 1)) ) end local ok, result = protectedCall(migration, migrated, fileVersion, fileVersion + constantPool[63]) if not ok then local toStr = tostring return Result.err( constantPool[35], 'ApplyMigrations', string.format('migration %s -> %s failed: %s', tostring(fileVersion), tostring(fileVersion + constantPool[63]), toStr(result)) ) end if result == nil then local toStr = tostring return Result.err( 'ConfigManager', 'ApplyMigrations', string.format('migration %s -> %s returned nil', tostring(fileVersion), toStr(fileVersion + 1)) ) end fileVersion += 1 migrated = result end return Result.ok(migrated) end ConfigManager.SetData = function(self, data) self.Data = data end ConfigManager.Reset = function(self) local fresh = deepCopy(self._defaultConfig) self.Data = fresh return fresh end ConfigManager.ToJson = function(self, data) if data == nil then data = self.Data end local serializeOk, serialized = protectedCall(self._serialize, data) if not serializeOk then return Result.err('ConfigManager', 'ToJson', string.format('failed to serialize config: %s', tostring(serialized))) end local encodeOk, json = protectedCall(httpService.JSONEncode, httpService, { Version = self._currentVersion, Data = serialized }) if not encodeOk then return Result.err( 'ConfigManager', 'ToJson', string.format('failed to encode config payload as JSON: %s', tostring(json)) ) end return Result.ok(json) end ConfigManager.FromJson = function(self, json) local decodeOk, payload = protectedCall(httpService.JSONDecode, httpService, json) if not decodeOk then return Result.err(constantPool[35], constantPool[169], string.format('failed to decode JSON payload: %s', tostring(payload))) end if type(payload) ~= 'table' then return Result.err('ConfigManager', 'FromJson', 'expected decoded config JSON to be a table') end local legacyVersion = self._legacyVersion local version = payload.Version local data = payload.Data if version == nil and data == nil then version = payload.version data = payload.data end if not (type(version) == 'number' and data ~= nil) then data = payload version = legacyVersion end local migrated = self:_ApplyMigrations(version, data) if not migrated.Ok then return Result.err('ConfigManager', constantPool[169], migrated.Error.Detail) end local deserializeOk, config = protectedCall(self._deserialize, migrated.Value) if not deserializeOk then return Result.err('ConfigManager', 'FromJson', string.format('failed to deserialize config data: %s', tostring(config))) end if config == nil then return Result.err('ConfigManager', 'FromJson', 'deserializer returned nil') end self.Data = config return Result.ok(config) end ConfigManager._SaveImpl = function(self, name, data, noOverwrite) local valid = validateConfigName(name) if not valid.Ok then return valid end local path = self:_ConfigPath(name) if noOverwrite and isfile(path) then return Result.err('ConfigManager', 'SaveImpl', string.format('path `%s` already exists', tostring(path))) end local json = self:ToJson(data) if not json.Ok then return Result.err('ConfigManager', 'SaveImpl', json.Error.Detail) end writefile(path, json.Value) return Result.VoidOk end ConfigManager.SaveToFile = function(self, name, noOverwrite) return self:_SaveImpl(name, nil, noOverwrite) end ConfigManager.LoadFromFile = function(self, name) local valid = validateConfigName(name) if not valid.Ok then return Result.err('ConfigManager', constantPool[76], valid.Error.Detail) end local path = self:_ConfigPath(name) if not isfile(path) then return Result.err(constantPool[35], 'LoadFromFile', string.format('path `%s` does not exist', tostring(path))) end local contents = readfile(path) return self:FromJson(contents) end ConfigManager.Exists = function(self, name) if not validateConfigName(name).Ok then return constantPool[153] end return isfile(self:_ConfigPath(name)) end ConfigManager.SaveDefaultToFile = function(self, name, noOverwrite) return self:_SaveImpl(name, self._defaultConfig, noOverwrite) end ConfigManager.Delete = function(self, name) local valid = validateConfigName(name) if not valid.Ok then return valid end local path = self:_ConfigPath(name) if not isfile(path) then return Result.err('ConfigManager', constantPool[38], string.format('path `%s` does not exist', tostring(path))) end delfile(path) return Result.VoidOk end ConfigManager.AllConfigs = function(self) local names = {} if self._savePath == '' then return names end for _, file in listfiles(self._savePath) do if isfile(file) then local match = (file:match('[/\\]([^/\\]+)$') or file):match('(.+)%.json$') if match ~= nil then table.insert(names, match) end end end return names end return ConfigManager end modules.f = function() local cached = modules.cache.f if not cached then cached = { c = buildConfigManager() } modules.cache.f = cached end return cached.c end end do -- g GoodSignal: Signal / Connection implementation local function buildGoodSignal() local freeRunnerThread local function acquireRunnerThreadAndCallEventHandler(fn, ...) local acquired = freeRunnerThread freeRunnerThread = nil fn(...) freeRunnerThread = acquired end local function runEventHandlerInFreeThread(...) acquireRunnerThreadAndCallEventHandler(...) while true do acquireRunnerThreadAndCallEventHandler(coroutine.yield()) end end local ConnectionClass = {} ConnectionClass.__index = ConnectionClass ConnectionClass.Disconnect = function(self) if not self.Connected then return end self.Connected = false if self._signal._handlerListHead == self then self._signal._handlerListHead = self._next else local node = self._signal._handlerListHead while node and node._next ~= self do node = node._next end if node then node._next = self._next end end end ConnectionClass.Destroy = ConnectionClass.Disconnect setmetatable( ConnectionClass, { __index = function(_, key) error(('Attempt to get Connection::%s (not a valid member)'):format(tostring(key)), 2) end, __newindex = function(_, key, _value) error(('Attempt to set Connection::%s (not a valid member)'):format(tostring(key)), 2) end, } ) local SignalClass = {} SignalClass.__index = SignalClass SignalClass.new = function() return (setmetatable({ _handlerListHead = false, _proxyHandler = nil, _yieldedThreads = nil }, SignalClass)) end SignalClass.Wrap = function(rbxScriptSignal) assert(typeof(rbxScriptSignal) == 'RBXScriptSignal', 'Argument #1 to Signal.Wrap must be a RBXScriptSignal; got ' .. typeof(rbxScriptSignal)) local signal = SignalClass.new() signal._proxyHandler = rbxScriptSignal:Connect(function(...) signal:Fire(...) end) return signal end SignalClass.Is = function(object) return type(object) == 'table' and getmetatable(object) == SignalClass end SignalClass.Connect = function(self, fn) local connection = setmetatable({ Connected = true, _signal = self, _fn = fn, _next = false }, ConnectionClass) if self._handlerListHead then connection._next = self._handlerListHead self._handlerListHead = connection else self._handlerListHead = connection end return connection end SignalClass.ConnectOnce = function(self, fn) return self:Once(fn) end SignalClass.Once = function(self, fn) local connection local done = false connection = self:Connect(function(...) if done then return end done = true connection:Disconnect() fn(...) end) return connection end SignalClass.GetConnections = function(self) local items, item = {}, self._handlerListHead while item do table.insert(items, item) item = item._next end return items end SignalClass.DisconnectAll = function(self) local item = self._handlerListHead while item do item.Connected = false item = item._next end self._handlerListHead = false item = rawGet(self, '_yieldedThreads') if item then for thread in item, nil, nil do if coroutine.status(thread) == 'suspended' then warn((debug.traceback :: any)(thread, 'signal disconnected; yielded thread cancelled', 2)) task.cancel(thread) end end table.clear(self._yieldedThreads) end end SignalClass.Fire = function(self, ...) local item = self._handlerListHead while item do if item.Connected then self = freeRunnerThread if not self then freeRunnerThread = coroutine.create(runEventHandlerInFreeThread) end task.spawn(freeRunnerThread, item._fn, ...) end item = item._next end end SignalClass.FireDeferred = function(self, ...) local item = self._handlerListHead while item do local current = item task.defer(function(...) if current.Connected then current._fn(...) end end, ...) item = item._next end end SignalClass.Wait = function(self) local yielded = rawGet(self, '_yieldedThreads') if not yielded then yielded = {} rawSet(self, '_yieldedThreads', yielded) end local thread = coroutine.running() yielded[thread] = true self:Once(function(...) yielded[thread] = nil if coroutine.status(thread) == 'suspended' then task.spawn(thread, ...) end end) return coroutine.yield() end SignalClass.Destroy = function(self) self:DisconnectAll() local proxy = rawGet(self, '_proxyHandler') if proxy then proxy:Disconnect() end end return table.freeze({ new = SignalClass.new, Wrap = SignalClass.Wrap, Is = SignalClass.Is }) end modules.g = function() local cached = modules.cache.g if not cached then local created = { c = buildGoodSignal() } modules.cache.g = created cached = created end return cached.c end end do -- h ManagedSignal: signal with switchable connections local function buildManagedSignal() local GoodSignal = modules.g() local ManagedSignal = {} ManagedSignal.__index = ManagedSignal local ManagedConnection = {} ManagedConnection.__index = ManagedConnection ManagedConnection.Enable = function(self) if self.Connected then return end self.Connected = true self._inner = self._signal._inner:Connect(self._callback) end ManagedConnection.Disable = function(self) if not self.Connected then return end self.Connected = false local inner = self._inner if inner ~= nil then inner:Disconnect() end self._inner = nil end ManagedConnection.Disconnect = ManagedConnection.Disable ManagedConnection.Destroy = ManagedConnection.Disable ManagedSignal.Connect = function(self, callback) local connection = setmetatable({ _signal = self, _callback = callback, _inner = nil, Connected = false }, ManagedConnection) connection:Enable() return connection end ManagedSignal.Fire = function(self, value) self._inner:Fire(value) end ManagedSignal.FireDeferred = function(self, value) self._inner:FireDeferred(value) end ManagedSignal.Once = function(self, callback) return self._inner:Once(callback) end ManagedSignal.Wait = function(self) return self._inner:Wait() end ManagedSignal.DisconnectAll = function(self) self._inner:DisconnectAll() end ManagedSignal.GetConnections = function(self) return self._inner:GetConnections() end ManagedSignal.Destroy = function(self) self._inner:Destroy() end return table.freeze({ new = function() return (setmetatable({ _inner = GoodSignal.new() }, ManagedSignal)) end }) end modules.h = function() local cached = modules.cache.h if not cached then local created = { c = buildManagedSignal() } modules.cache.h = created cached = created end return cached.c end end do -- i Atomic: marks tables that are never merged or walked local function buildAtomic() local marker = {} return { atomic = function(value) setmetatable(value, marker) return value end, isAtomic = function(value) return type(value) == 'table' and getmetatable(value) == marker end, } end modules.i = function() local cached = modules.cache.i if not cached then local created = { c = buildAtomic() } modules.cache.i = created cached = created end return cached.c end end do -- j PathUtil: path <-> key, navigate / set / walk nested tables local function buildPathUtil() if true then local isAtomic = modules.i().isAtomic local PathUtil PathUtil = { PathToKey = function(path) return table.concat(path, '.') end, KeyToPath = function(key) return key:split('.') end, NavigateTo = function(node, path, silent) if #path == 0 then return nil end for index = 1, #path, 1 do local key = path[index] if index == #path then return node[key] end node = node[key] if node == nil then if silent then return nil end error( string.format( "Invalid path specified '%s', key %s is nil!", tostring(table.concat(path, '.')), tostring(key) ), 2 ) elseif type(node) ~= 'table' then if silent then return nil end error( string.format( "Invalid path specified '%s', key %s is not a branch!", tostring(table.concat(path, '.')), tostring(key) ), 2 ) end end return nil end, Set = function(node, path, value) for index = 1, #path, 1 do local key = path[index] if index == #path then node[key] = value return end local child = node[key] if child == nil then child = {} node[key] = child elseif type(child) ~= 'table' then local pathKey = PathUtil.PathToKey(path) error( string.format("Invalid path specified '%s', key %s is not a table!", tostring(pathKey), tostring(key)), 2 ) end node = child end end, ForEachEntry = function(tree, callback) local path = {} local function walk(branch) for key, child in branch, nil, nil do table.insert(path, key) callback(path, child) if type(child) == 'table' and not isAtomic(child) then walk(child) end table.remove(path) end end walk(tree) end, ForEachLeafValue = function(tree, reference, callback) local path = {} local function walk(branch, refBranch) for key, value in branch, nil, nil do table.insert(path, key) local refChild = if refBranch ~= nil then refBranch[key] else nil local refIsTable = type(refChild) == 'table' key = if type(value) == 'table' and not isAtomic(value) and not (refIsTable and (isAtomic(refChild))) and (refChild == nil or refIsTable) then (walk(value, if refIsTable then refChild else nil)) else if refIsTable and not isAtomic(refChild) then false else callback(path, value) == true table.remove(path) if key then return true end end return false end walk(tree, reference) end, } return PathUtil end return nil -- (anti-tamper freeze trap removed) end modules.j = function() local cached = modules.cache.j if not cached then local created = { c = buildPathUtil() } modules.cache.j = created cached = created end return cached.c end end do -- l ReactiveStore: config store with change signals and live overrides local function buildReactiveStore() local ConfigManager = modules.f() local ManagedSignal = modules.h() local PathUtil = modules.j() local Result = modules.a() local GoodSignal = modules.g() local Trove = modules.k() local deepCopy = modules.d() local isAtomic = modules.i().isAtomic local ReactiveStore = {} ReactiveStore.__index = ReactiveStore ReactiveStore.new = function(options) local trove = Trove.new('ReactiveStore') return setmetatable({ _trove = trove, Default = options.DefaultConfig, Data = deepCopy(options.DefaultConfig), _middleware = {}, _configManager = ConfigManager.new({ DefaultConfig = options.DefaultConfig, CurrentVersion = options.CurrentVersion, LegacyVersion = options.LegacyVersion, SavePath = options.SavePath, Migrations = options.Migrations, Serialize = options.Serialize, Deserialize = options.Deserialize, }), _listenerByPathKey = {}, _listenerTrieRoot = {}, _pendingUpdateByPathKey = {}, _pendingOrder = {}, _isFlushing = false, _isLoading = false, _lastPublishedValueByPathKey = {}, _liveValueByPathKey = {}, Reloaded = trove:Add(GoodSignal.new()), Changed = trove:Add(GoodSignal.new()), }, ReactiveStore) end ReactiveStore.UseMiddleware = function(self, middleware) self._middleware = middleware end local leafKey = {} ReactiveStore.GetPropertyChangedSignal = function(self, path) local pathKey = PathUtil.PathToKey(path) local signal = self._listenerByPathKey[pathKey] if signal == nil then local newSignal = self._trove:Add(ManagedSignal.new()) self._listenerByPathKey[pathKey] = newSignal local listenerTrieRoot = self._listenerTrieRoot for _, key in path, nil, nil do local child = listenerTrieRoot[key] if child ~= nil then listenerTrieRoot = child else local branch = {} listenerTrieRoot[key] = branch listenerTrieRoot = branch end end listenerTrieRoot[leafKey] = newSignal signal = newSignal end return signal end ReactiveStore._FireChanged = function(self, pathKey, value, force) local pending = self._pendingUpdateByPathKey[pathKey] if not force and pending == nil and self._lastPublishedValueByPathKey[pathKey] == value then return end if pending == nil then table.insert(self._pendingOrder, pathKey) end self._pendingUpdateByPathKey[pathKey] = { Value = value } end ReactiveStore._Flush = function(self) if self._isFlushing then return end self._isFlushing = true while #self._pendingOrder > 0 do local order, updates = self._pendingOrder, self._pendingUpdateByPathKey self._pendingUpdateByPathKey = {} self._pendingOrder = {} for _index, pathKey in order, nil, nil do local update, signal = updates[pathKey], self._listenerByPathKey[pathKey] if signal then signal:Fire(update.Value) end self._lastPublishedValueByPathKey[pathKey] = update.Value end end self._isFlushing = false end ReactiveStore._FireForChangedPaths = function(self, paths) local function publish(p) local value = PathUtil.NavigateTo(self.Data, p, true) self:_FireChanged(PathUtil.PathToKey(p), value, type(value) == 'table') end local function below(node, p) for k, child in next, node do if k ~= leafKey and type(child) == 'table' then table.insert(p, k) if child[leafKey] ~= nil then publish(p) end below(child, p) table.remove(p) end end end for _, path in paths do local node, prefix = self._listenerTrieRoot, {} for i = 1, #path do node = node[path[i]] if node == nil then break end prefix[i] = path[i] if node[leafKey] ~= nil then publish(prefix) end if i == #path then below(node, table.clone(prefix)) end end end end ReactiveStore._FireListenersOnly = function(self, path) self:_FireForChangedPaths({ path }) self:_Flush() end ReactiveStore.Get = function(self, path, silent) return PathUtil.NavigateTo(self.Data, path, silent) end ReactiveStore.Set = function(self, path, value) local middleware = self._middleware local scratch = middleware.Set value = if scratch ~= nil then (scratch(middleware, path, value)) else value local scratch2 = self.Data scratch = PathUtil.NavigateTo(scratch2, path, true) ~= value if scratch then PathUtil.Set(scratch2, path, value) end scratch2 = middleware.SetApplied if scratch2 ~= nil then scratch2(middleware, path, value) end if scratch then self:_FireForChangedPaths({ path }) end scratch2 = PathUtil.PathToKey(path) scratch = self._liveValueByPathKey[scratch2] if scratch ~= nil then scratch.Base = value end if self._pendingUpdateByPathKey[scratch2] == nil then self:_FireChanged(scratch2, value) end self:_Flush() if not self._isLoading then self.Changed:Fire(path, value) end end ReactiveStore.SetLiveOverride = function(self, path, value) local pathKey = PathUtil.PathToKey(path) if self._liveValueByPathKey[pathKey] == nil then self._liveValueByPathKey[pathKey] = { Path = path, Base = PathUtil.NavigateTo(self.Data, path, true) } end PathUtil.Set(self.Data, path, value) self:_FireListenersOnly(path, value) end ReactiveStore.ClearLiveOverride = function(self, path) local pathKey = PathUtil.PathToKey(path) local live = self._liveValueByPathKey[pathKey] if live == nil then return end self._liveValueByPathKey[pathKey] = nil PathUtil.Set(self.Data, path, live.Base) self:_FireListenersOnly(path, live.Base) end ReactiveStore.GetBase = function(self, path) local live = self._liveValueByPathKey[PathUtil.PathToKey(path)] if live ~= nil then return live.Base end return PathUtil.NavigateTo(self.Data, path, true) end ReactiveStore.RebaseLiveOverride = function(self, path, base) local live = self._liveValueByPathKey[PathUtil.PathToKey(path)] if live == nil then return constantPool[153] end live.Base = base return true end ReactiveStore.PublishPath = function(self, path) self:_FireListenersOnly(path, PathUtil.NavigateTo(self.Data, path, true)) end ReactiveStore._PersistableData = function(self) local data = self.Data local isPristine = true if next(self._liveValueByPathKey) ~= nil then data = deepCopy(data) for _, live in self._liveValueByPathKey, nil, nil do PathUtil.Set(data, live.Path, live.Base) end isPristine = false end local middleware = self._middleware local persistable = middleware.Persistable local persisted if persistable ~= nil then persisted = persistable(middleware, data, isPristine) else persisted = data end return persisted end ReactiveStore.SaveToFile = function(self, name, noOverwrite) self._configManager:SetData(self:_PersistableData()) return self._configManager:SaveToFile(name, noOverwrite) end ReactiveStore.CreateDefault = function(self, name) return self._configManager:SaveDefaultToFile(name, constantPool[34]) end local mergeDefaults = nil mergeDefaults = function(defaults, loaded) if type(defaults) ~= 'table' or isAtomic(defaults) then local picked = (loaded == nil and { defaults } or { loaded })[1] return (type(picked) == 'table' and { (deepCopy(picked)) } or { picked })[1] end if type(loaded) ~= 'table' then return deepCopy(defaults) end local merged = table.clone(defaults) for k, value in defaults, nil, nil do merged[k] = mergeDefaults(value, loaded[k]) end for k, value in loaded, nil, nil do if defaults[k] == nil then merged[k] = (type(value) == 'table' and { (deepCopy(value)) } or { value })[constantPool[63]] end end return merged end local applyDiff = nil applyDiff = function(target, source, defaults, path, changedPaths) for k, oldValue in target, nil, nil do if not (source ~= nil and source[k] ~= nil) then table.insert(path, k) local defaultValue = (defaults ~= nil and { defaults[k] } or { nil })[1] if type(oldValue) == 'table' and not isAtomic(oldValue) and (defaultValue == nil or type(defaultValue) == 'table' and not isAtomic(defaultValue)) then applyDiff(oldValue, nil, defaultValue, path, changedPaths) end target[k] = nil table.insert(changedPaths, table.clone(path)) table.remove(path) end end if source == nil then return end for k, newValue in source, nil, nil do table.insert(path, k) local existing = target[k] local defaultValue = (defaults ~= nil and { defaults[k] } or { nil })[1] if type(newValue) == 'table' and not isAtomic(newValue) and (defaultValue == nil or type(defaultValue) == 'table' and not isAtomic(defaultValue)) then if type(existing) ~= 'table' or isAtomic(existing) then existing = {} target[k] = existing table.insert(changedPaths, table.clone(path)) end applyDiff(existing, newValue, defaultValue, path, changedPaths) elseif existing ~= newValue then if type(existing) == 'table' and not isAtomic(existing) and (defaultValue == nil or type(defaultValue) == 'table' and not isAtomic(defaultValue)) then applyDiff(existing, nil, defaultValue, path, changedPaths) end target[k] = newValue table.insert(changedPaths, table.clone(path)) end table.remove(path) end end ReactiveStore._ApplyLoadedData = function(self, data) local merged = mergeDefaults(self.Default, data) self._isLoading = true table.clear(self._lastPublishedValueByPathKey) table.clear(self._pendingUpdateByPathKey) table.clear(self._pendingOrder) table.clear(self._liveValueByPathKey) local changedPaths = {} applyDiff(self.Data, merged, self.Default, {}, changedPaths) local loaded = self._middleware.Loaded if loaded ~= nil then loaded(self._middleware, merged) end self:_FireForChangedPaths(changedPaths) self:_Flush() self.Reloaded:Fire() self._isLoading = constantPool[153] end ReactiveStore.LoadFromFile = function(self, name) local result = self._configManager:LoadFromFile(name) if not result.Ok then return Result.err('ReactiveStore', 'LoadFromFile', result.Error.Detail) end local value = result.Value self:_ApplyLoadedData(value) return Result.ok(value) end ReactiveStore.ExportToJson = function(self) local result = self._configManager:ToJson(self:_PersistableData()) if not result.Ok then return Result.err('ReactiveStore', constantPool[99], result.Error.Detail) end return Result.ok(result.Value) end ReactiveStore.InstallFromJson = function(self, name, json) local decoded = self._configManager:FromJson(json) if not decoded.Ok then return Result.err('ReactiveStore', 'InstallFromJson', decoded.Error.Detail) end local saved = self._configManager:SaveToFile(name) if not saved.Ok then return Result.err(constantPool[2], 'InstallFromJson', saved.Error.Detail) end return Result.VoidOk end ReactiveStore.LoadFromJson = function(self, json) local decoded = self._configManager:FromJson(json) if not decoded.Ok then return Result.err(constantPool[2], 'LoadFromJson', decoded.Error.Detail) end self:_ApplyLoadedData(decoded.Value) return Result.VoidOk end ReactiveStore.DeleteFile = function(self, name) local result = self._configManager:Delete(name) if not result.Ok then return Result.err('ReactiveStore', 'DeleteFile', result.Error.Detail) end return Result.VoidOk end ReactiveStore.AllConfigs = function(self) return self._configManager:AllConfigs() end ReactiveStore.Destroy = function(self) self._trove:Destroy() end return ReactiveStore end modules.l = function() local cached = modules.cache.l if not cached then cached = { c = buildReactiveStore() } modules.cache.l = cached end return cached.c end end do -- m ValueCodec: Roblox values <-> plain tables for JSON local function buildValueCodec() local ValueCodec ValueCodec = { normalize = function(config) local normalized = {} for k, value in config, nil, nil do local kind = typeof(value) local encoded if kind == 'Color3' then encoded = { __type = constantPool[115], R = value.R, G = value.G, B = value.B } elseif kind == 'EnumItem' then encoded = { __type = 'EnumItem', EnumType = tostring(value.EnumType), Name = value.Name } elseif kind == 'Vector3' then encoded = { __type = constantPool[20], X = value.X, Y = value.Y, Z = value.Z } elseif kind == constantPool[127] then encoded = { __type = 'CFrame', Components = { value:GetComponents() } } elseif kind == 'ColorSequence' then local keypoints = table.create(#value.Keypoints) for k2, keypoint in value.Keypoints, nil, nil do keypoints[k2] = { Time = keypoint.Time, R = keypoint.Value.R, G = keypoint.Value.G, B = keypoint.Value.B } end encoded = { __type = constantPool[93], Keypoints = keypoints } elseif kind == 'NumberSequence' then local keypoints = table.create(#value.Keypoints) for k2, keypoint in value.Keypoints, nil, nil do keypoints[k2] = { Time = keypoint.Time, Value = keypoint.Value, Envelope = keypoint.Envelope } end encoded = { __type = 'NumberSequence', Keypoints = keypoints } elseif kind == 'table' then encoded = ValueCodec.normalize(value) else encoded = value end normalized[k] = encoded end return normalized end, parse = function(encoded) local decoded = {} for k, value in encoded, nil, nil do if typeof(value) == 'table' then local type_ = value.__type if type(type_) ~= 'string' then decoded[k] = ValueCodec.parse(value) else if type_ == 'Color3' then value = Color3.new(value.R, value.G, value.B) elseif type_ == 'EnumItem' then value = Enum[value.EnumType][value.Name] elseif type_ == 'Vector3' then value = Vector3.new(value.X, value.Y, value.Z) elseif type_ == 'CFrame' then value = CFrame.new(unpack(value.Components)) elseif type_ == constantPool[93] then local keypoints = table.create(#value.Keypoints) for k2, keypoint in value.Keypoints, nil, nil do keypoints[k2] = ColorSequenceKeypoint.new(keypoint.Time, Color3.new(keypoint.R, keypoint.G, keypoint.B)) end value = ColorSequence.new(keypoints) elseif type_ == 'NumberSequence' then local keypoints = table.create(#value.Keypoints) for k2, keypoint in value.Keypoints, nil, nil do keypoints[k2] = NumberSequenceKeypoint.new(keypoint.Time, keypoint.Value, keypoint.Envelope) end value = NumberSequence.new(keypoints) end decoded[k] = value end continue end decoded[k] = value end return decoded end, } return ValueCodec end modules.m = function() local cached = modules.cache.m if not cached then local created = { c = buildValueCodec() } modules.cache.m = created cached = created end return cached.c end end do -- n GlobalTrove: root cleanup container local function buildGlobalTrove() local GlobalTrove = modules.k().new('GlobalTrove') Zkxhub.GlobalTrove = GlobalTrove return GlobalTrove end modules.n = function() local cached = modules.cache.n if not cached then local created = { c = buildGlobalTrove() } modules.cache.n = created cached = created end return cached.c end end do -- ad ErrorReporter: error log that forwards to the sink local function buildErrorReporter() local errorSink = modules.b() modules.a() local ErrorReporter = {} ErrorReporter.__index = ErrorReporter ErrorReporter.new = function() local reporter = { _errors = {}, _sink = errorSink.get() } setmetatable(reporter, ErrorReporter) return reporter end ErrorReporter.Report = function(self, failure) local errors = self._errors if #errors >= constantPool[137] then table.remove(errors, constantPool[63]) end table.insert(errors, failure) local sink = self._sink if sink ~= nil then sink:Report(failure) end end ErrorReporter.ReportResult = function(self, result) if not result.Ok then self:Report(result.Error) end return result end ErrorReporter.GetErrors = function(self) return self._errors end ErrorReporter.Destroy = function(self) table.clear(self._errors) self._sink = nil end return ErrorReporter end modules.ad = function() local cached = modules.cache.ad if not cached then local created = { c = buildErrorReporter() } modules.cache.ad = created cached = created end return cached.c end end do -- ay Promise (evaera): promises, timeouts, retries local function buildPromise() local weakKeyMetatable = { __mode = 'k' } local function isCallable(value) if type(value) == 'function' then return true end if type(value) == 'table' then local metatable = getmetatable(value) if metatable and type(rawGet(metatable, '__call')) == 'function' then return true end end return false end local function makeEnum(enumName, members) local enum = {} for _, member in ipairs(members) do enum[member] = member end return setmetatable( enum, { __index = function(_, key) error(string.format('%s is not in %s!', key, enumName), 2) end, __newindex = function() error(string.format('Creating new members in %s is not allowed!', enumName), 2) end, } ) end local PromiseError: any PromiseError = { Kind = makeEnum('Promise.Error.Kind', { 'ExecutionError', 'AlreadyCancelled', 'NotResolvedInTime', 'TimedOut' }) } PromiseError.__index = PromiseError PromiseError.new = function(options, parent) options = options or {} return setmetatable( { error = tostring(options.error) or '[This error has no error text.]', trace = options.trace, context = options.context, kind = options.kind, parent = parent, createdTick = os.clock(), createdTrace = debug.traceback(), }, PromiseError ) end PromiseError.is = function(value) if type(value) == 'table' then local metatable = getmetatable(value) if type(metatable) == 'table' then return rawGet(value, 'error') ~= nil and type(rawGet(metatable, 'extend')) == 'function' end end return false end PromiseError.isKind = function(err, kind) assert(kind ~= nil, 'Argument #2 to Promise.Error.isKind must not be nil') return PromiseError.is(err) and err.kind == kind end PromiseError.extend = function(self, options) options = options or {} options.kind = options.kind or self.kind return PromiseError.new(options, self) end PromiseError.getErrorChain = function(self) local chain = { self } while chain[#chain].parent do table.insert(chain, chain[#chain].parent) end return chain end PromiseError.__tostring = function(self) local parts = { string.format('-- Promise.Error(%s) --', self.kind or '?') } for _, err in ipairs(self:getErrorChain()) do table.insert(parts, table.concat({ err.trace or err.error, err.context }, '\10')) end return table.concat(parts, '\10') end local function pack(...) return select('#', ...), { ... } end local function packResult(success, ...) return success, select('#', ...), { ... } end local function makeErrorHandler(traceback) assert(traceback ~= nil, 'traceback is nil') return function(err) if type(err) == 'table' then return err end return PromiseError.new({ error = err, kind = PromiseError.Kind.ExecutionError, trace = debug.traceback(tostring(err), 2), context = 'Promise created at:\10\10' .. traceback, }) end end local function runExecutor(traceback, callback, ...) return packResult(xpcall(callback, makeErrorHandler(traceback), ...)) end local function createAdvancer(traceback, callback, resolve, reject) return function(...) local ok, resultLength, values = runExecutor(traceback, callback, ...) if ok then resolve(unpack(values, 1, resultLength)) else reject(values[1]) end end end local function isEmpty(tbl) return next(tbl) == nil end local Promise: any = { Error = PromiseError, Status = makeEnum('Promise.Status', { 'Started', 'Resolved', 'Rejected', 'Cancelled' }), _getTime = os.clock, _timeEvent = game:GetService('RunService').Heartbeat, _unhandledRejectionCallbacks = {}, prototype = {}, } Promise.__index = Promise.prototype Promise._new = function(traceback, callback, parent) if parent ~= nil and not Promise.is(parent) then error('Argument #2 to Promise.new must be a promise or nil', 2) end local promise = { _thread = nil, _source = traceback, _status = Promise.Status.Started, _values = nil, _valuesLength = -1, _unhandledRejection = true, _queuedResolve = {}, _queuedReject = {}, _queuedFinally = {}, _cancellationHook = nil, _parent = parent, _consumers = setmetatable({}, weakKeyMetatable), } if parent and parent._status == Promise.Status.Started then parent._consumers[promise] = true end setmetatable(promise, Promise) local function resolve(...) promise:_resolve(...) end local function reject(...) promise:_reject(...) end local function onCancel(cancellationHook) if cancellationHook then if promise._status == Promise.Status.Cancelled then cancellationHook() else promise._cancellationHook = cancellationHook end end return promise._status == Promise.Status.Cancelled end promise._thread = coroutine.create(function() local ok, _resultLength, values = runExecutor(promise._source, callback, resolve, reject, onCancel) if not ok then reject(values[1]) end end) task.spawn(promise._thread) return promise end Promise.new = function(executor) return Promise._new(debug.traceback(nil, 2), executor) end Promise.__tostring = function(self) return string.format('Promise(%s)', self._status) end Promise.defer = function(callback) local traceback = debug.traceback(nil, 2) return ( Promise._new(traceback, function(resolve, reject, onCancel) local connection connection = Promise._timeEvent:Connect(function() connection:Disconnect() local ok, _resultLength, values = runExecutor(traceback, callback, resolve, reject, onCancel) if not ok then reject(values[1]) end end) end) ) end Promise.async = Promise.defer Promise.resolve = function(...) local length, values = pack(...) return Promise._new(debug.traceback(nil, 2), function(resolve) resolve(unpack(values, 1, length)) end) end Promise.reject = function(...) local length, values = pack(...) return Promise._new(debug.traceback(nil, 2), function(_, reject) reject(unpack(values, 1, length)) end) end Promise._try = function(traceback, callback, ...) local length, values = pack(...) return Promise._new(traceback, function(resolve) resolve(callback(unpack(values, 1, length))) end) end Promise.try = function(callback, ...) return Promise._try(debug.traceback(nil, 2), callback, ...) end Promise._all = function(traceback, promises, amount) if type(promises) ~= 'table' then error(string.format('Please pass a list of promises to %s', 'Promise.all'), 3) end for index, promise in pairs(promises) do if not Promise.is(promise) then error(string.format('Non-promise value passed into %s at index %s', 'Promise.all', tostring(index)), 3) end end if #promises == 0 or amount == 0 then return Promise.resolve({}) end return Promise._new(traceback, function(resolve, reject, onCancel) local resolvedValues, newPromises, resolvedCount, rejectedCount, done = {}, {}, 0, 0, false local function cancelAll() for _, promise in ipairs(newPromises) do promise:cancel() end end local function resolveOne(index, ...) if done then return end resolvedCount += 1 if amount == nil then resolvedValues[index] = ... else resolvedValues[resolvedCount] = ... end if resolvedCount >= (amount or #promises) then done = true resolve(resolvedValues) cancelAll() end end onCancel(cancelAll) for index, promise in ipairs(promises) do newPromises[index] = promise:andThen(function(...) resolveOne(index, ...) end, function(...) rejectedCount += 1 if amount == nil or #promises - rejectedCount < amount then cancelAll() done = true reject(...) end end) end if done then cancelAll() end end) end Promise.all = function(promises) return Promise._all(debug.traceback(nil, 2), promises) end Promise.fold = function(list, callback, initialValue) assert(type(list) == 'table', 'Bad argument #1 to Promise.fold: must be a table') assert(isCallable(callback), 'Bad argument #2 to Promise.fold: must be a function') local accumulator = Promise.resolve(initialValue) return Promise.each(list, function(value, index) accumulator = accumulator:andThen(function(previous) return callback(previous, value, index) end) end):andThen(function() return accumulator end) end Promise.some = function(promises, amount) assert(type(amount) == 'number', 'Bad argument #2 to Promise.some: must be a number') return Promise._all(debug.traceback(nil, 2), promises, amount) end Promise.any = function(promises) return Promise._all(debug.traceback(nil, 2), promises, 1):andThen(function(values) return values[1] end) end Promise.allSettled = function(promises) if type(promises) ~= 'table' then error(string.format('Please pass a list of promises to %s', 'Promise.allSettled'), 2) end for index, promise in pairs(promises) do if not Promise.is(promise) then error(string.format('Non-promise value passed into %s at index %s', 'Promise.allSettled', tostring(index)), 2) end end if #promises == 0 then return Promise.resolve({}) end return Promise._new(debug.traceback(nil, 2), function(resolve, _reject, onCancel) local fates, newPromises, finished = {}, {}, 0 local function resolveOne(index, ...) finished += 1 fates[index] = ... if finished >= #promises then resolve(fates) end end onCancel(function() for _, promise in ipairs(newPromises) do promise:cancel() end end) for index, promise in ipairs(promises) do newPromises[index] = promise:finally(function(...) resolveOne(index, ...) end) end end) end Promise.race = function(promises) assert(type(promises) == 'table', string.format('Please pass a list of promises to %s', 'Promise.race')) for index, promise in pairs(promises) do assert(Promise.is(promise), string.format('Non-promise value passed into %s at index %s', 'Promise.race', tostring(index))) end return Promise._new(debug.traceback(nil, 2), function(resolve, reject, onCancel) local newPromises, resolved = {}, false local function cancelAll() for _, promise in ipairs(newPromises) do promise:cancel() end end local function finalize(callback) return function(...) cancelAll() resolved = true return callback(...) end end if onCancel((finalize(reject))) then return end for index, promise in ipairs(promises) do newPromises[index] = promise:andThen(finalize(resolve), (finalize(reject))) end if resolved then cancelAll() end end) end Promise.each = function(list, predicate) assert(type(list) == 'table', string.format('Please pass a list of promises to %s', 'Promise.each')) assert(isCallable(predicate), string.format('Please pass a handler function to %s!', 'Promise.each')) return Promise._new(debug.traceback(nil, 2), function(resolve, reject, onCancel) local results, promisesToCancel, cancelled = {}, {}, false local function cancelAll() for _, promise in ipairs(promisesToCancel) do promise:cancel() end end onCancel(function() cancelled = true cancelAll() end) onCancel = {} for index, value in ipairs(list) do if Promise.is(value) then if value:getStatus() == Promise.Status.Cancelled then cancelAll() return reject( PromiseError.new({ error = 'Promise is cancelled', kind = PromiseError.Kind.AlreadyCancelled, context = string.format( 'The Promise that was part of the array at index %d passed into Promise.each was already cancelled when Promise.each began.\10\10That Promise was created at:\10\10%s', index, value._source ), }) ) elseif value:getStatus() == Promise.Status.Rejected then cancelAll() return reject(select(2, value:await())) end local fillPromise = value:andThen(function(...) return ... end) table.insert(promisesToCancel, fillPromise) onCancel[index] = fillPromise else onCancel[index] = value end end for index, value in ipairs(onCancel) do if Promise.is(value) then local ok ok, value = value:await() if not ok then cancelAll() return reject(value) end end if cancelled then return end local predicatePromise = Promise.resolve(predicate(value, index)) table.insert(promisesToCancel, predicatePromise) local ok, result = predicatePromise:await() if not ok then cancelAll() return reject(result) end results[index] = result end resolve(results) return nil end) end Promise.is = function(value) if type(value) ~= 'table' then return false end local metatable = getmetatable(value) if metatable == Promise then return true elseif metatable == nil then return isCallable(value.andThen) elseif type(metatable) == 'table' and type(rawGet(metatable, '__index')) == 'table' and (isCallable(rawGet(rawGet(metatable, '__index'), 'andThen'))) then return true end return false end Promise.promisify = function(callback) return function(...) return Promise._try(debug.traceback(nil, 2), callback, ...) end end do local first, connection Promise.delay = function(seconds) assert(type(seconds) == 'number', 'Bad argument #1 to Promise.delay, must be a number.') if not (seconds >= 0.016666666666666666) or seconds == math.huge then seconds = 0.016666666666666666 end return Promise._new(debug.traceback(nil, 2), function(resolve, startTime, onCancel) startTime = Promise._getTime() local endTime = startTime + seconds local node = { resolve = resolve, startTime = startTime, endTime = endTime } if connection == nil then first = node connection = Promise._timeEvent:Connect(function() local now = Promise._getTime() while first ~= nil and first.endTime < now do local current = first local finished = current first = current.next if first == nil then connection:Disconnect() connection = nil else first.previous = nil end finished.resolve(Promise._getTime() - finished.startTime) end end) elseif first.endTime < endTime then startTime = first resolve = startTime.next while resolve ~= nil and resolve.endTime < endTime do resolve, startTime = resolve.next, resolve end startTime.next = node node.previous = startTime if resolve ~= nil then node.next = resolve resolve.previous = node end else node.next = first first.previous = node first = node end onCancel(function() local nextNode = node.next if first == node then if nextNode == nil then connection:Disconnect() connection = nil else nextNode.previous = nil end first = nextNode else local previous = node.previous previous.next = nextNode if nextNode ~= nil then nextNode.previous = previous end end end) end) end end Promise.prototype.timeout = function(self, seconds, rejectionValue) local traceback = debug.traceback(nil, 2) return Promise.race({ Promise.delay(seconds):andThen( function() return Promise.reject( rejectionValue == nil and (PromiseError.new({ kind = PromiseError.Kind.TimedOut, error = 'Timed out', context = string.format( 'Timeout of %d seconds exceeded.\10:timeout() called at:\10\10%s', seconds, traceback ), })) or rejectionValue ) end ), self, }) end Promise.prototype.getStatus = function(self) return self._status end Promise.prototype._andThen = function(self, traceback, successHandler, failureHandler) self._unhandledRejection = false if self._status == Promise.Status.Cancelled then local promise = Promise.new(function() end) promise:cancel() return promise end return Promise._new(traceback, function(resolve, reject, onCancel) local successCallback = resolve if successHandler then successCallback = createAdvancer(traceback, successHandler, resolve, reject) end local failureCallback = reject if failureHandler then failureCallback = createAdvancer(traceback, failureHandler, resolve, reject) end if self._status == Promise.Status.Started then table.insert(self._queuedResolve, successCallback) table.insert(self._queuedReject, failureCallback) onCancel(function() if self._status == Promise.Status.Started then table.remove(self._queuedResolve, table.find(self._queuedResolve, successCallback)) table.remove(self._queuedReject, table.find(self._queuedReject, failureCallback)) end end) elseif self._status == Promise.Status.Resolved then successCallback(unpack(self._values, 1, self._valuesLength)) elseif self._status == Promise.Status.Rejected then failureCallback(unpack(self._values, 1, self._valuesLength)) end end, self) end Promise.prototype.andThen = function(self, successHandler, failureHandler) assert(successHandler == nil or (isCallable(successHandler)), string.format('Please pass a handler function to %s!', 'Promise:andThen')) assert(failureHandler == nil or (isCallable(failureHandler)), string.format('Please pass a handler function to %s!', 'Promise:andThen')) return self:_andThen(debug.traceback(nil, 2), successHandler, failureHandler) end Promise.prototype.catch = function(self, failureHandler) assert(failureHandler == nil or (isCallable(failureHandler)), string.format('Please pass a handler function to %s!', 'Promise:catch')) return self:_andThen(debug.traceback(nil, 2), nil, failureHandler) end Promise.prototype.tap = function(self, tapHandler) assert(isCallable(tapHandler), string.format('Please pass a handler function to %s!', 'Promise:tap')) return self:_andThen(debug.traceback(nil, 2), function(...) local result = tapHandler(...) if Promise.is(result) then local length, values = pack(...) return result:andThen(function() return unpack(values, 1, length) end) end return ... end) end Promise.prototype.andThenCall = function(self, callback, ...) assert(isCallable(callback), string.format('Please pass a handler function to %s!', 'Promise:andThenCall')) local length, values = pack(...) return self:_andThen(debug.traceback(nil, 2), function() return callback(unpack(values, 1, length)) end) end Promise.prototype.andThenReturn = function(self, ...) local length, values = pack(...) return self:_andThen(debug.traceback(nil, 2), function() return unpack(values, 1, length) end) end Promise.prototype.cancel = function(self) if self._status ~= Promise.Status.Started then return end self._status = Promise.Status.Cancelled if self._cancellationHook then self._cancellationHook() end coroutine.close(self._thread) if self._parent then self._parent:_consumerCancelled(self) end for consumer in pairs(self._consumers) do consumer:cancel() end self:_finalize() end Promise.prototype._consumerCancelled = function(self, consumer) if self._status ~= Promise.Status.Started then return end self._consumers[consumer] = nil if next(self._consumers) == nil then self:cancel() end end Promise.prototype._finally = function(self, traceback, finallyHandler) self._unhandledRejection = false return ( Promise._new(traceback, function(resolve, reject, onCancel) local upstreamPromise onCancel(function() self:_consumerCancelled(self) if upstreamPromise then upstreamPromise:cancel() end end) onCancel = if finallyHandler then function(...) local returned = finallyHandler(...) if Promise.is(returned) then upstreamPromise = returned returned:finally(function(status) if status ~= Promise.Status.Rejected then resolve(self) end end):catch(function(...) reject(...) end) else resolve(self) end end else resolve if self._status == Promise.Status.Started then table.insert(self._queuedFinally, onCancel) else onCancel(self._status) end end) ) end Promise.prototype.finally = function(self, finallyHandler) assert(finallyHandler == nil or (isCallable(finallyHandler)), string.format('Please pass a handler function to %s!', 'Promise:finally')) return self:_finally(debug.traceback(nil, 2), finallyHandler) end Promise.prototype.finallyCall = function(self, callback, ...) assert(isCallable(callback), string.format('Please pass a handler function to %s!', 'Promise:finallyCall')) local length, values = pack(...) return self:_finally(debug.traceback(nil, 2), function() return callback(unpack(values, 1, length)) end) end Promise.prototype.finallyReturn = function(self, ...) local length, values = pack(...) return self:_finally(debug.traceback(nil, 2), function() return unpack(values, 1, length) end) end Promise.prototype.awaitStatus = function(self) self._unhandledRejection = false if self._status == Promise.Status.Started then local thread = coroutine.running() self:finally(function() task.spawn(thread) end):catch(function() end) coroutine.yield() end if self._status == Promise.Status.Resolved then return self._status, unpack(self._values, 1, self._valuesLength) elseif self._status == Promise.Status.Rejected then return self._status, unpack(self._values, 1, self._valuesLength) end return self._status end local function awaitResult(status, ...) return status == Promise.Status.Resolved, ... end Promise.prototype.await = function(self) return awaitResult(self:awaitStatus()) end local function expectResult(status, ...) if status ~= Promise.Status.Resolved then error(... == nil and 'Expected Promise rejected with no value.' or (...), 3) end return ... end Promise.prototype.expect = function(self) return expectResult(self:awaitStatus()) end Promise.prototype.awaitValue = Promise.prototype.expect Promise.prototype._unwrap = function(self) if self._status == Promise.Status.Started then error('Promise has not resolved or rejected.', 2) end return self._status == Promise.Status.Resolved, unpack(self._values, 1, self._valuesLength) end Promise.prototype._resolve = function(self, ...) if self._status ~= Promise.Status.Started then if Promise.is(...) then (...):_consumerCancelled(self) end return end if Promise.is(...) then if select('#', ...) > 1 then warn( ( string.format( 'When returning a Promise from andThen, extra arguments are discarded! See:\10\10%s', self._source ) ) ) end local promise = ... local chained = promise:andThen(function(...) self:_resolve(...) end, function(...) local err = promise._values[1] err = if promise._error then (PromiseError.new({ error = promise._error, kind = PromiseError.Kind.ExecutionError, context = 'No stack trace available as this Promise originated from an older version of the Promise library (< v2)', })) else err if PromiseError.isKind(err, PromiseError.Kind.ExecutionError) then return self:_reject( err:extend({ error = 'This Promise was chained to a Promise that errored.', trace = '', context = string.format( 'The Promise at:\10\10%s\10...Rejected because it was chained to the following Promise, which encountered an error:\10', self._source ), }) ) end self:_reject(...) return nil end) if chained._status == Promise.Status.Cancelled then self:cancel() elseif chained._status == Promise.Status.Started then self._parent = chained chained._consumers[self] = true end return end self._status = Promise.Status.Resolved self._valuesLength, self._values = pack(...) for _, callback in ipairs(self._queuedResolve) do coroutine.wrap(callback)(...) end self:_finalize() end Promise.prototype._reject = function(self, ...) if self._status ~= Promise.Status.Started then return end self._status = Promise.Status.Rejected self._valuesLength, self._values = pack(...) if not isEmpty(self._queuedReject) then for _, callback in ipairs(self._queuedReject) do coroutine.wrap(callback)(...) end else local errorText = tostring(...) coroutine.wrap(function() Promise._timeEvent:Wait() if not self._unhandledRejection then return end local message = string.format('Unhandled Promise rejection:\10\10%s\10\10%s', errorText, self._source) for _, callback in ipairs(Promise._unhandledRejectionCallbacks) do task.spawn(callback, self, unpack(self._values, 1, self._valuesLength)) end if Promise.TEST then return end warn(message) end)() end self:_finalize() end Promise.prototype._finalize = function(self) for _, callback in ipairs(self._queuedFinally) do coroutine.wrap(callback)(self._status) end self._queuedFinally = nil self._queuedReject = nil self._queuedResolve = nil if not Promise.TEST then self._parent = nil self._consumers = nil end task.defer(coroutine.close, self._thread) end Promise.prototype.now = function(self, rejectionValue) local traceback = debug.traceback(nil, 2) if self._status == Promise.Status.Resolved then return self:_andThen(traceback, function(...) return ... end) else return Promise.reject( rejectionValue == nil and (PromiseError.new({ kind = PromiseError.Kind.NotResolvedInTime, error = 'This Promise was not resolved in time for :now()', context = ':now() was called at:\10\10' .. traceback, })) or rejectionValue ) end end Promise.retry = function(callback, times, ...) assert(isCallable(callback), 'Parameter #1 to Promise.retry must be a function') assert(type(times) == 'number', 'Parameter #2 to Promise.retry must be a number') local args, length = { ... }, select('#', ...) return Promise.resolve(callback(...)):catch(function(...) if times > 0 then return Promise.retry(callback, times - 1, unpack(args, 1, length)) else return Promise.reject(...) end end) end Promise.retryWithDelay = function(callback, times, seconds, ...) assert(isCallable(callback), 'Parameter #1 to Promise.retry must be a function') assert(type(times) == 'number', 'Parameter #2 (times) to Promise.retry must be a number') assert(type(seconds) == 'number', 'Parameter #3 (seconds) to Promise.retry must be a number') local args, length = { ... }, select('#', ...) return Promise.resolve(callback(...)):catch(function(...) if times > 0 then Promise.delay(seconds):await() return Promise.retryWithDelay(callback, times - 1, seconds, unpack(args, 1, length)) else return Promise.reject(...) end end) end Promise.fromEvent = function(event, predicate) predicate = predicate or function() return true end return Promise._new(debug.traceback(nil, 2), function(resolve, _reject, onCancel) local connection local resolvedBeforeConnected = false local function disconnect() connection:Disconnect() connection = nil end connection = event:Connect(function(...) local shouldResolve = predicate(...) if shouldResolve == true then resolve(...) if connection then disconnect() else resolvedBeforeConnected = true end elseif type(shouldResolve) ~= 'boolean' then error('Promise.fromEvent predicate should always return a boolean') end end) if resolvedBeforeConnected and connection then disconnect() return end onCancel(disconnect) end) end Promise.onUnhandledRejection = function(callback) table.insert(Promise._unhandledRejectionCallbacks, callback) return function() local index = table.find(Promise._unhandledRejectionCallbacks, callback) if index then table.remove(Promise._unhandledRejectionCallbacks, index) end end end return Promise end modules.ay = function() local cached = modules.cache.ay if not cached then local created = { c = buildPromise() } modules.cache.ay = created cached = created end return cached.c end end do -- aR waitForPath: wait for instances along a path, optionally require local function buildWaitForPath() return function(parent, path, requireModule, timeout) local names if typeof(path) ~= 'string' then names = path else names = { path } end for _, name in names, nil, nil do local child if timeout ~= nil then child = parent:WaitForChild(name, timeout) if child == nil then return nil end else local _t0 = os.clock() while true do child = parent:WaitForChild(name, 5) if child ~= nil then break end print( '[Zkxhub] waiting for ' .. parent:GetFullName() .. ' -> ' .. tostring(name) .. ' (' .. math.floor(os.clock() - _t0) .. 's)' ) if os.clock() - _t0 > 60 then error( '[Zkxhub] timed out waiting for ' .. parent:GetFullName() .. ' -> ' .. tostring(name) .. '. Wrong game? This script is built for Rivals.', 2 ) end end end parent = cloneref(child) end if requireModule then local _ok, _res = pcall(require, parent) if _ok then return _res end for _try = 1, 4 do task.wait(2) _ok, _res = pcall(require, parent) if _ok then return _res end end return require(parent) end return cloneref(parent) end end modules.aR = function() local cached = modules.cache.aR if not cached then cached = { c = buildWaitForPath() } modules.cache.aR = cached end return cached.c end end do -- aS GameModules: remotes, controllers and libraries of the game local function buildGameModules() modules.aP() modules.aL() modules.aN() modules.aM() modules.aQ() modules.aO() local waitForPath = modules.aR() local players = cloneref(game:GetService('Players')) local replicatedStorage = cloneref(game:GetService('ReplicatedStorage')) local localPlayer = players.LocalPlayer local playerScripts = waitForPath(localPlayer, 'PlayerScripts') local controllers = waitForPath(playerScripts, 'Controllers') local modules = waitForPath(replicatedStorage, 'Modules') local modules2 = waitForPath(playerScripts, 'Modules') local clientReplicatedClasses = waitForPath(modules2, 'ClientReplicatedClasses') local remotes = waitForPath(replicatedStorage, 'Remotes') local replication = waitForPath(remotes, 'Replication') local useItemRemote = waitForPath(replication, { 'Fighter', 'UseItem' }) local quickAttackFunction = waitForPath(replication, { 'Fighter', 'RegisterQuickAttack' }) local updateStateRemote = waitForPath(replication, { 'Fighter', 'UpdateState' }) local pickWeaponsRemote = waitForPath(replication, { 'Fighter', 'PickWeapons' }) local updateCameraRotationRemote = waitForPath(replication, { 'Fighter', 'UpdateCameraRotation' }) local respawnNowRemote = waitForPath(remotes, { 'Duels', 'RespawnNow' }) local voteEvent = waitForPath(remotes, { 'Duels', 'Vote' }) local requestProfileFunction = waitForPath(remotes, { 'Misc', 'RequestProfile' }) local shootingRangeTrashItems = waitForPath(waitForPath(remotes, 'Misc'), 'ShootingRangeTrashItems') local replicatedController = waitForPath(modules2, 'ReplicatedController', true) local controlsController = waitForPath(controllers, 'ControlsController', true) local fighterController = waitForPath(controllers, 'FighterController', true) local mechanicsController = waitForPath(controllers, 'MechanicsController', true) local enemyController = waitForPath(controllers, 'EnemyController', true) local shootingRangeController = waitForPath(controllers, 'ShootingRangeController', true) local arcadeController = waitForPath(controllers, 'ArcadeController', constantPool[34]) local cameraController = waitForPath(controllers, 'CameraController', true) local duelController = waitForPath(controllers, 'DuelController', true) local complianceController = waitForPath(controllers, 'ComplianceController', true) local userInterface = waitForPath(modules2, 'UserInterface') local equipment = waitForPath(userInterface, 'Equipment', constantPool[34]) local lobby = waitForPath(userInterface, 'Lobby') local module = require(lobby) local pages = waitForPath(userInterface, 'Pages', true) local matchmaking = waitForPath(lobby, 'Matchmaking', true) local constants = waitForPath(modules, 'CONSTANTS', true) local enumLibrary = waitForPath(modules, 'EnumLibrary', true) local inputLibrary = waitForPath(modules, 'InputLibrary', true) local seasonLibrary = waitForPath(modules, 'SeasonLibrary', true) local cosmeticLibrary = waitForPath(modules, 'CosmeticLibrary', true) local itemLibrary = waitForPath(modules, 'ItemLibrary', true) local duelLibrary = waitForPath(modules, 'DuelLibrary', true) local utility = waitForPath(modules, 'Utility', true) local playerDataUtility = waitForPath(modules, 'PlayerDataUtility', true) local taskLibrary = waitForPath(modules, 'TaskLibrary', true) local replicatedClass = waitForPath(modules, 'ReplicatedClass', true) local outOfBoundsMachine = waitForPath(modules, 'OutOfBoundsMachine', true) local playerDataController = waitForPath(controllers, 'PlayerDataController', true) local matchmakingController = waitForPath(controllers, 'MatchmakingController', true) local leaderboardController = waitForPath(controllers, 'LeaderboardController', constantPool[34]) local clientEntity = waitForPath(clientReplicatedClasses, 'ClientEntity', true) local clientDueler = waitForPath(clientReplicatedClasses, 'ClientDueler', true) local clientFighterCharacter = waitForPath(clientReplicatedClasses, { 'ClientFighter', 'ClientFighterCharacter' }, true) local clientFighterCharacterJoints = waitForPath(clientReplicatedClasses, { 'ClientFighter', 'ClientFighterCharacter', 'Joints' }, true) local clientFighterCharacterVisibility = waitForPath(clientReplicatedClasses, { 'ClientFighter', 'ClientFighterCharacter', 'Visibility' }, true) local clientItem = waitForPath(clientReplicatedClasses, { 'ClientFighter', 'ClientItem' }, constantPool[34]) local clientViewModel = waitForPath(clientReplicatedClasses, { 'ClientFighter', 'ClientItem', 'ClientViewModel' }, true) local viewModelAnimator = waitForPath(clientReplicatedClasses, { 'ClientFighter', 'ClientItem', 'ClientViewModel', 'ViewModelAnimator' }, true) local itemInterfaceOther = waitForPath(clientReplicatedClasses, { 'ClientFighter', 'ClientItem', 'ItemInterface', 'Other' }, constantPool[34]) local scope = waitForPath(clientReplicatedClasses, { 'ClientFighter', 'ClientItem', 'ItemInterface', 'Mouse', 'Scope' }, true) local cosmeticSlot = waitForPath(modules2, 'CosmeticSlot', true) local crosshair = waitForPath(modules2, 'Crosshair', true) local cosmeticViewportFrame = waitForPath(modules2, 'CosmeticViewportFrame', true) local gun = waitForPath(modules2, { 'ItemTypes', 'Gun' }, true) local throwable = waitForPath(modules2, { 'ItemTypes', 'Throwable' }, constantPool[34]) local pages2 = waitForPath(modules2, 'Pages') local equipEmote = waitForPath(pages2, 'EquipEmote', true) local pickEmote = waitForPath(pages2, 'PickEmote', true) local viewProfile = waitForPath(pages2, 'ViewProfile', true) local emotes = waitForPath(replicatedStorage, { 'Modules', 'Emotes' }) local finishers = waitForPath(replicatedStorage, { 'Modules', 'Finishers' }) local serverOsTime = waitForPath(replicatedStorage, { 'Modules', 'ServerOsTime' }, constantPool[34]) return { Modules = modules, Controllers = controllers, UserInterface = userInterface, Equipment = equipment, Lobby = module, MatchmakingInterface = matchmaking, MatchmakingController = matchmakingController, LeaderboardController = leaderboardController, UseItemRemote = useItemRemote, QuickAttackFunction = quickAttackFunction, UpdateStateRemote = updateStateRemote, PickWeaponsRemote = pickWeaponsRemote, UpdateCameraRotationRemote = updateCameraRotationRemote, ShootingRangeTrashItemsEvent = shootingRangeTrashItems, RespawnNowRemote = respawnNowRemote, VoteEvent = voteEvent, PagesModule = pages, RequestProfileFunction = requestProfileFunction, Constants = constants, TaskLibrary = taskLibrary, ReplicatedController = replicatedController, ControlsController = controlsController, PlayerDataController = playerDataController, FighterController = fighterController, MechanicsController = mechanicsController, EnemyController = enemyController, ShootingRangeController = shootingRangeController, ArcadeController = arcadeController, CameraController = cameraController, DuelController = duelController, ComplianceController = complianceController, EnumLibrary = enumLibrary, InputLibrary = inputLibrary, SeasonLibrary = seasonLibrary, CosmeticLibrary = cosmeticLibrary, ItemLibrary = itemLibrary, DuelLibrary = duelLibrary, Utility = utility, PlayerDataUtility = playerDataUtility, ReplicatedClass = replicatedClass, OutOfBoundsMachine = outOfBoundsMachine, ClientEntity = clientEntity, ClientDueler = clientDueler, ClientFighterCharacter = clientFighterCharacter, ClientFighterCharacterJoints = clientFighterCharacterJoints, ClientFighterCharacterVisibility = clientFighterCharacterVisibility, Gun = gun, Throwable = throwable, ClientItem = clientItem, ClientViewModel = clientViewModel, ViewModelAnimator = viewModelAnimator, ItemInterfaceOther = itemInterfaceOther, Scope = scope, Crosshair = crosshair, CosmeticSlot = cosmeticSlot, CosmeticViewportFrame = cosmeticViewportFrame, Pages = pages2, EquipEmote = equipEmote, PickEmote = pickEmote, ViewProfile = viewProfile, Emotes = emotes, Finishers = finishers, PlayerModule = waitForPath(localPlayer, { 'PlayerScripts', 'PlayerModule' }, constantPool[34]), ServerOsTime = serverOsTime, } end modules.aS = function() local cached = modules.cache.aS if not cached then cached = { c = buildGameModules() } modules.cache.aS = cached end return cached.c end end do -- a6 concatArrays local function buildConcatArrays() return function(first, second) local joined = table.clone(first) table.move(second, 1, #second, #first + 1, joined) return joined end end modules.a6 = function() local cached = modules.cache.a6 if not cached then cached = { c = buildConcatArrays() } modules.cache.a6 = cached end return cached.c end end do -- bK PlayerRegistry: player added / removed signals local function buildPlayerRegistry() local GoodSignal = modules.g() local Trove = modules.k() local players = cloneref(game:GetService('Players')) local PlayerRegistry = {} PlayerRegistry.__index = PlayerRegistry PlayerRegistry.new = function() local registry = Trove.new('registry') local playerRegistry = { _trove = registry, _playerSet = {}, PlayerAdded = registry:Add(GoodSignal.new()), PlayerRemoved = registry:Add(GoodSignal.new()), } setmetatable(playerRegistry, PlayerRegistry) playerRegistry:_Initialize() return playerRegistry end PlayerRegistry._Initialize = function(self) self._trove:Connect(players.PlayerAdded, function(player) self._playerSet[player] = constantPool[34] self.PlayerAdded:Fire(player) end) self._trove:Connect(players.PlayerRemoving, function(player) if self._playerSet[player] == nil then return end self.PlayerRemoved:Fire(player) self._playerSet[player] = nil end) for _, player in players:GetPlayers() do self._playerSet[player] = true end end PlayerRegistry.ObservePlayers = function(self, onAdded, onRemoved) local ObservePlayers = Trove.new('registry.ObservePlayers') for k in self._playerSet, nil, nil do onAdded(k) end ObservePlayers:Connect(self.PlayerAdded, onAdded) ObservePlayers:Connect(self.PlayerRemoved, onRemoved) return ObservePlayers end PlayerRegistry.GetPlayers = function(self) local players = {} for k in self._playerSet, nil, nil do table.insert(players, k) end return players end PlayerRegistry.Destroy = function(self) self._trove:Destroy() end return PlayerRegistry end modules.bK = function() local cached = modules.cache.bK if not cached then cached = { c = buildPlayerRegistry() } modules.cache.bK = cached end return cached.c end end do -- bM PlayerTags: tags per player (Hacker, Moderator, ModFriend) local function buildPlayerTags() modules.bK() local GoodSignal = modules.g() local Trove = modules.k() local PlayerTags = {} PlayerTags.__index = PlayerTags local validTags = { Hacker = true, Moderator = true, ModFriend = constantPool[34] } PlayerTags.new = function(playerRegistry) local tags = Trove.new('tags') local playersByTag = {} for k in validTags, nil, nil do playersByTag[k] = {} end local playerTags = { _trove = tags, _tagSetByPlayer = {}, _playersByTag = playersByTag, TagAdded = tags:Add(GoodSignal.new()), TagRemoved = tags:Add(GoodSignal.new()), } setmetatable(playerTags, PlayerTags) playerTags:_Initialize(playerRegistry) return playerTags end PlayerTags._Initialize = function(self, playerRegistry) self._trove:Connect(playerRegistry.PlayerRemoved, function(player) local tagSet = self._tagSetByPlayer[player] if tagSet == nil then return end for k in tagSet, nil, nil do self:_RemoveFromTagBucket(player, k) self.TagRemoved:Fire(player, k) end self._tagSetByPlayer[player] = nil end) end PlayerTags.Add = function(self, player, tag) local tagSet = self._tagSetByPlayer[player] if tagSet == nil then tagSet = {} self._tagSetByPlayer[player] = tagSet end if tagSet[tag] then return false end table.insert(self._playersByTag[tag], player) tagSet[tag] = true self.TagAdded:Fire(player, tag) return true end PlayerTags.Remove = function(self, player, tag) local tagSet = self._tagSetByPlayer[player] if tagSet == nil or not tagSet[tag] then return false end self:_RemoveFromTagBucket(player, tag) tagSet[tag] = nil self.TagRemoved:Fire(player, tag) return true end PlayerTags.Has = function(self, player, tag) local tagSet = self._tagSetByPlayer[player] return tagSet ~= nil and tagSet[tag] ~= nil end PlayerTags.GetPlayersWith = function(self, tag) return self._playersByTag[tag] end PlayerTags.GetTags = function(self, player) local tagSet = self._tagSetByPlayer[player] if tagSet == nil then return {} end local tags = {} for k in tagSet, nil, nil do table.insert(tags, k) end return tags end PlayerTags._RemoveFromTagBucket = function(self, player, tag) local bucket = self._playersByTag[tag] for k, entry in bucket, nil, nil do if entry ~= player then continue end local n = #bucket bucket[k] = bucket[n] bucket[n] = nil return end end PlayerTags.Destroy = function(self) self._trove:Destroy() end return PlayerTags end modules.bM = function() local cached = modules.cache.bM if not cached then local created = { c = buildPlayerTags() } modules.cache.bM = created cached = created end return cached.c end end do -- b2 instancesEqual local function buildInstancesEqual() return function(first, second) return compareinstances ~= nil and compareinstances(first, second) or first == second end end modules.b2 = function() local cached = modules.cache.b2 if not cached then cached = { c = buildInstancesEqual() } modules.cache.b2 = cached end return cached.c end end do -- b3 callAtIdentityTwo: protected call at thread identity 2 local function buildCallAtIdentityTwo() return function(callback, ...) local previousIdentity = getthreadidentity() setthreadidentity(constantPool[56]) local pack = table.pack local call = protectedCall local packed = table.pack(...) packed.n = 2 + packed.n - 1 table.move(packed, 1, packed.n, 2, packed) packed[1] = callback local results = pack(call(table.unpack(packed, 1, packed.n))) setthreadidentity(previousIdentity) if not results[1] then error(results[constantPool[56]], 2) end return unpack(results, 2, results.n) end end modules.b3 = function() local cached = modules.cache.b3 if not cached then cached = { c = buildCallAtIdentityTwo() } modules.cache.b3 = cached end return cached.c end end do -- cp cameraRotation: read / write the game camera rotation local function buildCameraRotation() local cameraController = modules.aS().CameraController local setRotation = rawGet(rawGet(getmetatable(cameraController), '__index'), 'SetRotation') return { get = function() return rawGet(cameraController, 'Rotation') or Vector2.zero end, set = function(rotation) coroutine.wrap(setRotation)(cameraController, rotation) end, } end modules.cp = function() local cached = modules.cache.cp if not cached then local created = { c = buildCameraRotation() } modules.cache.cp = created cached = created end return cached.c end end do -- cr CharacterContext: per-player character state (alive, parts, velocity) local function buildCharacterContext() local GoodSignal = modules.g() local Trove = modules.k() local CharacterContext = {} CharacterContext.__index = CharacterContext CharacterContext.new = function(player) local trove = Trove.new('fighters.CharacterContext') local context = { _trove = trove, _player = player, State = { Alive = false }, Created = trove:Add(GoodSignal.new()), Died = trove:Add(GoodSignal.new()), } setmetatable(context, CharacterContext) context:_Initialize() return context end CharacterContext._Initialize = function(self) local characterTrove = self._trove:Extend() local function onCharacterGone() local state = self.State if state.Alive then characterTrove:Clean() self.State = { Alive = false } self.Died:Fire(state) end end local function onCharacterAdded(character) local humanoid = character:WaitForChild('Humanoid', math.huge) if humanoid.Health <= 0 then return end local humanoidRootPart = character:WaitForChild('HumanoidRootPart', math.huge) local hitboxHead = character:WaitForChild('HitboxHead', math.huge) local hitboxBody = character:WaitForChild('HitboxBody', math.huge) local hitboxHeadSmall = character:WaitForChild('HitboxHeadSmall', math.huge) local hitboxBodySmall = character:WaitForChild('HitboxBodySmall', math.huge) characterTrove:Connect(humanoid:GetPropertyChangedSignal('Health'), function() local health = humanoid.Health if self.State.Alive then if health > 0 then self.State.Health = health else onCharacterGone() end end end) characterTrove:Connect(humanoid:GetPropertyChangedSignal('MaxHealth'), function() local maxHealth = humanoid.MaxHealth if self.State.Alive then if maxHealth > 0 then self.State.MaxHealth = maxHealth else onCharacterGone() end end end) local state = { Alive = constantPool[34], Health = humanoid.Health, MaxHealth = humanoid.MaxHealth, Model = character, Humanoid = humanoid, RootPart = humanoidRootPart, HitboxHead = hitboxHead, HitboxBody = hitboxBody, TargetPartsByName = { HitboxHead = hitboxHead, HitboxBody = hitboxBody, HitboxHeadSmall = hitboxHeadSmall, HitboxBodySmall = hitboxBodySmall, }, _lastPosition = nil, Velocity = Vector3.zero, } self.State = state self.Created:Fire(state) end self._trove:Connect(self._player.CharacterAdded, onCharacterAdded) self._trove:Connect(self._player.CharacterRemoving, onCharacterGone) local character = self._player.Character if character ~= nil then task.spawn(onCharacterAdded, character) end end CharacterContext.Update = function(self, deltaTime) self:_UpdateVelocity(deltaTime) end CharacterContext._UpdateVelocity = function(self, deltaTime) local state = self.State if not state.Alive then return end local position, lastPosition = state.RootPart.Position, state._lastPosition if lastPosition == nil then state._lastPosition = position return end state.Velocity = (position - lastPosition) / deltaTime state._lastPosition = position end CharacterContext.Destroy = function(self) self._trove:Destroy() end return CharacterContext end modules.cr = function() local cached = modules.cache.cr if not cached then cached = { c = buildCharacterContext() } modules.cache.cr = cached end return cached.c end end do -- cs ServerCFrameSync: root part CFrame override around the server step local function buildServerCFrameSync() local runService = cloneref(game:GetService('RunService')) local httpService = cloneref(game:GetService(constantPool[182])) local ServerCFrameSync = {} ServerCFrameSync.__index = ServerCFrameSync ServerCFrameSync.new = function(rootPart) local sync = { _rootPart = rootPart, _boundId = httpService:GenerateGUID(constantPool[153]) } setmetatable(sync, ServerCFrameSync) sync:_Initialize() return sync end ServerCFrameSync._Initialize = function(self) runService:BindToRenderStep(self._boundId, Enum.RenderPriority.First.Value, function() self:_RenderStepUpdate() end) end ServerCFrameSync.SetServerCFrame = function(self, cframe) self._cframe = cframe local oldCFrame = self._oldCFrame if oldCFrame ~= nil then self._rootPart.CFrame = cframe or oldCFrame end end ServerCFrameSync.GetServerCFrame = function(self) return self._cframe or self._rootPart.CFrame end ServerCFrameSync.GetClientCFrame = function(self) return self._oldCFrame or self._rootPart.CFrame end ServerCFrameSync._RenderStepUpdate = function(self) local oldCFrame = self._oldCFrame if oldCFrame ~= nil then self._rootPart.CFrame = oldCFrame self._oldCFrame = nil end end ServerCFrameSync.HeartbeatUpdate = function(self) local cframe = self._cframe if cframe == nil then return end local rootPart = self._rootPart if self._oldCFrame == nil then self._oldCFrame = rootPart.CFrame end rootPart.CFrame = cframe end ServerCFrameSync.Destroy = function(self) self:_RenderStepUpdate() runService:UnbindFromRenderStep(self._boundId) end return ServerCFrameSync end modules.cs = function() local cached = modules.cache.cs if not cached then cached = { c = buildServerCFrameSync() } modules.cache.cs = cached end return cached.c end end do -- ct signalConnect: raw Connect function local function buildSignalConnect() return modules.g().new().Connect end modules.ct = function() local cached = modules.cache.ct if not cached then local created = { c = buildSignalConnect() } modules.cache.ct = created cached = created end return cached.c end end do -- cu connectAtIdentityEight: connect with the callback at identity 8 local function buildConnectAtIdentityEight() modules.g() local signalConnect = modules.ct() return function(signal, callback) return signalConnect(signal, function(...) local previousIdentity = getthreadidentity() setthreadidentity(8) callback(...) setthreadidentity(previousIdentity) end) end end modules.cu = function() local cached = modules.cache.cu if not cached then cached = { c = buildConnectAtIdentityEight() } modules.cache.cu = cached end return cached.c end end do -- cv readDataField: read a field of a fighter Data table local function buildReadDataField() modules.aM() return function(fighter, key) return rawGet(rawGet(fighter, 'Data'), key) end end modules.cv = function() local cached = modules.cache.cv if not cached then cached = { c = buildReadDataField() } modules.cache.cv = cached end return cached.c end end do -- cw LocalFighterState: local fighter (items, spectating, attributes) local function buildLocalFighterState() local CharacterContext = modules.cr() modules.aM() modules.aO() local GoodSignal = modules.g() local Trove = modules.k() local connectAtIdentityEight = modules.cu() local signalConnect = modules.ct() local readDataField = modules.cv() local callAtIdentityTwo = modules.b3() local LocalFighterState = {} LocalFighterState.__index = LocalFighterState LocalFighterState.new = function(player, fighter) local trove = Trove.new('fighters.LocalFighterState') local fighterClass = rawGet(getmetatable(fighter), '__index') local data = rawGet(fighter, 'Data') local obj = setmetatable({ _trove = trove, _data = data, _inputFn = rawGet(fighterClass, 'Input'), _quickAttackFn = rawGet(fighterClass, 'QuickAttack'), Inner = fighter, Player = player, Character = trove:Add(CharacterContext.new(player)), ObjectId = rawGet(data, 'ObjectID'), EnvironmentId = player:GetAttribute('EnvironmentID'), IsSpectating = readDataField(fighter, 'IsSpectating') == true, EnvironmentIdChanged = trove:Add(GoodSignal.new()), IsSpectatingChanged = trove:Add(GoodSignal.new()), EntityAdded = trove:Add(GoodSignal.new()), EntityRemoved = trove:Add(GoodSignal.new()), }, LocalFighterState) obj._trove:Connect(player:GetAttributeChangedSignal('EnvironmentID'), function() local attribute = player:GetAttribute('EnvironmentID') obj.EnvironmentId = attribute obj.EnvironmentIdChanged:Fire(attribute) end) obj._trove:Add(signalConnect(callAtIdentityTwo(fighter.GetDataChangedSignal, fighter, 'IsSpectating'), function() local isSpectating = readDataField(fighter, 'IsSpectating') == true if isSpectating == obj.IsSpectating then return end obj.IsSpectating = isSpectating obj.IsSpectatingChanged:Fire(isSpectating) end)) local inner = obj.Inner obj._trove:Add(signalConnect(rawGet(inner, 'EntityAdded'), function(entity) obj.EntityAdded:Fire(entity) end)) obj._trove:Add(signalConnect(rawGet(inner, 'EntityRemoved'), function(entity) obj.EntityRemoved:Fire(entity) end)) local entity = rawGet(inner, 'Entity') if entity ~= nil then obj.EntityAdded:Fire(entity) end return obj end LocalFighterState.GetEntity = function(self) return rawGet(self.Inner, 'Entity') end LocalFighterState.GetItems = function(self) return rawGet(self.Inner, 'Items') end LocalFighterState.GetEquippedItem = function(self) return rawGet(self.Inner, 'EquippedItem') end LocalFighterState.IsInvincible = function(self) local entity = rawGet(self.Inner, 'Entity') local data = entity and rawGet(entity, 'Data') return data ~= nil and rawGet(data, 'IsInvincible') == true end LocalFighterState.IsCrouching = function(self) local data = rawGet(self.Inner, 'Data') return data ~= nil and rawGet(data, 'IsCrouching') == true end LocalFighterState.FindItemByObjectId = function(self, objectId) local nextFn = next local items, firstKey = self:GetItems() for _, item in nextFn, items, firstKey do if objectId == rawGet(rawGet(item, 'Data'), 'ObjectID') then return item end end return nil end LocalFighterState.OnItemAddedSignal = function(self, callback) return connectAtIdentityEight(rawGet(self.Inner, 'ItemAdded'), callback) end LocalFighterState.OnItemRemovedSignal = function(self, callback) return connectAtIdentityEight(rawGet(self.Inner, 'ItemRemoved'), callback) end LocalFighterState.OnEquippedItemChangedSignal = function(self, callback) return connectAtIdentityEight(rawGet(self.Inner, 'EquippedItemChanged'), function() callback(self:GetEquippedItem()) end) end LocalFighterState.Input = function(self, input) callAtIdentityTwo(self._inputFn, self.Inner, input) end LocalFighterState.QuickAttack = function(self, payload) if not self.Character.State.Alive then return end task.spawn(callAtIdentityTwo, self._quickAttackFn, self.Inner, payload) end LocalFighterState.Update = function(self, deltaTime) self.Character:Update(deltaTime) end LocalFighterState.Destroy = function(self) self._trove:Destroy() end return LocalFighterState end modules.cw = function() local cached = modules.cache.cw if not cached then local created = { c = buildLocalFighterState() } modules.cache.cw = created cached = created end return cached.c end end do -- cx getRankName: rank name from ELO local function buildGetRankName() local seasonLibrary = modules.aS().SeasonLibrary return function(userId, elo) if elo == nil then return 'Unranked' end local name = rawGet(rawGet(rawGet(seasonLibrary, 'CurrentSeason'), 'RankProfile'), 'Name') local rankProfile = rawGet(rawGet(seasonLibrary, 'RankProfiles'), name) if rankProfile == nil then return 'Unranked' end local ranksOrder = rawGet(rankProfile, 'RanksOrder') local ranks = rawGet(rankProfile, 'Ranks') local rankCount = rawlen(ranksOrder) for k, rankName in next, ranksOrder, nil do local rank = rawGet(ranks, rankName) if rank == nil then continue end if elo < rawGet(rank, 'RequiredELO') and rawGet(rank, 'RequiredELOLeaderboardRanking') == nil then local previousRank = rawGet(ranksOrder, math.clamp(k - 1, 1, rankCount)) if previousRank ~= nil then return previousRank end end end for i = rankCount, 1, -1 do local rankName = rawGet(ranksOrder, i) if rankName ~= nil then local rank = rawGet(ranks, rankName) if rank ~= nil and rawGet(rank, 'RequiredELOLeaderboardRanking') == nil then return rankName end end end return 'Unranked' end end modules.cx = function() local cached = modules.cache.cx if not cached then cached = { c = buildGetRankName() } modules.cache.cx = cached end return cached.c end end do -- cy RankTracker: rank of a player from DisplayELO local function buildRankTracker() local Trove = modules.k() local getRankName = modules.cx() local RankTracker = {} RankTracker.__index = RankTracker RankTracker.new = function(player) local tracker = { _trove = Trove.new('fighters.RankTracker'), Rank = 'Unranked' } setmetatable(tracker, RankTracker) tracker:_Initialize(player) return tracker end RankTracker._Initialize = function(self, player) self._trove:Connect(player:GetAttributeChangedSignal('DisplayELO'), function() local tracker = self local userId = player.UserId local attribute = player:GetAttribute('DisplayELO') tracker.Rank = getRankName(userId, attribute) end) local userId = player.UserId local attribute = player:GetAttribute('DisplayELO') self.Rank = getRankName(userId, attribute) end RankTracker.Destroy = function(self) self._trove:Destroy() end return RankTracker end modules.cy = function() local cached = modules.cache.cy if not cached then cached = { c = buildRankTracker() } modules.cache.cy = cached end return cached.c end end do -- cz CustomView: read-only view of a custom item local function buildCustomView() modules.aO() local CustomView = {} CustomView.__index = CustomView CustomView.Type = 'CustomView' CustomView.new = function(item) local info2 = rawGet(item, 'Info') local view = { _data = rawGet(item, 'Data'), Info = info2, Inner = item, Name = rawGet(item, 'Name') } setmetatable(view, CustomView) return view end CustomView.IsEquipped = function(self) return rawGet(self.Inner, 'IsEquipped') end return CustomView end modules.cz = function() local cached = modules.cache.cz if not cached then cached = { c = buildCustomView() } modules.cache.cz = cached end return cached.c end end do -- cA GunView: read-only view of a gun local function buildGunView() modules.aO() local GunView = {} GunView.__index = GunView GunView.Type = 'GunView' GunView.new = function(item) local info2 = rawGet(item, 'Info') return setmetatable({ _data = rawGet(item, 'Data'), Inner = item, Name = rawGet(item, 'Name'), IsRaycast = rawGet(info2, 'IsRaycast'), MaxAmmo = rawGet(info2, 'MaxAmmo'), }, GunView) end GunView.GetAmmo = function(self) return rawGet(self._data, 'Ammo') end GunView.GetReloadTimeRemaining = function(self) local now2 = tick() local n = constantPool[186] local reloadCooldown = rawGet(self.Inner, '_reload_cooldown') if reloadCooldown ~= nil then n = math.max(reloadCooldown - now2, 0) end local noAmmoCooldown = rawGet(self.Inner, '_shoot_cooldown_no_ammo') local remaining if noAmmoCooldown == nil then remaining = n else remaining = math.max(n, noAmmoCooldown - now2) end return remaining end return GunView end modules.cA = function() local cached = modules.cache.cA if not cached then local created = { c = buildGunView() } modules.cache.cA = created cached = created end return cached.c end end do -- cB MeleeView: read-only view of a melee weapon local function buildMeleeView() modules.aO() local MeleeView = {} MeleeView.__index = MeleeView MeleeView.Type = 'MeleeView' MeleeView.new = function(item) return setmetatable( { _info = rawGet(item, 'Info'), _data = rawGet(item, 'Data'), Inner = item, Name = rawGet(item, 'Name') }, MeleeView ) end MeleeView.IsDeflecting = function(self) local deflectCooldownEnd = rawGet(self.Inner, '_deflect_cooldown') if deflectCooldownEnd == nil then return false end local deflectCooldown = rawGet(self._info, 'DeflectCooldown') return deflectCooldown ~= nil and tick() < deflectCooldownEnd - deflectCooldown end MeleeView.IsAttacking = function(self) local attackCooldownEnd = rawGet(self.Inner, '_attack_cooldown') if attackCooldownEnd == nil then return constantPool[153] end return tick() < attackCooldownEnd end return MeleeView end modules.cB = function() local cached = modules.cache.cB if not cached then local created = { c = buildMeleeView() } modules.cache.cB = created cached = created end return cached.c end end do -- cC ThrowableView: read-only view of a throwable local function buildThrowableView() modules.aO() local ThrowableView = {} ThrowableView.__index = ThrowableView ThrowableView.Type = 'ThrowableView' ThrowableView.new = function(item) return setmetatable({ _data = rawGet(item, 'Data'), Inner = item, Name = rawGet(item, 'Name') }, ThrowableView) end return ThrowableView end modules.cC = function() local cached = modules.cache.cC if not cached then cached = { c = buildThrowableView() } modules.cache.cC = cached end return cached.c end end do -- cD RemoteItemObserver: items of remote fighters local function buildRemoteItemObserver() local CustomView = modules.cz() modules.aM() local GunView = modules.cA() modules.aO() local MeleeView = modules.cB() local GoodSignal = modules.g() local ThrowableView = modules.cC() local Trove = modules.k() local signalConnect = modules.ct() local RemoteItemObserver = {} RemoteItemObserver.__index = RemoteItemObserver RemoteItemObserver.new = function(inner) local remoteItemObserver = Trove.new('fighters.remote_item_observer') local observer = { _trove = remoteItemObserver, _inner = inner, _items = {}, ItemChanged = remoteItemObserver:Add(GoodSignal.new()) } setmetatable(observer, RemoteItemObserver) observer:_BindToGameEvents() return observer end RemoteItemObserver._BindToGameEvents = function(self) local itemAdded = rawGet(self._inner, 'ItemAdded') local itemRemoved = rawGet(self._inner, 'ItemRemoved') self._trove:Add(signalConnect(itemAdded, function(item) self:_AddItem(item) end)) self._trove:Add(signalConnect(itemRemoved, function(item) self:_RemoveItem(item) end)) self._trove:Add(signalConnect(rawGet(self._inner, 'EquippedItemChanged'), function() local equippedItem = rawGet(self._inner, 'EquippedItem') self.ItemChanged:Fire(equippedItem) if equippedItem == nil then self._equippedItem = nil return end self._equippedItem = self:_GetViewByInner(equippedItem) end)) for _, item in rawGet(self._inner, 'Items') do self:_AddItem(item) end local equippedItem = rawGet(self._inner, 'EquippedItem') if equippedItem ~= nil then self._equippedItem = self:_GetViewByInner(equippedItem) end end RemoteItemObserver._AddItem = function(self, item) if item.Info.Type == 'Gun' then table.insert(self._items, GunView.new(item)) elseif item.Info.Type == 'Melee' then table.insert(self._items, MeleeView.new(item)) elseif item.Info.Type == 'Custom' then table.insert(self._items, CustomView.new(item)) elseif item.Info.Type == 'Throwable' then table.insert(self._items, ThrowableView.new(item)) end end RemoteItemObserver._GetViewByInner = function(self, inner) for _, view in self._items, nil, nil do if view.Inner == inner then return view end end return nil end RemoteItemObserver._RemoveItem = function(self, inner) for k, view in self._items, nil, nil do if view.Inner == inner then table.remove(self._items, k) break end end end RemoteItemObserver.GetItems = function(self) return self._items end RemoteItemObserver.GetEquippedItem = function(self) return self._equippedItem end RemoteItemObserver.EquippedItemAsGun = function(self) local equippedItem = self._equippedItem if equippedItem ~= nil and equippedItem.Type == 'GunView' then return equippedItem end return nil end RemoteItemObserver.EquippedItemAsMelee = function(self) local equippedItem = self._equippedItem if equippedItem ~= nil and equippedItem.Type == 'MeleeView' then return equippedItem end return nil end RemoteItemObserver.EquippedItemAsCustom = function(self) local equippedItem = self._equippedItem if equippedItem ~= nil and equippedItem.Type == 'CustomView' then return equippedItem end return nil end RemoteItemObserver.GetEquippedAmmoState = function(self) local equippedItem = self._equippedItem if equippedItem == nil then return nil end if equippedItem.Type == 'GunView' then return equippedItem:GetAmmo() > 0 end return nil end RemoteItemObserver.FindByObjectId = function(self, objectId) for _, view in next, self._items, nil do if objectId == rawGet(view._data, 'ObjectID') then return view end end return nil end RemoteItemObserver.Destroy = function(self) self._trove:Destroy() if not integrityFlagA then return end end return RemoteItemObserver end modules.cD = function() local cached = modules.cache.cD if not cached then local created = { c = buildRemoteItemObserver() } modules.cache.cD = created cached = created end return cached.c end end do -- cE RemoteFighterState: remote fighter (enemy flag, rank, items) local function buildRemoteFighterState() local CharacterContext = modules.cr() modules.aM() modules.aO() modules.cw() local RankTracker = modules.cy() local RemoteItemObserver = modules.cD() local GoodSignal = modules.g() local Trove = modules.k() modules.cv() local RemoteFighterState = {} RemoteFighterState.__index = RemoteFighterState RemoteFighterState.new = function(player, fighter) local trove = Trove.new('fighters.RemoteFighterState') local state = { _trove = trove, _localBindingTrove = trove:Extend(), _data = rawGet(fighter, 'Data'), Inner = fighter, Player = player, Character = trove:Add(CharacterContext.new(player)), ItemObserver = trove:Add(RemoteItemObserver.new(fighter)), _rankTracker = trove:Add(RankTracker.new(player)), EnvironmentId = player:GetAttribute('EnvironmentID'), EnvironmentIdChanged = trove:Add(GoodSignal.new()), IsEnemy = false, EnemyChanged = trove:Add(GoodSignal.new()), } state._trove:Connect(player:GetAttributeChangedSignal('EnvironmentID'), function() local attribute = player:GetAttribute('EnvironmentID') state.EnvironmentId = attribute state.EnvironmentIdChanged:Fire(attribute) end) return setmetatable(state, RemoteFighterState) end RemoteFighterState.SetLocalFighter = function(self, localFighter) self._localBindingTrove:Clean() local localBindingTrove = self._localBindingTrove local function refreshEnemyState() local environmentId = self.EnvironmentId local isEnemy if environmentId == nil or environmentId ~= localFighter.EnvironmentId then isEnemy = false else local attribute = self.Player:GetAttribute('TeamID') local attribute2 = localFighter.Player:GetAttribute('TeamID') isEnemy = attribute2 == nil or attribute ~= attribute2 end if isEnemy == self.IsEnemy then return end self.IsEnemy = isEnemy self.EnemyChanged:Fire(isEnemy) end refreshEnemyState() localBindingTrove:Connect(localFighter.EnvironmentIdChanged, refreshEnemyState) localBindingTrove:Connect(localFighter.Player:GetAttributeChangedSignal('TeamID'), refreshEnemyState) localBindingTrove:Connect(self.EnvironmentIdChanged, refreshEnemyState) localBindingTrove:Connect(self.Player:GetAttributeChangedSignal('TeamID'), refreshEnemyState) end RemoteFighterState.GetRank = function(self) return self._rankTracker.Rank end RemoteFighterState.IsInvincible = function(self) local entity = rawGet(self.Inner, 'Entity') local data = entity and rawGet(entity, 'Data') return data ~= nil and rawGet(data, 'IsInvincible') == true end RemoteFighterState.GetCameraRotation = function(self) local value = self._data and rawGet(self._data, 'CameraRotation') return typeof(value) == 'Vector2' and value or Vector2.zero end RemoteFighterState.IsCrouching = function(self) return self._data ~= nil and rawGet(self._data, 'IsCrouching') == true end RemoteFighterState.Update = function(self, deltaTime) self.Character:Update(deltaTime) end RemoteFighterState.Destroy = function(self) self._trove:Destroy() end return RemoteFighterState end modules.cE = function() local cached = modules.cache.cE if not cached then local created = { c = buildRemoteFighterState() } modules.cache.cE = created cached = created end return cached.c end end do -- cF FighterRegistry: local and remote fighter states, observers local function buildFighterRegistry() modules.aM() modules.aO() local Trove = modules.k() local fighterController = modules.aS().FighterController local LocalFighterState = modules.cw() local RemoteFighterState = modules.cE() local GoodSignal = modules.g() local signalConnect = modules.ct() local localPlayer = cloneref(game:GetService('Players')).LocalPlayer local FighterRegistry = {} FighterRegistry.__index = FighterRegistry FighterRegistry.new = function() local fighters = Trove.new('fighters') local registry = { _trove = fighters, LocalState = nil, StateByPlayer = {}, StateByModel = {}, EnemyByPlayer = {}, LocalStateSet = fighters:Add(GoodSignal.new()), LocalStateRemoved = fighters:Add(GoodSignal.new()), RemoteStateAdded = fighters:Add(GoodSignal.new()), RemoteStateRemoved = fighters:Add(GoodSignal.new()), RemoteEntityAdded = fighters:Add(GoodSignal.new()), RemoteEntityRemoved = fighters:Add(GoodSignal.new()), } setmetatable(registry, FighterRegistry) registry:_Initialize() return registry end FighterRegistry._Initialize = function(self) local objectAdded = rawGet(fighterController, 'ObjectAdded') local objectRemoved = rawGet(fighterController, 'ObjectRemoved') local playerToFighter = rawGet(fighterController, '_player_to_fighter') local getIdentity = getthreadidentity local setIdentity = setthreadidentity local function forgetModel(state) local state = state.Character.State if state.Alive then self.StateByModel[state.Model] = nil end end local function onFighterAdded(fighter) local previousIdentity = getIdentity() setIdentity(8) local player = rawGet(fighter, 'Player') if player == localPlayer then if self.LocalState ~= nil then self.LocalStateRemoved:Fire(self.LocalState) self._trove:Remove(self.LocalState) self.LocalState = nil end self:_BuildLocalFighter(player, fighter) setIdentity(previousIdentity) return end local existing = self.StateByPlayer[player] if existing then self.RemoteStateRemoved:Fire(existing) forgetModel(existing) self._trove:Remove(existing) end self:_BuildRemoteFighter(player, fighter) setIdentity(previousIdentity) end local function onFighterRemoved(fighter) if true then local previousIdentity = getIdentity() setIdentity(8) local localState = self.LocalState if localState and localState.Inner == fighter then self._trove:Remove(localState) self.LocalState = nil self.LocalStateRemoved:Fire(localState) setIdentity(previousIdentity) return end local player = rawGet(fighter, 'Player') local existing = self.StateByPlayer[player] if existing and existing.Inner == fighter then forgetModel(existing) self._trove:Remove(existing) self.StateByPlayer[player] = nil self.EnemyByPlayer[player] = nil self.RemoteStateRemoved:Fire(existing) setIdentity(previousIdentity) return end setIdentity(previousIdentity) else -- (anti-tamper freeze trap removed) end end self._trove:Add(signalConnect(objectAdded, onFighterAdded)) self._trove:Add(signalConnect(objectRemoved, onFighterRemoved)) for _, fighter in next, playerToFighter, nil do onFighterAdded(fighter) end end FighterRegistry._BuildLocalFighter = function(self, player, fighter) local localState = self._trove:Add(LocalFighterState.new(player, fighter)) self.LocalState = localState for _, remoteState in self.StateByPlayer, nil, nil do remoteState:SetLocalFighter(localState) end self.LocalStateSet:Fire(localState) end FighterRegistry._BuildRemoteFighter = function(self, player, fighter) local remoteState = self._trove:Add(RemoteFighterState.new(player, fighter)) remoteState._trove:Connect(remoteState.Character.Created, function(characterState) self.StateByModel[characterState.Model] = remoteState end) remoteState._trove:Connect(remoteState.Character.Died, function(characterState) self.StateByModel[characterState.Model] = nil end) local state = remoteState.Character.State if state.Alive then self.StateByModel[state.Model] = remoteState end local entityAdded = rawGet(fighter, 'EntityAdded') local entityRemoved = rawGet(fighter, 'EntityRemoved') remoteState._trove:Add(signalConnect(entityAdded, function(entity) self.RemoteEntityAdded:Fire(remoteState, entity) end)) remoteState._trove:Add(signalConnect(entityRemoved, function(entity) self.RemoteEntityRemoved:Fire(remoteState, entity) end)) remoteState._trove:Connect(remoteState.EnemyChanged, function(isEnemy) self.EnemyByPlayer[player] = isEnemy and remoteState or nil end) self.StateByPlayer[player] = remoteState self.EnemyByPlayer[player] = remoteState.IsEnemy and remoteState or nil local localState = self.LocalState if localState ~= nil then remoteState:SetLocalFighter(localState) end self.RemoteStateAdded:Fire(remoteState) local entity = rawGet(fighter, 'Entity') if entity ~= nil then self.RemoteEntityAdded:Fire(remoteState, entity) end end FighterRegistry.ObserveRemoteStates = function(self, onAdded, onRemoved) local observer = Trove.new('fighters.ObserveRemoteStates') for _, state in self.StateByPlayer, nil, nil do onAdded(state) end observer:Connect(self.RemoteStateAdded, onAdded) observer:Connect(self.RemoteStateRemoved, onRemoved) return observer end FighterRegistry.ObserveAllStates = function(self, onLocalSet, onLocalRemoved, onRemoteAdded, onRemoteRemoved) local observer = Trove.new('fighters.ObserveAllStates') local localState = self.LocalState if localState then onLocalSet(localState) end observer:Connect(self.LocalStateSet, onLocalSet) observer:Connect(self.LocalStateRemoved, onLocalRemoved) for _, state in self.StateByPlayer, nil, nil do onRemoteAdded(state) end observer:Connect(self.RemoteStateAdded, onRemoteAdded) observer:Connect(self.RemoteStateRemoved, onRemoteRemoved) return observer end FighterRegistry.ObserveRemoteEntities = function(self, onAdded, onRemoved) local observer = Trove.new('fighters.ObserveRemoteEntities') for _, state in self.StateByPlayer, nil, nil do local entity = rawGet(state.Inner, 'Entity') if entity ~= nil then onAdded(state, entity) end end observer:Connect(self.RemoteEntityAdded, onAdded) observer:Connect(self.RemoteEntityRemoved, onRemoved) return observer end FighterRegistry.Update = function(self, deltaTime) local localState = self.LocalState if localState then localState:Update(deltaTime) end for player, state in self.StateByPlayer, nil, nil do state:Update(deltaTime) end end FighterRegistry.Destroy = function(self) self._trove:Destroy() end return FighterRegistry end modules.cF = function() local cached = modules.cache.cF if not cached then local created = { c = buildFighterRegistry() } modules.cache.cF = created cached = created end return cached.c end end do -- cG ServerOrigin: server-side origin offsets and delay estimates local function buildServerOrigin() modules.cr() modules.cF() local localPlayer = cloneref(game:GetService('Players')).LocalPlayer local ServerOrigin ServerOrigin = { getServerOriginOffset = function(isCrouching) return Vector3.new(0, isCrouching and 0.25 or 1.75, constantPool[186]) end, getServerOrigin = function(characterState, fighterState) return characterState.RootPart.Position + ServerOrigin.getServerOriginOffset(fighterState:IsCrouching()) end, getOneWayPing = function() return 0.5 * localPlayer:GetNetworkPing() end, getEstimatedReplicationDelay = function() return ServerOrigin.getOneWayPing() + 0.05 end, getEstimatedRemoteDelay = function() return ServerOrigin.getOneWayPing() + 0.016666666666666666 end, } return ServerOrigin end modules.cG = function() local cached = modules.cache.cG if not cached then cached = { c = buildServerOrigin() } modules.cache.cG = cached end return cached.c end end do -- cH RotationCodec: camera rotation encode / decode local function buildRotationCodec() local RotationCodec RotationCodec = { encodeCameraRotation = function(rotation) if rotation ~= rotation then return utf8.char(0) .. utf8.char(0) end local xByte = math.clamp(math.floor(rotation.X % math.tau / 3.1415926535897931 / 2 * 256 + 0.5), 0, 255) local yByte = math.clamp(math.floor(rotation.Y % math.tau / 3.1415926535897931 / 2 * 256 + 0.5), 0, constantPool[108]) return utf8.char(xByte) .. utf8.char(yByte) end, encodeSingle = function(angle) if angle ~= angle then return utf8.char(0) end local minByte = constantPool[186] local maxByte = constantPool[108] return utf8.char(math.clamp(math.floor(angle % math.tau / 3.1415926535897931 / 2 * constantPool[137] + 0.5), minByte, maxByte)) end, decodeSingle = function(encoded) local factor = constantPool[56] return utf8.codepoint(encoded) * 3.1415926535897931 * factor / 256 end, fromXYToCameraRotation = function(x, y) return Vector2.new(x, y) * 3.1415926535897931 * 2 / 256 end, decodeCameraRotation = function(encoded) local x, y = utf8.codepoint(encoded, 1, #encoded) return RotationCodec.fromXYToCameraRotation(x, y) end, } return RotationCodec end modules.cH = function() local cached = modules.cache.cH if not cached then local created = { c = buildRotationCodec() } modules.cache.cH = created cached = created end return cached.c end end do -- cI ViewAngleDriver: camera rotation payloads for the server local function buildViewAngleDriver() modules.aM() local Result = modules.a() local Trove = modules.k() local fighterController = modules.aS().FighterController local clientFighterCharacterJoints = modules.aS().ClientFighterCharacterJoints local updateCameraRotationRemote = modules.aS().UpdateCameraRotationRemote local get = modules.cp().get local RotationCodec = modules.cH() local localPlayer = cloneref(game:GetService('Players')).LocalPlayer local ViewAngleDriver = {} ViewAngleDriver.__index = ViewAngleDriver local function encodeViewAngle(angle) if angle.Kind == 'Unnormalized' then return utf8.char(math.clamp(angle.Pitch, constantPool[186], 255)) .. utf8.char(math.clamp(angle.Yaw, 0, 255)) end return RotationCodec.encodeSingle(math.rad(angle.Pitch)) .. RotationCodec.encodeSingle(math.rad(angle.Yaw)) end local function decodeViewAngle(angle) if angle.Kind == 'Unnormalized' then return RotationCodec.fromXYToCameraRotation(angle.Pitch, angle.Yaw) end local pitch = RotationCodec.decodeSingle(RotationCodec.encodeSingle(math.rad(angle.Pitch))) local yaw = RotationCodec.decodeSingle(RotationCodec.encodeSingle(math.rad(angle.Yaw))) return Vector2.new(pitch, yaw) end ViewAngleDriver.new = function() return setmetatable({ _restoreTrove = Trove.new('view_angle_driver.restore'), _restoreTask = nil, _payloadBySlot = {}, _winning = nil, _fullySuppressed = constantPool[153], _dirty = false, }, ViewAngleDriver) end ViewAngleDriver._LoadJointsHook = function(self) if self._jointsRestore ~= nil then return Result.VoidOk end local update = rawGet(clientFighterCharacterJoints, 'Update') if update == nil then return Result.err('ViewAngleDriver', 'function_lookup', 'Joints.Update method not found') end rawSet(clientFighterCharacterJoints, 'Update', function(joints, jointsInput, context) local clientFighterCharacter = rawGet(joints, 'ClientFighterCharacter') local clientFighter = clientFighterCharacter and rawGet(clientFighterCharacter, 'ClientFighter') if clientFighter == nil then return update(joints, jointsInput, context) end if rawGet(clientFighter, 'IsLocalPlayer') == true then local winning = self._winning if winning ~= nil then rawSet(context, 'CameraRotationRaw', decodeViewAngle(winning)) end end return update(joints, jointsInput, context) end) self._jointsRestore = { JointsUpdateMethod = update } return Result.VoidOk end ViewAngleDriver._LoadReplicationHook = function(self) if self._replicationRestore ~= nil then return Result.VoidOk end local metatable = getmetatable(fighterController) if metatable == nil then return Result.err('ViewAngleDriver', 'get_metatable', 'FighterController metatable not found') end local prototype = rawGet(metatable, '__index') if type(prototype) ~= 'table' then return Result.err('ViewAngleDriver', 'get_prototype', 'FighterController prototype not found') end local replicationLoop = rawGet(prototype, '_CameraReplicationLoop') if type(replicationLoop) ~= 'function' then return Result.err('ViewAngleDriver', 'function_lookup', 'FighterController._CameraReplicationLoop method not found') end local utilityIndex = nil local utility = nil for k, value in debug.getupvalues(replicationLoop) do if type(value) ~= 'table' then utilityIndex = nil utility = nil else local valueMeta = getmetatable(value) if valueMeta == nil then utilityIndex = nil utility = nil else local valueIndex = rawGet(valueMeta, '__index') if type(valueIndex) ~= 'table' then utilityIndex = nil utility = nil elseif type(rawGet(valueIndex, 'EncodeCameraRotation')) == 'function' then utilityIndex = k utility = value break else utilityIndex = nil utility = nil end end end end if utilityIndex == nil or utility == nil then return Result.err('ViewAngleDriver', 'upvalue_search', 'Utility upvalue index not found') end debug.setupvalue(replicationLoop, utilityIndex, { EncodeCameraRotation = function(_utility, rotation) if next(self._payloadBySlot) ~= nil or self._fullySuppressed then rawSet(fighterController, '_replication_stopped', false) return rawGet(fighterController, '_last_encoded_camera_rotation') or RotationCodec.encodeCameraRotation(rotation) end return RotationCodec.encodeCameraRotation(rotation) end, }) self._replicationRestore = { CameraReplicationLoopMethod = replicationLoop, UtilityIndex = utilityIndex, Utility = utility } return Result.VoidOk end ViewAngleDriver._Resolve = function(self) local n = -math.huge local winningPayload = nil for k, payload in self._payloadBySlot, nil, nil do if k > n then n = k winningPayload = payload end end self._winning = winningPayload end ViewAngleDriver._CancelRestore = function(self) local restoreTask = self._restoreTask if restoreTask ~= nil then self._restoreTask = nil self._restoreTrove:Remove(restoreTask) end end ViewAngleDriver.SendViewAngles = function(self, slot, payload) if self._payloadBySlot[slot] == payload then return Result.VoidOk end self._payloadBySlot[slot] = payload self._dirty = true self:_Resolve() self:_CancelRestore() if self._winning == nil then return Result.VoidOk end local result = self:_LoadJointsHook() if not result.Ok then return result end return self:_LoadReplicationHook() end ViewAngleDriver.SendRawCameraRotation = function(self, encoded) fireServer2(updateCameraRotationRemote, encoded, nil) end ViewAngleDriver.Suppress = function(self, fullySuppressed) if self._fullySuppressed == fullySuppressed then return Result.VoidOk end self._fullySuppressed = fullySuppressed if fullySuppressed then self:_CancelRestore() return self:_LoadReplicationHook() end return Result.VoidOk end ViewAngleDriver.ClearAll = function(self) self:_CancelRestore() table.clear(self._payloadBySlot) self._winning = nil self._dirty = constantPool[153] end ViewAngleDriver.Flush = function(self) if not self._dirty then return end if self._fullySuppressed then return end local winning = self._winning if winning == nil then self._dirty = false self._restoreTask = self._restoreTrove:Add(task.delay(0.05 + localPlayer:GetNetworkPing(), function() if self._winning ~= nil or self._fullySuppressed then return end fireServer2(updateCameraRotationRemote, RotationCodec.encodeCameraRotation(get()), nil) end)) return end self._dirty = false fireServer2(updateCameraRotationRemote, encodeViewAngle(winning), nil) end ViewAngleDriver._RevertHooks = function(self) local jointsRestore = self._jointsRestore if jointsRestore ~= nil then self._jointsRestore = nil rawSet(clientFighterCharacterJoints, 'Update', jointsRestore.JointsUpdateMethod) end local replicationRestore = self._replicationRestore if replicationRestore ~= nil then self._replicationRestore = nil debug.setupvalue( replicationRestore.CameraReplicationLoopMethod, replicationRestore.UtilityIndex, replicationRestore.Utility ) end end ViewAngleDriver.Destroy = function(self) self._restoreTrove:Destroy() self._restoreTask = nil self:_RevertHooks() end return ViewAngleDriver end modules.cI = function() local cached = modules.cache.cI if not cached then cached = { c = buildViewAngleDriver() } modules.cache.cI = cached end return cached.c end end do -- cJ CharacterController: server CFrame and view angles of the local character local function buildCharacterController() modules.cr() local mechanicsController = modules.aS().MechanicsController local ServerCFrameSync = modules.cs() local ServerOrigin = modules.cG() local Trove = modules.k() local ViewAngleDriver = modules.cI() local CharacterController = {} CharacterController.__index = CharacterController CharacterController.new = function(context) local characterController = Trove.new('context.character_controller') local controller = { _trove = characterController, _context = context, _viewAngleDriver = characterController:Add(ViewAngleDriver.new()) } setmetatable(controller, CharacterController) controller:_Initialize() return controller end CharacterController._Initialize = function(self) local function createRootSync(characterState) self._rootDesync = self._trove:Add(ServerCFrameSync.new(characterState.RootPart)) end self._trove:Connect(self._context.Created, createRootSync) self._trove:Connect(self._context.Died, function() self._viewAngleDriver:ClearAll() local rootDesync = self._rootDesync if rootDesync ~= nil then self._trove:Remove(rootDesync) self._rootDesync = nil end end) local state = self._context.State if state.Alive then createRootSync(state) end end CharacterController.SetServerCFrame = function(self, cframe) local rootDesync = self._rootDesync if rootDesync == nil then return end rootDesync:SetServerCFrame(cframe) end CharacterController.GetServerCFrame = function(self) local rootDesync = self._rootDesync if rootDesync == nil then return nil end return rootDesync:GetServerCFrame() end CharacterController.IsCrouching = function() local crouchingValue = constantPool[34] return rawGet(mechanicsController, 'IsCrouching') == crouchingValue end if not integrityFlagB then return end CharacterController.GetServerOriginAnchor = function(self) local isCrouching = self:IsCrouching() local rootDesync = self._rootDesync local offset = ServerOrigin.getServerOriginOffset(isCrouching) return rootDesync ~= nil and rootDesync:GetServerCFrame() + offset or nil, isCrouching end CharacterController.GetServerHeadOrigin = function(self) return (self:GetServerOriginAnchor()) end CharacterController.GetClientCFrame = function(self) local rootDesync = self._rootDesync if rootDesync == nil then return nil end return rootDesync:GetClientCFrame() end CharacterController.SendViewAngles = function(self, slot, payload) self._viewAngleDriver:SendViewAngles(slot, payload) end CharacterController.SendRawCameraRotation = function(self, encoded) self._viewAngleDriver:SendRawCameraRotation(encoded) end CharacterController.SuppressCameraReplication = function(self, suppressed) self._viewAngleDriver:Suppress(suppressed) end CharacterController.FlushViewAngles = function(self) self._viewAngleDriver:Flush() end CharacterController.HeartbeatUpdate = function(self) local rootDesync = self._rootDesync if rootDesync == nil then return end rootDesync:HeartbeatUpdate() end CharacterController.Destroy = function(self) self._trove:Destroy() end return CharacterController end modules.cJ = function() local cached = modules.cache.cJ if not cached then cached = { c = buildCharacterController() } modules.cache.cJ = cached end return cached.c end end do -- cK EnumCodec: game enum <-> byte local function buildEnumCodec() local enumLibrary = modules.aS().EnumLibrary local toEnum = rawGet(enumLibrary, '_to_enum') local fromEnum = rawGet(enumLibrary, '_from_enum') return { encode = function(name) return rawGet(toEnum, name) end, decode = function(id) return rawGet(fromEnum, id) end, } end modules.cK = function() local cached = modules.cache.cK if not cached then local created = { c = buildEnumCodec() } modules.cache.cK = created cached = created end return cached.c end end do -- cL ItemInterfaceAdapter: tracks an item interface instance local function buildItemInterfaceAdapter() modules.aO() local GoodSignal = modules.g() local Trove = modules.k() local signalConnect = modules.ct() local ItemInterfaceAdapter = {} ItemInterfaceAdapter.__index = ItemInterfaceAdapter ItemInterfaceAdapter.new = function(item) local trove = Trove.new('ItemInterfaceAdapter') local adapter = { _trove = trove, Instance = rawGet(item, 'ItemInterface'), Created = trove:Add(GoodSignal.new()), } setmetatable(adapter, ItemInterfaceAdapter) trove:Add(signalConnect(rawGet(item, 'InterfaceAdded'), function(instance) adapter.Instance = instance adapter.Created:Fire(instance) end)) return adapter end ItemInterfaceAdapter.Destroy = function(self) self.Instance = nil self._trove:Destroy() end return ItemInterfaceAdapter end modules.cL = function() local cached = modules.cache.cL if not cached then local created = { c = buildItemInterfaceAdapter() } modules.cache.cL = created cached = created end return cached.c end end do -- cM CustomItem local function buildCustomItem() local EnumCodec = modules.cK() modules.aM() local ItemInterfaceAdapter = modules.cL() modules.aO() local callAtIdentityTwo = modules.b3() local useItemRemote = modules.aS().UseItemRemote local CustomItem = {} CustomItem.__index = CustomItem CustomItem.__type = 'Custom' CustomItem.new = function(inner, index) local info2 = rawGet(inner, 'Info') local data = rawGet(inner, 'Data') local item = { Index = index, _data = data, Info = info2, _clientFighter = rawGet(inner, 'ClientFighter'), ItemInterface = ItemInterfaceAdapter.new(inner), Inner = inner, Name = rawGet(inner, 'Name'), ObjectId = rawGet(data, 'ObjectID'), } setmetatable(item, CustomItem) return item end CustomItem.Destroy = function(self) self.ItemInterface:Destroy() end CustomItem.Equip = function(self) if self:IsEquipped() then return end local clientFighter = self._clientFighter task.spawn(callAtIdentityTwo, clientFighter.EquipItem, clientFighter, self.Index) end CustomItem.IsEquipped = function(self) return rawGet(self.Inner, 'IsEquipped') end CustomItem.Reload = function(self) local ammo = self:GetAmmo() local ammoReserve = self:GetAmmoReserve() if ammo == nil or ammoReserve == nil or self:IsReloading() or ammoReserve <= 0 or self:IsMagFull() then return end local wasEmpty = ammo == 0 local animations = rawGet(rawGet(rawGet(self.Inner, 'ViewModel'), 'Info'), 'Animations') local useEmptyReload = (rawGet(animations, 'EmptyReload') ~= nil or rawGet(animations, 'EmptyReloadStart') ~= nil) and wasEmpty local animationName = 'Reload' if useEmptyReload then animationName = 'EmptyReload' end local animation = rawGet(self.Info, animations[animationName]) if animation == nil then return end local length = animation.Length local n = tick() + length local inner = self.Inner rawSet(inner, '_reload_cooldown', n) rawSet(inner, '_reload_cancel_cooldown', wasEmpty and n or tick() + 0.25) local actionTimestamp = animation.ActionTimestamp rawSet(inner, '_reload_cancel_expiration', tick() + actionTimestamp) local payload = { ['\1'] = EnumCodec.encode(animationName) } fireServer(useItemRemote, self.ObjectId, EnumCodec.encode('StartReloading'), payload, nil) end CustomItem.IsAmmoBased = function(self) return rawGet(self._data, 'Ammo') ~= nil end CustomItem.GetAmmo = function(self) return rawGet(self._data, 'Ammo') end CustomItem.GetAmmoReserve = function(self) return rawGet(self._data, 'AmmoReserve') end CustomItem.IsMagFull = function(self) local ammo = self:GetAmmo() if ammo == nil then return nil end local maxAmmo = rawGet(self.Info, 'MaxAmmo') return maxAmmo ~= nil and ammo >= maxAmmo end CustomItem.IsReloading = function(self) local cooldownEnd = rawGet(self.Inner, '_reload_cooldown') return cooldownEnd ~= nil and tick() < cooldownEnd end return CustomItem end modules.cM = function() local cached = modules.cache.cM if not cached then cached = { c = buildCustomItem() } modules.cache.cM = cached end return cached.c end end do -- cN CFrameCodec: CFrame <-> payload table local function buildCFrameCodec() return { encode = function(cframe) local rx, ry, rz = cframe:ToOrientation() return { ['\0'] = cframe.X, ['\1'] = cframe.Y, ['\2'] = cframe.Z, ['\3'] = rx, ['\4'] = ry, ['\5'] = rz } end, decode = function(encoded) local tableType = constantPool[68] if type(encoded) ~= tableType then return nil end local x = encoded['\0'] local y = encoded['\1'] local z = encoded['\2'] local rx = encoded['\3'] local ry = encoded['\4'] local rz = encoded['\5'] local invalid = type(x) ~= 'number' if not invalid then local numberType = constantPool[95] invalid = type(y) ~= numberType end if invalid or type(z) ~= 'number' or type(rx) ~= 'number' or type(ry) ~= 'number' or type(rz) ~= 'number' then return nil end return CFrame.new(x, y, z) * CFrame.fromOrientation(rx, ry, rz) end, } end modules.cN = function() local cached = modules.cache.cN if not cached then local created = { c = buildCFrameCodec() } modules.cache.cN = created cached = created end return cached.c end end do -- cO raycastObjectSpace: hit position in a part object space local function buildRaycastObjectSpace() return function(origin, part, target) local raycastParams = RaycastParams.new() raycastParams.FilterType = Enum.RaycastFilterType.Include raycastParams.FilterDescendantsInstances = { part } local hit = workspace:Raycast(origin, target - origin, raycastParams) if hit == nil then return nil end local objectSpace = part.CFrame:PointToObjectSpace(hit.Position) if objectSpace == Vector3.zero then return nil end return objectSpace end end modules.cO = function() local cached = modules.cache.cO if not cached then cached = { c = buildRaycastObjectSpace() } modules.cache.cO = cached end return cached.c end end do -- cP encodeAimTarget: part + object space offset for a shot local function buildEncodeAimTarget() local CFrameCodec = modules.cN() local raycastObjectSpace = modules.cO() return function(origin, target) if target == nil then return nil, nil end local position = origin.Position local part = target.Part local objectSpace = target.ObjectSpace or raycastObjectSpace(position, part, part.Position) if objectSpace == nil or objectSpace == Vector3.zero then return nil, nil end return part, objectSpace ~= nil and CFrameCodec.encode(CFrame.new(objectSpace)) or CFrameCodec.encode( CFrame.new(part:GetClosestPointOnSurface(position)) ) end end modules.cP = function() local cached = modules.cache.cP if not cached then cached = { c = buildEncodeAimTarget() } modules.cache.cP = cached end return cached.c end end do -- cQ getEquipCooldown local function buildGetEquipCooldown() local itemLibrary = modules.aS().ItemLibrary return function(itemName) local itemInfo = rawGet(rawGet(itemLibrary, 'Items'), itemName) return itemInfo ~= nil and rawGet(itemInfo, 'EquipCooldown') or nil end end modules.cQ = function() local cached = modules.cache.cQ if not cached then cached = { c = buildGetEquipCooldown() } modules.cache.cQ = cached end return cached.c end end do -- cR GunItem: shooting, reload and ammo bookkeeping local function buildGunItem() local CFrameCodec = modules.cN() local EnumCodec = modules.cK() modules.aM() modules.aO() local useItemRemote = modules.aS().UseItemRemote local ItemInterfaceAdapter = modules.cL() local ServerOrigin = modules.cG() local encodeAimTarget = modules.cP() local getEquipCooldown = modules.cQ() local callAtIdentityTwo = modules.b3() local GunItem = {} GunItem.__index = GunItem GunItem.__type = 'Gun' GunItem.new = function(inner, index) local info2 = rawGet(inner, 'Info') local data = rawGet(inner, 'Data') local item = { Index = index, _data = data, Info = info2, _clientFighter = rawGet(inner, 'ClientFighter'), _aimScopePercent = rawGet(info2, 'AimScopePercent'), _equipRequestedAt = nil, _expectedAmmoRaw = rawGet(data, 'Ammo'), _nextLikelyShotRequestAt = 0, _outstandingAmmoSpend = 0, _outstandingAmmoSpendDeadline = nil, ItemInterface = ItemInterfaceAdapter.new(inner), Inner = inner, Name = rawGet(inner, 'Name'), ObjectId = rawGet(data, 'ObjectID'), IsRaycast = rawGet(info2, 'IsRaycast'), MaxAmmo = rawGet(info2, 'MaxAmmo'), ShotRequestCount = 0, EquipCooldown = getEquipCooldown(rawGet(inner, 'Name')) or 0, } setmetatable(item, GunItem) return item end GunItem.Destroy = function(self) self.ItemInterface:Destroy() end GunItem._ClearPendingShots = function(self) self._expectedAmmoRaw = self:GetAmmo() self._nextLikelyShotRequestAt = constantPool[186] self._outstandingAmmoSpend = constantPool[186] self._outstandingAmmoSpendDeadline = nil end GunItem._ReconcilePendingShots = function(self, now) local ammo = self:GetAmmo() local expectedAmmoRaw = self._expectedAmmoRaw if ammo > expectedAmmoRaw then self:_ClearPendingShots() return end if ammo < expectedAmmoRaw then self._outstandingAmmoSpend = math.max(self._outstandingAmmoSpend - expectedAmmoRaw - ammo, constantPool[186]) self._expectedAmmoRaw = ammo if self._outstandingAmmoSpend <= 0 then self._outstandingAmmoSpendDeadline = nil end end local outstandingAmmoSpendDeadline = self._outstandingAmmoSpendDeadline if outstandingAmmoSpendDeadline ~= nil and now >= outstandingAmmoSpendDeadline then self._outstandingAmmoSpend = 0 self._nextLikelyShotRequestAt = 0 self._outstandingAmmoSpendDeadline = nil end end GunItem._RecordPendingShot = function(self) local now2 = os.clock() self:_ReconcilePendingShots(now2) if now2 < self._nextLikelyShotRequestAt then return end local n = math.max(self._expectedAmmoRaw - self._outstandingAmmoSpend, 0) if n <= constantPool[186] then return end local burst = math.max(self.Info.ShootBurst or 1, 1) self._outstandingAmmoSpend = self._outstandingAmmoSpend + math.min(burst, n) self._outstandingAmmoSpendDeadline = now2 + ServerOrigin.getEstimatedRemoteDelay() + ServerOrigin.getEstimatedReplicationDelay() local quickShotCooldown = self:IsFanFired() and rawGet(self.Info, 'QuickShotCooldown') or nil if quickShotCooldown ~= nil then self._nextLikelyShotRequestAt = now2 + quickShotCooldown return end local shootBurstCooldown = self.Info.ShootBurstCooldown or 0 self._nextLikelyShotRequestAt = now2 + (burst - 1) * shootBurstCooldown + math.max(self.Info.ShootCooldown or 0, shootBurstCooldown) end GunItem.Equip = function(self) if self:IsEquipped() then self._equipRequestedAt = nil return end local now2 = tick() local equipRequestedAt = self._equipRequestedAt if equipRequestedAt ~= nil and now2 < equipRequestedAt + self.EquipCooldown then return end self:_ClearPendingShots() self._equipRequestedAt = now2 local clientFighter = self._clientFighter task.spawn(callAtIdentityTwo, clientFighter.EquipItem, clientFighter, self.Index) end GunItem.IsEquipped = function(self) return (rawGet(self.Inner, 'IsEquipped')) end GunItem.IsFanFired = function(self) return self.Name == 'Revolver' end GunItem.ShootAt = function(self, originCFrame, aimCFrame, hitTarget) local part, objectSpace = encodeAimTarget(originCFrame, hitTarget) self:ShootEncoded(CFrameCodec.encode(originCFrame), CFrameCodec.encode(aimCFrame), part, objectSpace) end GunItem.ShootEncoded = function(self, origin, aim, part, hitOffset) local shot = { ['\0'] = origin, ['\1'] = aim, ['\2'] = part, ['\3'] = hitOffset, ['\4'] = self:IsFanFired() or nil } local payload = self.IsRaycast and { ['\1'] = shot, ['\2'] = true } or { ['\1'] = shot } fireServer(useItemRemote, self.ObjectId, EnumCodec.encode('StartShooting'), payload, nil) self.ShotRequestCount = self.ShotRequestCount + 1 self:_RecordPendingShot() end GunItem.IsFullyAiming = function(self) local isAiming = rawGet(self._data, 'IsAiming') local aimScopePercent = self._aimScopePercent local currentAimValue = rawGet(rawGet(self.Inner, 'ViewModel'), 'CurrentAimValue') return isAiming and (aimScopePercent == nil or currentAimValue >= aimScopePercent) end GunItem.Reload = function(self) if self:IsReloading() or self:GetAmmoReserve() <= 0 or self:IsMagFull() then return false end local Reload = EnumCodec.encode('Reload') fireServer(useItemRemote, self.ObjectId, EnumCodec.encode('StartReloading'), { ['\1'] = Reload, ['\2'] = Reload }, nil) return true end GunItem.GetAmmo = function(self) return rawGet(self._data, 'Ammo') end GunItem.GetExpectedAmmoAfterPendingShots = function(self) self:_ReconcilePendingShots(os.clock()) return math.max(self._expectedAmmoRaw - self._outstandingAmmoSpend, 0) end GunItem.GetAmmoReserve = function(self) return rawGet(self._data, 'AmmoReserve') or math.huge end GunItem.GetShootTimeRemaining = function(self) local zero = constantPool[186] return math.max(rawGet(self.Inner, '_shoot_cooldown') - tick(), zero) end GunItem.IsOnShootCooldown = function(self) return self:GetShootTimeRemaining() > 0 end GunItem.GetReloadTimeRemaining = function(self) local now2 = tick() local reloadCooldown = rawGet(self.Inner, '_reload_cooldown') local n = 0 if reloadCooldown ~= nil then n = math.max(reloadCooldown - now2, 0) end local noAmmoCooldown = rawGet(self.Inner, '_shoot_cooldown_no_ammo') local remaining if noAmmoCooldown ~= nil then remaining = math.max(n, noAmmoCooldown - now2) else remaining = n end return remaining end GunItem.GetEquipTimeRemaining = function(self) if not self:IsEquipped() then local equipRequestedAt = self._equipRequestedAt if equipRequestedAt == nil then return self.EquipCooldown end return math.max(equipRequestedAt + self.EquipCooldown - tick(), 0) end self._equipRequestedAt = nil local equipCooldownEnd = rawGet(self.Inner, '_equip_cooldown') if equipCooldownEnd == nil then return 0 end return math.max(equipCooldownEnd - tick(), 0) end GunItem.IsReloading = function(self) return self:GetReloadTimeRemaining() > 0 end GunItem.IsMagFull = function(self) local maxAmmo = self.MaxAmmo return self:GetAmmo() >= maxAmmo end return GunItem end modules.cR = function() local cached = modules.cache.cR if not cached then cached = { c = buildGunItem() } modules.cache.cR = cached end return cached.c end end do -- cS MeleeItem: attacks and dash local function buildMeleeItem() local CFrameCodec = modules.cN() local EnumCodec = modules.cK() modules.aM() local ItemInterfaceAdapter = modules.cL() modules.aO() local callAtIdentityTwo = modules.b3() local useItemRemote = modules.aS().UseItemRemote local encodeAimTarget = modules.cP() local MeleeItem = {} MeleeItem.__index = MeleeItem MeleeItem.__type = 'Melee' MeleeItem.new = function(inner, index) local info2 = rawGet(inner, 'Info') local data = rawGet(inner, 'Data') local item = { Index = index, _info = info2, _clientFighter = rawGet(inner, 'ClientFighter'), ItemInterface = ItemInterfaceAdapter.new(inner), Inner = inner, Name = rawGet(inner, 'Name'), ObjectId = rawGet(data, 'ObjectID'), } setmetatable(item, MeleeItem) return item end MeleeItem.Destroy = function(self) self.ItemInterface:Destroy() end MeleeItem.Equip = function(self) if self:IsEquipped() then return end local clientFighter = self._clientFighter task.spawn(callAtIdentityTwo, clientFighter.EquipItem, clientFighter, self.Index) end MeleeItem.IsEquipped = function(self) return (rawGet(self.Inner, 'IsEquipped')) end MeleeItem.IsOnDashCooldown = function(self) local dashCooldownEnd = rawGet(self.Inner, '_dash_cooldown') return dashCooldownEnd ~= nil and tick() < dashCooldownEnd end MeleeItem.Dash = function(self) if rawGet(self._info, 'DashCooldown') == nil then if integrityCounterB > 4815 then -- (anti-tamper freeze trap removed) end return end local clientFighter = self._clientFighter callAtIdentityTwo(clientFighter.Input, clientFighter, 'StartAiming') end MeleeItem.Attack = function(self, originCFrame, aimCFrame, hitTarget) local part, objectSpace = encodeAimTarget(originCFrame, hitTarget) self:AttackEncoded(CFrameCodec.encode(originCFrame), CFrameCodec.encode(aimCFrame), part, objectSpace) end MeleeItem.AttackEncoded = function(self, origin, aim, part, hitOffset) local payload = { ['\1'] = { ['\0'] = origin, ['\1'] = aim, ['\2'] = part, ['\3'] = hitOffset }, ['\2'] = EnumCodec.encode('AttackAnimation1') or EnumCodec.encode('Attack1'), } fireServer(useItemRemote, self.ObjectId, EnumCodec.encode('StartShooting'), payload, nil) end MeleeItem.HeavyAttack = function(self, originCFrame, aimCFrame, hitTarget) local part, objectSpace = encodeAimTarget(originCFrame, hitTarget) self:HeavyAttackEncoded(CFrameCodec.encode(originCFrame), CFrameCodec.encode(aimCFrame), part, objectSpace) end MeleeItem.HeavyAttackEncoded = function(self, origin, aim, part, hitOffset) local payload = { ['\1'] = { ['\0'] = origin, ['\1'] = aim, ['\2'] = part, ['\3'] = hitOffset }, ['\2'] = EnumCodec.encode('HeavyAttackAnimation1') or EnumCodec.encode('HeavyAttack1'), } fireServer(useItemRemote, self.ObjectId, EnumCodec.encode('StartAiming'), payload, nil) end return MeleeItem end modules.cS = function() local cached = modules.cache.cS if not cached then cached = { c = buildMeleeItem() } modules.cache.cS = cached end return cached.c end end do -- cT ThrowableItem local function buildThrowableItem() local EnumCodec = modules.cK() modules.aM() local ItemInterfaceAdapter = modules.cL() modules.aO() local callAtIdentityTwo = modules.b3() local useItemRemote = modules.aS().UseItemRemote local ThrowableItem = {} ThrowableItem.__index = ThrowableItem ThrowableItem.__type = 'Throwable' ThrowableItem.new = function(inner, index) local info2 = rawGet(inner, 'Info') local data = rawGet(inner, 'Data') local item = { Index = index, _data = data, _clientFighter = rawGet(inner, 'ClientFighter'), ItemInterface = ItemInterfaceAdapter.new(inner), Info = info2, Inner = inner, Name = rawGet(inner, 'Name'), ObjectId = rawGet(data, 'ObjectID'), } setmetatable(item, ThrowableItem) return item end ThrowableItem.Destroy = function(self) self.ItemInterface:Destroy() end ThrowableItem.Equip = function(self) if self:IsEquipped() then return end local clientFighter = self._clientFighter task.spawn(callAtIdentityTwo, clientFighter.EquipItem, clientFighter, self.Index) end ThrowableItem.IsEquipped = function(self) return (rawGet(self.Inner, 'IsEquipped')) end ThrowableItem.IsOnCooldown = function(self) local cooldownEnd = rawGet(self.Inner, '_throw_cooldown') return cooldownEnd ~= nil and tick() < cooldownEnd end ThrowableItem.Throw = function(self) local inner = self.Inner local startShooting = callAtIdentityTwo(inner.Input, inner, 'StartShooting') local throwStartedAt = rawGet(inner, '_is_throwing') if startShooting == false or throwStartedAt == nil then return end if tick() < throwStartedAt + 0.6 then return end local ok, actionName, x, y, z = callAtIdentityTwo(inner.FinishShooting, inner, false) if ok == false then return end fireServer( useItemRemote, rawGet(self._data, 'ObjectID'), EnumCodec.encode(actionName), { ['\0'] = x, ['\1'] = y, ['\2'] = z }, nil ) end return ThrowableItem end modules.cT = function() local cached = modules.cache.cT if not cached then cached = { c = buildThrowableItem() } modules.cache.cT = cached end return cached.c end end do -- cU ItemBehaviors: item wrappers of the local fighter local function buildItemBehaviors() local CustomItem = modules.cM() modules.cF() local GunItem = modules.cR() modules.aO() local MeleeItem = modules.cS() local GoodSignal = modules.g() local ThrowableItem = modules.cT() local Trove = modules.k() local ItemBehaviors = {} ItemBehaviors.__index = ItemBehaviors ItemBehaviors.new = function(localFighterState) local itemBehaviors = Trove.new('context.item_behaviors') local behaviors = { _trove = itemBehaviors, _localFighterState = localFighterState, _items = {}, _itemByInner = {}, _itemByObjectId = {}, _itemByName = {}, ItemAdded = itemBehaviors:Add(GoodSignal.new()), ItemRemoved = itemBehaviors:Add(GoodSignal.new()), EquippedItemChanged = itemBehaviors:Add(GoodSignal.new()), } setmetatable(behaviors, ItemBehaviors) return behaviors end ItemBehaviors.Destroy = function(self) for _, item in self._items, nil, nil do item:Destroy() end self._trove:Destroy() end ItemBehaviors.Initialize = function(self) for k, inner in self._localFighterState:GetItems() do self:AddItem(inner, k) end local function refreshIndexes() for k, inner in self._localFighterState:GetItems() do local index = self:FindByInner(inner) if index ~= nil then self._items[index].Index = k end end end self._trove:Add(self._localFighterState:OnItemAddedSignal(function(inner) local item = self:AddItem(inner, table.find(self._localFighterState:GetItems(), inner) or #self._items) refreshIndexes() if item ~= nil then self.ItemAdded:Fire(item) end end)) self._trove:Add(self._localFighterState:OnItemRemovedSignal(function(inner) local index, item = self:FindByInner(inner) if index ~= nil and item ~= nil then table.remove(self._items, index) self._itemByObjectId[item.ObjectId] = nil self._itemByName[inner.Name] = nil self._itemByInner[inner] = nil refreshIndexes() self.ItemRemoved:Fire(item) item:Destroy() end end)) local function onEquippedChanged(inner) if inner ~= nil then self.EquippedItemChanged:Fire(self._itemByInner[inner]) else self.EquippedItemChanged:Fire(nil) end end self._trove:Add(self._localFighterState:OnEquippedItemChangedSignal(onEquippedChanged)) onEquippedChanged(self._localFighterState:GetEquippedItem()) end ItemBehaviors.AddItem = function(self, inner, index) if self._itemByInner[inner] then return nil end local item if inner.Info.Type == 'Melee' then item = MeleeItem.new(inner, index) elseif inner.Info.Type == 'Gun' then item = GunItem.new(inner, index) elseif inner.Info.Type == 'Throwable' then item = ThrowableItem.new(inner, index) else if inner.Info.Type ~= 'Custom' then return nil end item = CustomItem.new(inner, index) end table.insert(self._items, item) self._itemByInner[inner] = item self._itemByObjectId[item.ObjectId] = item self._itemByName[item.Name] = item return item end ItemBehaviors.FindByInner = function(self, inner) for k, item in self._items, nil, nil do if item.Inner == inner then return k, item end end return nil, nil end ItemBehaviors.FindByObjectId = function(self, objectId) return self._itemByObjectId[objectId] end ItemBehaviors.FindMeleeByName = function(self, name) local item = self._itemByName[name] if item == nil or item.__type ~= 'Melee' then return nil end return item end ItemBehaviors.FindGunByName = function(self, name) local item = self._itemByName[name] if item == nil or item.__type ~= 'Gun' then return nil end return item end ItemBehaviors.FindThrowableByName = function(self, name, requireReady) local item = self._itemByName[name] if item == nil or item.__type ~= 'Throwable' then return nil end if requireReady and item:IsOnCooldown() then return nil end return item end ItemBehaviors.FindCustomByName = function(self, name) local item = self._itemByName[name] if item == nil or item.__type ~= 'Custom' then return nil end return item end ItemBehaviors.GetHitscanGun = function(self) for _, item in self._items, nil, nil do if item.__type == 'Gun' and item.IsRaycast then return item end end return nil end ItemBehaviors.GetEquipped = function(self) local equippedItem = self._localFighterState:GetEquippedItem() if equippedItem == nil then return nil end return self._itemByInner[equippedItem] end ItemBehaviors.GetEquippedAmmoState = function(self) local equipped = self:GetEquipped() if equipped == nil then return nil end if equipped.__type == 'Gun' then local zero = constantPool[186] return equipped:GetAmmo() > zero end return nil end ItemBehaviors.EquippedItemAsGun = function(self) local equipped = self:GetEquipped() if equipped ~= nil and equipped.__type == 'Gun' then return equipped end return nil end ItemBehaviors.EquippedItemAsMelee = function(self) local equipped = self:GetEquipped() if equipped ~= nil and equipped.__type == 'Melee' then return equipped end return nil end ItemBehaviors.EquippedItemAsThrowable = function(self) local equipped = self:GetEquipped() if equipped ~= nil and equipped.__type == 'Throwable' then return equipped end return nil end ItemBehaviors.EquippedItemAsCustom = function(self) local equipped = self:GetEquipped() if equipped ~= nil and equipped.__type == 'Custom' then return equipped end return nil end ItemBehaviors.HasEquippedGun = function(self) return self:EquippedItemAsGun() ~= nil end ItemBehaviors.GetEquippedGunName = function(self) local gun = self:EquippedItemAsGun() return gun ~= nil and gun.Inner.Name or nil end ItemBehaviors.GetItems = function(self) return self._items end return ItemBehaviors end modules.cU = function() local cached = modules.cache.cU if not cached then cached = { c = buildItemBehaviors() } modules.cache.cU = cached end return cached.c end end do -- cV signalOnce: raw Once function local function buildSignalOnce() return modules.g().new().Once end modules.cV = function() local cached = modules.cache.cV if not cached then cached = { c = buildSignalOnce() } modules.cache.cV = cached end return cached.c end end do -- cW LoadoutPicker: pick / trash weapons local function buildLoadoutPicker() modules.cF() local Promise = modules.ay() local pickWeaponsRemote = modules.aS().PickWeaponsRemote local shootingRangeTrashItemsEvent = modules.aS().ShootingRangeTrashItemsEvent modules.g() local readDataField = modules.cv() local signalOnce = modules.cV() local LoadoutPicker = {} LoadoutPicker.__index = LoadoutPicker LoadoutPicker.new = function(fighterState) return setmetatable({ _fighterState = fighterState }, LoadoutPicker) end LoadoutPicker.PickLoadoutAsync = function(self, loadout) local alive = self._fighterState.Character.State.Alive if not readDataField(self._fighterState.Inner, 'CanPickWeapons') or not alive then return Promise.reject() end if #self._fighterState:GetItems() < 4 then return self:_ForcePickLoadoutAsync(loadout) end if readDataField(self._fighterState.Inner, 'IsInShootingRange') then return self:_TrashRangeItemsAsync():andThen(function() return self:_ForcePickLoadoutAsync(loadout) end) end return Promise.reject() end LoadoutPicker._ForcePickLoadoutAsync = function(self, loadout) fireServer(pickWeaponsRemote, loadout) local connection = nil return Promise.new(function(resolve) connection = signalOnce(self._fighterState.Inner.ItemAdded, function() resolve() end) end) :timeout(1.5) :finally(function() connection:Disconnect() end) end LoadoutPicker._TrashRangeItemsAsync = function(self) fireServer(shootingRangeTrashItemsEvent) local connection = nil return Promise.new(function(resolve) connection = signalOnce(self._fighterState.Inner.ItemRemoved, function() resolve() end) end) :timeout(1.5) :finally(function() connection:Disconnect() end) end return LoadoutPicker end modules.cW = function() local cached = modules.cache.cW if not cached then local created = { c = buildLoadoutPicker() } modules.cache.cW = created cached = created end return cached.c end end do -- cX FighterContext: lifecycle of the local fighter context local function buildFighterContext() local CharacterController = modules.cJ() modules.cF() local ItemBehaviors = modules.cU() local LoadoutPicker = modules.cW() modules.cw() local GoodSignal = modules.g() local Trove = modules.k() local FighterContext = {} FighterContext.__index = FighterContext FighterContext.new = function(fighters) local context = Trove.new('context') local manager = { _trove = context, _contextTrove = context:Extend(), ContextAdded = context:Add(GoodSignal.new()), ContextRemoved = context:Add(GoodSignal.new()), _fighters = fighters, } setmetatable(manager, FighterContext) manager:_Initialize() return manager end FighterContext._ClearContext = function(self) local inner = self.Inner if inner == nil then self._contextTrove:Clean() return end self.Inner = nil self.ContextRemoved:Fire(inner) self._contextTrove:Clean() end FighterContext._Initialize = function(self) local function onLocalStateSet(localState) self:_ClearContext() local characterController = self._contextTrove:Add(CharacterController.new(localState.Character)) local itemBehaviors = self._contextTrove:Add(ItemBehaviors.new(localState)) itemBehaviors:Initialize() local inner = { CharacterController = characterController, FighterState = localState, ItemBehaviors = itemBehaviors, LoadoutPicker = LoadoutPicker.new(localState) } self.Inner = inner self.ContextAdded:Fire(inner) end self._trove:Connect(self._fighters.LocalStateSet, onLocalStateSet) self._trove:Connect(self._fighters.LocalStateRemoved, function() self:_ClearContext() end) local localState = self._fighters.LocalState if localState then onLocalStateSet(localState) end end FighterContext.ObserveContext = function(self, name, callback) local observer = Trove.new(name) local contextTrove = observer:Extend() local function clearContext() contextTrove:Clean() end local function onContextAdded(context) clearContext() callback(context, contextTrove) end observer:Connect(self.ContextAdded, onContextAdded) observer:Connect(self.ContextRemoved, clearContext) local inner = self.Inner if inner ~= nil then onContextAdded(inner) end return observer end FighterContext.HeartbeatUpdate = function(self) local inner = self.Inner if inner ~= nil then inner.CharacterController:HeartbeatUpdate() end end FighterContext.FlushViewAngles = function(self) local inner = self.Inner if inner ~= nil then inner.CharacterController:FlushViewAngles() end end FighterContext.Destroy = function(self) self:_ClearContext() self._trove:Destroy() end return FighterContext end modules.cX = function() local cached = modules.cache.cX if not cached then cached = { c = buildFighterContext() } modules.cache.cX = cached end return cached.c end end do -- c0 Keybind local function buildKeybind() local Keybind = {} Keybind.__index = Keybind Keybind.new = function(flag, kind, callback, initialState) return setmetatable({ Kind = kind, State = initialState == true, Flag = flag, Callback = callback }, Keybind) end Keybind.InputBegan = function(self) local kind = self.Kind if kind == 'Toggle' or kind == constantPool[3] then self.State = not self.State else if not (kind == 'Hold' or kind == 'Release') then return end self.State = kind == 'Hold' end self.Callback(self.State) end Keybind.InputEnded = function(self) local kind = self.Kind if kind == 'Hold' or kind == 'Release' then self.State = kind == 'Release' self.Callback(self.State) return end end Keybind.SetKind = function(self, kind) if self.Kind == kind then return end self.Kind = kind end return Keybind end modules.c0 = function() local cached = modules.cache.c0 if not cached then local created = { c = buildKeybind() } modules.cache.c0 = created cached = created end return cached.c end end do -- c1 KeybindController: input -> keybind dispatch local function buildKeybindController() modules.c0() local Trove = modules.k() local userInputService = cloneref(game:GetService('UserInputService')) local function bindKeyFromEnum(enumItem) if typeof(enumItem) ~= 'EnumItem' then return nil end local enumType = enumItem.EnumType if enumType == Enum.KeyCode then return 'KC/' .. enumItem.Name end local isSupported = enumType == Enum.UserInputType if isSupported then isSupported = enumItem == (Enum.UserInputType :: any).MouseButton1 or enumItem == (Enum.UserInputType :: any).MouseButton2 or enumItem == (Enum.UserInputType :: any).MouseButton3 end if isSupported then return 'UIT/' .. enumItem.Name end return nil end local function bindKeyFromInput(input) if true then local userInputType = input.UserInputType if userInputType == Enum.UserInputType.Keyboard then return 'KC/' .. input.KeyCode.Name end if userInputType == Enum.UserInputType.MouseButton1 or userInputType == Enum.UserInputType.MouseButton2 or userInputType == Enum.UserInputType.MouseButton3 then return 'UIT/' .. userInputType.Name end return nil end return nil -- (anti-tamper freeze trap removed) end local KeybindController = {} KeybindController.__index = KeybindController KeybindController.new = function() local controller = { _trove = Trove.new('keybind_controller'), _keybindByFlagByBindKey = {}, _bindKeyByFlag = {} } setmetatable(controller, KeybindController) controller:Initialize() return controller end KeybindController.Destroy = function(self) self._trove:Destroy() end local function shouldHandle(input, gameProcessed) if not gameProcessed then return false end local userInputType = input.UserInputType if userInputType == Enum.UserInputType.MouseButton1 or userInputType == Enum.UserInputType.MouseButton2 or userInputType == Enum.UserInputType.MouseButton3 then return false end return true end KeybindController.Initialize = function(self) local function each(input, method) local key = bindKeyFromInput(input) local set = key and self._keybindByFlagByBindKey[key] if set == nil then return end for _, keybind in table.clone(set) do keybind[method](keybind) end end self._trove:Connect(userInputService.InputBegan, function(input, processed) if shouldHandle(input, processed) then return end each(input, 'InputBegan') end) self._trove:Connect(userInputService.InputEnded, function(input) each(input, 'InputEnded') end) end KeybindController.Register = function(self, bind, keybind) local bindKey = bindKeyFromEnum(bind) if not bindKey then return false end local set = self._keybindByFlagByBindKey[bindKey] if not set then set = {} self._keybindByFlagByBindKey[bindKey] = set end local replaced = set[keybind.Flag] ~= nil set[keybind.Flag] = keybind self._bindKeyByFlag[keybind.Flag] = bindKey return replaced end KeybindController.Remove = function(self, flag) local bindKey = self._bindKeyByFlag[flag] if not bindKey then return false end local set = self._keybindByFlagByBindKey[bindKey] if not set then return constantPool[153] end set[flag] = nil self._bindKeyByFlag[flag] = nil if not next(set) then self._keybindByFlagByBindKey[bindKey] = nil end return true end KeybindController.FindByFlag = function(self, flag) local bindKey = self._bindKeyByFlag[flag] if not bindKey then return nil end local set = self._keybindByFlagByBindKey[bindKey] if not set then return nil end return set[flag] end KeybindController.ChangeState = function(self, flag, state) local keybind = self:FindByFlag(flag) if not keybind then return false end keybind.State = state return constantPool[34] end KeybindController.ChangeBind = function(self, flag, bind) local keybind = self:FindByFlag(flag) if not keybind then return false end self:Remove(flag) self:Register(bind, keybind) return true end KeybindController.ChangeKind = function(self, flag, kind) local keybind = self:FindByFlag(flag) if not keybind then return constantPool[153] end keybind:SetKind(kind) return true end return KeybindController end modules.c1 = function() local cached = modules.cache.c1 if not cached then cached = { c = buildKeybindController() } modules.cache.c1 = cached end return cached.c end end do -- c2 KeybindRegistry: bind config keybinds to the store local function buildKeybindRegistry() local Keybind = modules.c0() modules.c1() local PathUtil = modules.j() local Result = modules.a() local Trove = modules.k() local concatArrays = modules.a6() local validKinds = { Always = constantPool[34], Hold = true, Release = true, Tap = true, Toggle = true } local function isValidKind(kind) return type(kind) == 'string' and validKinds[kind] == true end local function isBindable(enumItem) if typeof(enumItem) ~= 'EnumItem' then return false end if enumItem.EnumType == (Enum.KeyCode :: any) then return constantPool[34] end return enumItem.EnumType == (Enum.KeyCode :: any) or enumItem.EnumType == (Enum.UserInputType :: any) and (enumItem == (Enum.UserInputType :: any).MouseButton1 or enumItem == (Enum.UserInputType :: any).MouseButton2 or enumItem == ( Enum.UserInputType :: any ).MouseButton3) end local function flagFor(path) return 'CONFIGURATION/' .. table.concat(path, '/') end local KeybindRegistry KeybindRegistry = { isKeybindConfig = function(config) if type(config) ~= 'table' then return false end local hasValidState = type(config.State) == 'boolean' and isValidKind(config.Kind) local valid if hasValidState then valid = config.Bind == nil or isBindable(config.Bind) else valid = hasValidState end return valid end, clear = function(deps, registrations) for _, registration in registrations, nil, nil do deps.Controller:Remove(registration.Flag) registration.Trove:Destroy() end table.clear(registrations) end, registerPath = function(deps, registrations, data, path) local pathKey = PathUtil.PathToKey(path) if registrations[pathKey] ~= nil then return Result.VoidOk end local config = PathUtil.NavigateTo(data, path) if not KeybindRegistry.isKeybindConfig(config) then local pathText = table.concat(path, '.') return Result.err( 'KeybindRegistry', 'registerPath', string.format('invalid keybind configuration at path `%s`', tostring(pathText)) ) end local flag = flagFor(path) local statePath = concatArrays(path, { 'State' }) local Registry = Trove.new('keybind_controller.Registry') if config.Kind == 'Always' and config.State ~= true then deps.Set(statePath, true) end local keybind = Keybind.new(flag, config.Kind, function(state) deps.Set(statePath, state) end, config.State) local bind = config.Bind if bind ~= nil then deps.Controller:Register(bind, keybind) end local function syncKind(kind) if not isValidKind(kind) then return false end if keybind.Kind ~= kind and not deps.Controller:ChangeKind(flag, kind) then keybind:SetKind(kind) end return true end local function syncBind(newBind) if newBind == bind then return end bind = newBind if isBindable(newBind) then if not deps.Controller:ChangeBind(flag, newBind) then deps.Controller:Register(newBind, keybind) end else deps.Controller:Remove(flag) end end local function syncState(state) if keybind.State ~= state and not deps.Controller:ChangeState(flag, state) then keybind.State = state end end local function onKindChanged(newKind) local kind = keybind.Kind if not syncKind(newKind) then return end if newKind == 'Always' then deps.Set(statePath, true) elseif kind == 'Always' then deps.Set(statePath, false) end end local function syncFromConfig() local current = PathUtil.NavigateTo(data, path, constantPool[34]) if not KeybindRegistry.isKeybindConfig(current) then return end syncKind(current.Kind) local state = current.State if current.Kind == constantPool[21] then deps.Set(statePath, true) state = true end syncBind(current.Bind) syncState(state) end Registry:Connect(deps.GetPropertyChangedSignal(concatArrays(path, { 'Kind' })), onKindChanged) Registry:Connect(deps.GetPropertyChangedSignal(concatArrays(path, { 'Bind' })), syncBind) Registry:Connect(deps.GetPropertyChangedSignal(statePath), syncState) registrations[pathKey] = { Flag = flag, Trove = Registry, SyncFromConfig = syncFromConfig } return Result.VoidOk end, collectPaths = function(node, path, paths) if KeybindRegistry.isKeybindConfig(node) then table.insert(paths, path) return end for k, child in node, nil, nil do if type(child) == 'table' then KeybindRegistry.collectPaths(child, concatArrays(path, { k }), paths) end end end, } return KeybindRegistry end modules.c2 = function() local cached = modules.cache.c2 if not cached then local created = { c = buildKeybindRegistry() } modules.cache.c2 = created cached = created end return cached.c end end do -- c3 splitKeybindPath local function buildSplitKeybindPath() local concatArrays = modules.a6() return function(path) if path[#path] == 'Keybind' then local parent = {} table.move(path, 1, #path - constantPool[63], 1, parent) return parent, path end return path, concatArrays(path, { 'Keybind' }) end end modules.c3 = function() local cached = modules.cache.c3 if not cached then local created = { c = buildSplitKeybindPath() } modules.cache.c3 = created cached = created end return cached.c end end do -- c4 KeybindBridge: store <-> keybinds, enabled state local function buildKeybindBridge() local ragebotConfig = modules.bG() local ErrorReporter = modules.ad() local GlobalTrove = modules.n() local KeybindController = modules.c1() local KeybindRegistry = modules.c2() modules.a() local Trove = modules.k() local concatArrays = modules.a6() local splitKeybindPath = modules.c3() local KeybindBridge = {} KeybindBridge.__index = KeybindBridge KeybindBridge.new = function() local Bridge = Trove.new('keybind_controller.Bridge') local controller = KeybindController.new() Bridge:Add(controller) local errorReporter = Bridge:Add(ErrorReporter.new()) local obj = setmetatable({ _trove = Bridge, _deps = { Controller = controller, GetPropertyChangedSignal = function(path) return ragebotConfig:GetPropertyChangedSignal(path) end, Set = function(path, value) ragebotConfig:Set(path, value) end, }, _errorReporter = errorReporter, _registrationByPathKey = {}, }, KeybindBridge) obj:_RebuildAll() Bridge:Connect(ragebotConfig.Reloaded, function() obj:_RebuildAll() end) return obj end KeybindBridge.Destroy = function(self) KeybindRegistry.clear(self._deps, self._registrationByPathKey) self._trove:Destroy() end KeybindBridge._RebuildAll = function(self) KeybindRegistry.clear(self._deps, self._registrationByPathKey) local paths = {} KeybindRegistry.collectPaths(ragebotConfig.Data, {}, paths) for _, path in paths, nil, nil do self._errorReporter:ReportResult(self:_RegisterPath(path)) end end KeybindBridge._RegisterPath = function(self, path) return KeybindRegistry.registerPath(self._deps, self._registrationByPathKey, ragebotConfig.Data, path) end KeybindBridge.EnsureRegistered = function(self, path) local _basePath, keybindPath = splitKeybindPath(path) return self:_RegisterPath(keybindPath) end KeybindBridge._GetEnabledKeybindState = function(self, basePath, keybindPath) if not ragebotConfig:Get(concatArrays(basePath, { 'Enabled' })) then return constantPool[153] end local alwaysKind = constantPool[21] if ragebotConfig:Get(concatArrays(keybindPath, { 'Kind' })) == alwaysKind then return true end return ragebotConfig:Get(concatArrays(keybindPath, { 'State' })) == true end KeybindBridge.GetEnabledKeybindState = function(self, path) local basePath, keybindPath = splitKeybindPath(path) self._errorReporter:ReportResult(self:_RegisterPath(keybindPath)) return self:_GetEnabledKeybindState(basePath, keybindPath) end KeybindBridge.ObserveEnabledKeybind = function(self, path, callback) local basePath, keybindPath = splitKeybindPath(path) self._errorReporter:ReportResult(self:_RegisterPath(keybindPath)) local Bridge = Trove.new('keybind_controller.Bridge') local enabledPath = concatArrays(basePath, { 'Enabled' }) local statePath = concatArrays(keybindPath, { 'State' }) local kindPath = concatArrays(keybindPath, { 'Kind' }) local function notify() callback(self:_GetEnabledKeybindState(basePath, keybindPath)) end Bridge:Connect(ragebotConfig:GetPropertyChangedSignal(enabledPath), notify) Bridge:Connect(ragebotConfig:GetPropertyChangedSignal(statePath), notify) Bridge:Connect(ragebotConfig:GetPropertyChangedSignal(kindPath), notify) notify() return Bridge end return GlobalTrove:Add(KeybindBridge.new()) end modules.c4 = function() local cached = modules.cache.c4 if not cached then local created = { c = buildKeybindBridge() } modules.cache.c4 = created cached = created end return cached.c end end do -- gA PartGlue: re-welds target parts while they are in use local function buildPartGlue() local findChildOfClass = findFirstChildOfClass local readProperty = gameIndex local writeProperty = gameNewIndex local random2 = math.random local cframe = CFrame.new(math.random(-100000, -10000), 100000, random2(-100000, 10000)) local function setPhysicsRepRoot(part, reference) local previousIdentity = getthreadidentity() setthreadidentity(8) local physicsRepRootRef, err = protectedCall(setHiddenProperty, part, 'PhysicsRepRootRef', reference) setthreadidentity(previousIdentity) if not physicsRepRootRef then error(err, 0) end end local function restoreTargetPart(part, info) local originalCFrame = info.OriginalCFrame local originalPart1 = info.OriginalPart1 local originalPart1ToTarget = info.OriginalPart1ToTarget if originalPart1 ~= nil and originalPart1ToTarget ~= nil then originalCFrame = originalPart1.CFrame * originalPart1ToTarget end if originalCFrame ~= nil then writeProperty(part, constantPool[127], originalCFrame) end local weld = info.Weld if weld ~= nil and originalPart1 ~= nil then writeProperty(weld, 'Part1', originalPart1) end local originalAnchored = info.OriginalAnchored if originalAnchored ~= nil then writeProperty(part, 'Anchored', originalAnchored) end end local PartGlue = {} PartGlue.__index = PartGlue PartGlue.new = function() local glue = { _glueInfoByTargetPart = {}, _targetPartBySourcePart = {} } setmetatable(glue, PartGlue) return glue end PartGlue.Acquire = function(self, sourcePart, targetPart) local info = self._glueInfoByTargetPart[targetPart] local reference = info and info.Reference or InstanceHandle.new(targetPart) setPhysicsRepRoot(sourcePart, reference) local currentTarget = self._targetPartBySourcePart[sourcePart] if currentTarget ~= targetPart then if currentTarget ~= nil then self:_ReleaseGlue(currentTarget) end self:_SetupGlue(targetPart, reference) self._targetPartBySourcePart[sourcePart] = targetPart end writeProperty(targetPart, 'CFrame', CFrame.new(cframe.Position)) return cframe end PartGlue.Free = function(self, sourcePart) local targetPart = self._targetPartBySourcePart[sourcePart] if targetPart == nil then return end self._targetPartBySourcePart[sourcePart] = nil self:_ReleaseGlue(targetPart) end PartGlue._SetupGlue = function(self, targetPart, reference) local info = self._glueInfoByTargetPart[targetPart] if info ~= nil then info.RefCount = info.RefCount + 1 return end local weldConstraint = findChildOfClass(targetPart, 'WeldConstraint') local originalAnchored = nil local originalCFrame = nil local originalPart1ToTarget = nil local originalPart1 = nil if weldConstraint ~= nil then local cFrame = readProperty(targetPart, 'CFrame') or targetPart.CFrame local anchored = readProperty(targetPart, 'Anchored') local booleanType = constantPool[116] if typeof(anchored) ~= booleanType then anchored = targetPart.Anchored end local part1 = readProperty(weldConstraint, 'Part1') or weldConstraint.Part1 local canRelocate = part1 ~= nil and cFrame ~= nil local part1ToTarget = nil if canRelocate then part1ToTarget = part1.CFrame:ToObjectSpace(cFrame) writeProperty(weldConstraint, 'Part1', nil) end writeProperty(targetPart, 'Anchored', constantPool[34]) originalAnchored = anchored originalCFrame = cFrame originalPart1ToTarget = part1ToTarget originalPart1 = part1 end self._glueInfoByTargetPart[targetPart] = { RefCount = 1, Reference = reference, Weld = weldConstraint, OriginalPart1 = originalPart1, OriginalPart1ToTarget = originalPart1ToTarget, OriginalCFrame = originalCFrame, OriginalAnchored = originalAnchored, } end PartGlue._ReleaseGlue = function(self, targetPart) local info = self._glueInfoByTargetPart[targetPart] if info == nil then return end info.RefCount = info.RefCount - 1 if info.RefCount > 0 then return end restoreTargetPart(targetPart, info) self._glueInfoByTargetPart[targetPart] = nil end PartGlue.FindAnchor = function(self, sourcePart) return self._targetPartBySourcePart[sourcePart] end PartGlue.Destroy = function(self) for k, info in self._glueInfoByTargetPart, nil, nil do restoreTargetPart(k, info) end table.clear(self._glueInfoByTargetPart) table.clear(self._targetPartBySourcePart) end return PartGlue end modules.gA = function() local cached = modules.cache.gA if not cached then local created = { c = buildPartGlue() } modules.cache.gA = created cached = created end return cached.c end end do -- g_ StateHook: forces outgoing state values (crouch) local function buildStateHook() local EnumCodec = modules.cK() local ErrorReporter = modules.ad() local Result = modules.a() local instancesEqual = modules.b2() local mechanicsController = modules.aS().MechanicsController local updateStateRemote = modules.aS().UpdateStateRemote local mechanics = mechanicsController local metatable = getmetatable(mechanicsController) local prototype = metatable ~= nil and rawGet(metatable, '__index') or nil local updateServerState = type(prototype) == 'table' and rawGet(prototype, '_UpdateServerState') or nil local updateServerStateFn = (type(updateServerState) == 'function' and { updateServerState } or { nil })[1] local StateHook = {} StateHook.__index = StateHook StateHook.new = function() local hook = { _errorReporter = ErrorReporter.new(), _dummy = Color3.new(), _forcedValueByEnumByte = {}, _loadFailure = nil, _restore = nil, } setmetatable(hook, StateHook) hook:_Initialize() return hook end StateHook._Initialize = function(self) local fakeReplicatedStorage = { Replication = { Fighter = { UpdateState = { FireServer = function(_remote, stateByte, value, ...) local forced = self._forcedValueByEnumByte[stateByte] if forced ~= nil then value = forced end local fire = fireServer local args = table.pack(...) local remote = updateStateRemote args.n = 4 + args.n - 1 table.move(args, 1, args.n, 4, args) args[1] = remote args[2] = stateByte args[3] = value fire(table.unpack(args, 1, args.n)) end, }, }, }, } setRawMetatable(self._dummy, { __index = function() return fakeReplicatedStorage end }) end StateHook.SetForced = function(self, stateName, value) local stateByte = EnumCodec.encode(stateName) if stateByte == nil or self._forcedValueByEnumByte[stateByte] == value then return end if not self:_EnsureLoaded().Ok then return end self._forcedValueByEnumByte[stateByte] = value fireServer(updateStateRemote, stateByte, value) end StateHook.GetOutgoingValue = function(self, stateName) local stateByte = EnumCodec.encode(stateName) local forced = (stateByte ~= nil and { self._forcedValueByEnumByte[stateByte] } or { nil })[1] if forced ~= nil then return forced end return rawGet(mechanics, stateName) end StateHook.ClearForced = function(self, stateName) local stateByte = EnumCodec.encode(stateName) if stateByte == nil or self._forcedValueByEnumByte[stateByte] == nil then return end self._forcedValueByEnumByte[stateByte] = nil fireServer(updateStateRemote, stateByte, rawGet(mechanics, stateName)) end StateHook._EnsureLoaded = function(self) if self._restore ~= nil then return Result.VoidOk end local loadFailure = self._loadFailure if loadFailure ~= nil then return loadFailure end local result = self._errorReporter:ReportResult(self:_Load()) if not result.Ok then self._loadFailure = result end return result end StateHook._Load = function(self) if self._restore ~= nil then return Result.VoidOk end local fn = updateServerStateFn if fn == nil then return Result.err('StateHook', 'function_lookup', '_UpdateServerState not found') end local upvalueIndex = nil local upvalueValue = nil for k, upvalue in debug.getupvalues(updateServerStateFn) do if not (typeof(upvalue) ~= 'Instance' or not instancesEqual(upvalue, game:GetService('ReplicatedStorage'))) then upvalueIndex = k upvalueValue = upvalue end end if upvalueIndex == nil or upvalueValue == nil then return Result.err('StateHook', 'upvalue_lookup', 'ReplicatedStorage not found in _UpdateServerState') end debug.setupvalue(updateServerStateFn, upvalueIndex, self._dummy) self._restore = { Fn = fn, Index = upvalueIndex, Original = upvalueValue } return Result.VoidOk end StateHook._Revert = function(self) local restore = self._restore if restore == nil then return end self._restore = nil debug.setupvalue(restore.Fn, restore.Index, restore.Original) end StateHook.Destroy = function(self) self:_Revert() self._errorReporter:Destroy() end return StateHook end modules.g_ = function() local cached = modules.cache.g_ if not cached then local created = { c = buildStateHook() } modules.cache.g_ = created cached = created end return cached.c end end do -- jy getRiotShieldSide local function buildGetRiotShieldSide() modules.cF() modules.jx() local function shieldSide(fighterState) local itemObserver = fighterState.ItemObserver local equippedItem = itemObserver:GetEquippedItem() if equippedItem ~= nil and equippedItem.Name == 'Riot Shield' then local pitch = math.deg(fighterState:GetCameraRotation().X) if pitch > 22 and pitch < 91 then return 'Below' end return 'Above' end for _, item in itemObserver:GetItems() do if item.Name == 'Riot Shield' then local pitch = math.deg(fighterState:GetCameraRotation().X) if pitch > 315 and pitch < 360 or pitch > 0 and pitch < 91 then return 'Above' end return 'Below' end end return constantPool[75] end if not integrityFlagA then return end return shieldSide end modules.jy = function() local cached = modules.cache.jy if not cached then local created = { c = buildGetRiotShieldSide() } modules.cache.jy = created cached = created end return cached.c end end do -- jz defensivePose: defensive CFrame and view angles local function buildDefensivePose() modules.cE() modules.jx() modules.cI() local getRiotShieldSide = modules.jy() local random = Random.new() return { getDefensiveCFrame = function(ownCFrame, mode, fighterState, targetCFrame) if mode == 'Equipped' then return CFrame.new(ownCFrame.Position, targetCFrame.Position) end if mode == 'Unequipped' then return CFrame.new(ownCFrame.Position, ownCFrame.Position + ownCFrame.Position - targetCFrame.Position) end local melee = fighterState.ItemObserver:EquippedItemAsMelee() if melee ~= nil and melee.Name == 'Knife' then local cframe = CFrame.fromOrientation local nextNumber = random.NextNumber local tau = math.tau return CFrame.new(ownCFrame.Position) * cframe(random:NextNumber(0, math.tau), random:NextNumber(0, math.tau), nextNumber(random, 0, tau)) end return ownCFrame end, getDefensiveViewAngles = function(mode, fighterState) if mode == 'None' then return nil end return { Kind = 'Normalized', Pitch = (mode == 'Equipped') ~= (getRiotShieldSide(fighterState) ~= 'Below') and constantPool[45] or -90, Yaw = random:NextNumber(0, 360), } end, } end modules.jz = function() local cached = modules.cache.jz if not cached then cached = { c = buildDefensivePose() } modules.cache.jz = cached end return cached.c end end do -- jA ShotLock: fire gate with a lock window local function buildShotLock() local ShotLock = {} ShotLock.__index = ShotLock ShotLock.new = function() return setmetatable({}, ShotLock) end ShotLock.ShouldFire = function(self, wantsToFire, lockDuration) local now2 = os.clock() local lockedUntil = self._lockedUntil local isLocked = lockedUntil ~= nil and now2 < lockedUntil if wantsToFire then self._lockedUntil = now2 + lockDuration end return isLocked or wantsToFire end ShotLock.Reset = function(self) self._lockedUntil = nil end return ShotLock end modules.jA = function() local cached = modules.cache.jA if not cached then local created = { c = buildShotLock() } modules.cache.jA = created cached = created end return cached.c end end do -- jB TargetSelector: enemy targets, hackers first local function buildTargetSelector() modules.cr() local ragebotConfig = modules.bG() modules.cF() modules.bM() local function isValidTarget(fighterState) if not fighterState.IsEnemy or fighterState:IsInvincible() then return false end if not fighterState.Character.State.Alive then return false end local melee = fighterState.ItemObserver:EquippedItemAsMelee() if melee ~= nil and melee:IsDeflecting() then return false end return true end local function makeTarget(fighterState) return { FighterState = fighterState, AliveState = fighterState.Character.State } end local TargetSelector = {} TargetSelector.__index = TargetSelector TargetSelector.new = function(fighters, playerTags) return setmetatable({ _fighters = fighters, _playerTags = playerTags }, TargetSelector) end TargetSelector.GetTarget = function(self) local prioritizeHackers = ragebotConfig.Data.Ragebot.PrioritizeHackers if prioritizeHackers then for _, player in self._playerTags:GetPlayersWith('Hacker') do local fighterState = self._fighters.StateByPlayer[player] if fighterState ~= nil and isValidTarget(fighterState) then return makeTarget(fighterState) end end end for k, fighterState in self._fighters.EnemyByPlayer, nil, nil do if prioritizeHackers and self._playerTags:Has(k, 'Hacker') then continue end if isValidTarget(fighterState) then return makeTarget(fighterState) end end return nil end TargetSelector.HasTargets = function(self) for _, fighterState in self._fighters.EnemyByPlayer, nil, nil do if isValidTarget(fighterState) then return true end end return constantPool[153] end TargetSelector.IsSameTarget = function(self, other) return self.FighterState == other.FighterState and self.AliveState == other.AliveState end return TargetSelector end modules.jB = function() local cached = modules.cache.jB if not cached then cached = { c = buildTargetSelector() } modules.cache.jB = cached end return cached.c end end do -- jC lookAtFlat local function buildLookAtFlat() local vector = Vector3.new(0, -constantPool[63], 0) local vector2 = Vector3.new(0, 0, -constantPool[63]) return function(from, to) local n = to - from local vector3 = Vector3.new(n.X, constantPool[186], n.Z) if vector3.Magnitude < 0.001 then vector3 = vector2 end return CFrame.lookAt(from, from + vector3, vector) end end modules.jC = function() local cached = modules.cache.jC if not cached then cached = { c = buildLookAtFlat() } modules.cache.jC = cached end return cached.c end end do -- jD GunPlanner: per-frame plan for hitscan weapons local function buildGunPlanner() local ragebotConfig = modules.bG() modules.cR() modules.gA() local ShotLock = modules.jA() modules.jB() local getRiotShieldSide = modules.jy() local lookAtFlat = modules.jC() local vector = Vector3.new(0, -0.7, 0.05) local vector2 = Vector3.new(0, -3.85, 0.05) local encodedOriginAbove = { ['\0'] = -9e37, ['\1'] = 0, ['\2'] = constantPool[186], ['\3'] = -1.5707963267948966, ['\4'] = 3.1415926535897931, ['\5'] = 3.1415926535897931, } local encodedAimAbove = { ['\0'] = constantPool[186], ['\1'] = -90000000, ['\2'] = 0, ['\3'] = -1.5707963267948966, ['\4'] = 3.1415926535897931, ['\5'] = 3.1415926535897931, } local encodedOriginBelow = { ['\0'] = -9e37, ['\1'] = constantPool[186], ['\2'] = 0, ['\3'] = 1.5707963267948966, ['\4'] = 3.1415926535897931, ['\5'] = 3.1415926535897931, } local encodedAimBelow = { ['\0'] = 0, ['\1'] = 90000000, ['\2'] = 0, ['\3'] = 1.5707963267948966, ['\4'] = 3.1415926535897931, ['\5'] = 3.1415926535897931, } local encodedHitOffset = { ['\0'] = 0, ['\1'] = 1, ['\2'] = constantPool[186], ['\3'] = constantPool[186], ['\4'] = 0, ['\5'] = 0 } local GunPlanner = {} GunPlanner.__index = GunPlanner GunPlanner.new = function(partGlue) return setmetatable({ _partGlue = partGlue, _shootLock = ShotLock.new() }, GunPlanner) end GunPlanner.Plan = function(self, frameTime, target, gun, ourPart, wantsToFire) local hitboxHead = target.AliveState.HitboxHead local above = getRiotShieldSide(target.FighterState) ~= 'Below' local ragebot = ragebotConfig.Data.Ragebot local offset = above and vector or vector2 local gluedCFrame = self._partGlue:Acquire(ourPart, hitboxHead) local n if above then n = gluedCFrame + offset else n = lookAtFlat(gluedCFrame.Position + offset, hitboxHead.Position) end if not self._shootLock:ShouldFire(wantsToFire, frameTime * ragebot.ShootFrames) then local random2 = math.random return CFrame.new(math.random(-1000000, 1000000), math.random(5000, 10000), random2(-1000000, 1000000)), nil end local origin = (above and { encodedOriginAbove } or { encodedOriginBelow })[1] local aim = (above and { encodedAimAbove } or { encodedAimBelow })[constantPool[63]] return n, function() gun:ShootEncoded(origin, aim, hitboxHead, encodedHitOffset) end end return GunPlanner end modules.jD = function() local cached = modules.cache.jD if not cached then local created = { c = buildGunPlanner() } modules.cache.jD = created cached = created end return cached.c end end do -- jE MeleePlanner: per-frame plan for melee (backstab window) local function buildMeleePlanner() modules.cJ() local ragebotConfig = modules.bG() modules.cS() modules.gA() local ShotLock = modules.jA() modules.jB() local getRiotShieldSide = modules.jy() local lookAtFlat = modules.jC() local vector = Vector3.new(0, -constantPool[195], 0.05) local vector2 = Vector3.new(constantPool[186], -3.85, 0.05) local encodedOriginAbove = { ['\0'] = -9e37, ['\1'] = 0, ['\2'] = 0, ['\3'] = -1.5707963267948966, ['\4'] = 3.1415926535897931, ['\5'] = 3.1415926535897931, } local encodedAimAbove = { ['\0'] = 0, ['\1'] = -90000000, ['\2'] = constantPool[186], ['\3'] = -1.5707963267948966, ['\4'] = 3.1415926535897931, ['\5'] = 3.1415926535897931, } local encodedOriginBelow = { ['\0'] = -9e37, ['\1'] = 0, ['\2'] = constantPool[186], ['\3'] = 1.5707963267948966, ['\4'] = 3.1415926535897931, ['\5'] = 3.1415926535897931, } local encodedAimBelow = { ['\0'] = 0, ['\1'] = 90000000, ['\2'] = constantPool[186], ['\3'] = 1.5707963267948966, ['\4'] = 3.1415926535897931, ['\5'] = 3.1415926535897931, } local encodedHitOffset = { ['\0'] = 0, ['\1'] = 1, ['\2'] = constantPool[186], ['\3'] = 0, ['\4'] = 0, ['\5'] = 0 } local function farAwayCFrame() local random2 = math.random return CFrame.new(math.random(-10000000, -100000), math.random(5000, 10000), random2(-10000000, -100000)) end local function viewAngles(pitchRad, yawRad) return { Kind = 'Normalized', Pitch = math.deg(pitchRad), Yaw = math.deg(yawRad) } end local function withOrientation(base, rx, ry, rz) return { ['\0'] = base['\0'], ['\1'] = base['\1'], ['\2'] = base['\2'], ['\3'] = rx, ['\4'] = ry, ['\5'] = rz, } end local MeleePlanner = {} MeleePlanner.__index = MeleePlanner MeleePlanner.new = function(partGlue) return setmetatable({ _partGlue = partGlue, _shootLock = ShotLock.new(), _hitboxWindowUntil = -1, _attackCooldown = -1 }, MeleePlanner) end MeleePlanner.Plan = function(self, frameTime, target, melee, gluedOurPart, wantsToFire) local aliveState = target.AliveState local hitboxHead = aliveState.HitboxHead local above = getRiotShieldSide(target.FighterState) ~= 'Below' local ragebot = ragebotConfig.Data.Ragebot local offset = above and vector or vector2 local gluedCFrame = self._partGlue:Acquire(gluedOurPart, hitboxHead) self._gluedOurPart = gluedOurPart local n if above then n = gluedCFrame + offset else n = lookAtFlat(gluedCFrame.Position + offset, hitboxHead.Position) end local rx, ry, rz = aliveState.RootPart.CFrame:ToOrientation() local pitch = above and -1.5707963267948966 or 1.5707963267948966 local origin = (above and { encodedOriginAbove } or { encodedOriginBelow })[1] local aim = (above and { encodedAimAbove } or { encodedAimBelow })[1] local originPose = withOrientation(origin, pitch, ry, rz) local aimPose = withOrientation(aim, pitch, ry, rz) local now2 = os.clock() if now2 < self._hitboxWindowUntil then return n, viewAngles(rx, ry), function() melee:HeavyAttackEncoded(originPose, aimPose, hitboxHead, encodedHitOffset) end end if not self._shootLock:ShouldFire(wantsToFire, frameTime * ragebot.ShootFrames) then return farAwayCFrame(), nil, nil end if now2 < self._attackCooldown then return farAwayCFrame(), nil, nil end if melee.Name == 'Knife' then self:_RecordBackstab() return n, viewAngles(rx, ry), function() melee:HeavyAttackEncoded(originPose, aimPose, hitboxHead, encodedHitOffset) end end return n, nil, function() melee:AttackEncoded(originPose, aimPose, hitboxHead, encodedHitOffset) end end MeleePlanner._RecordBackstab = function(self) local now2 = os.clock() self._hitboxWindowUntil = now2 + 0.625 self._attackCooldown = now2 + 1.25 end MeleePlanner.ResetState = function(self) self._hitboxWindowUntil = -1 self._attackCooldown = -1 self._shootLock:Reset() local gluedOurPart = self._gluedOurPart if gluedOurPart ~= nil then self._partGlue:Free(gluedOurPart) self._gluedOurPart = nil end end return MeleePlanner end modules.jE = function() local cached = modules.cache.jE if not cached then local created = { c = buildMeleePlanner() } modules.cache.jE = created cached = created end return cached.c end end do -- jF randomPointAround local function buildRandomPointAround() local random = Random.new() return function(center, minRadius, maxRadius) local angle = random:NextNumber(0, 6.2831853071795862) local radius = random:NextNumber(minRadius, maxRadius) local cframe = CFrame.new(center + Vector3.new(math.cos(angle) * radius, 0, math.sin(angle) * radius)) local nextNumber = random.NextNumber return cframe * CFrame.fromOrientation( random:NextNumber(0, 6.2831853071795862), random:NextNumber(0, 6.2831853071795862), nextNumber(random, 0, 6.2831853071795862) ) end end modules.jF = function() local cached = modules.cache.jF if not cached then cached = { c = buildRandomPointAround() } modules.cache.jF = cached end return cached.c end end do -- jG surfaceNormal local function buildSurfaceNormal() return function(part, point) local closestPointOnSurface = part:GetClosestPointOnSurface(point) local offset = point - closestPointOnSurface if offset.Magnitude < 1e-06 then return closestPointOnSurface, nil end local dx = part:GetClosestPointOnSurface(point + Vector3.xAxis * 0.05) - closestPointOnSurface local dy = part:GetClosestPointOnSurface(point + Vector3.yAxis * 0.05) - closestPointOnSurface local dz = part:GetClosestPointOnSurface(point + Vector3.zAxis * 0.05) - closestPointOnSurface local normal = dy:Cross(dx) if normal.Magnitude < 1e-06 then normal = dy:Cross(dz) end if normal.Magnitude < 1e-06 then normal = dx:Cross(dz) end if normal.Magnitude < 1e-06 then return closestPointOnSurface, nil end if normal:Dot(offset) < 0 then normal = -normal end return closestPointOnSurface, normal.Unit end end modules.jG = function() local cached = modules.cache.jG if not cached then local created = { c = buildSurfaceNormal() } modules.cache.jG = created cached = created end return cached.c end end do -- jH ProjectileBreakerTeleport: surface pool and positions local function buildProjectileBreakerTeleport() local ragebotConfig = modules.bG() modules.cF() modules.cX() local Trove = modules.k() local randomPointAround = modules.jF() local surfaceNormal = modules.jG() local collectionService = cloneref(game:GetService('CollectionService')) local random = Random.new() local vector = Vector3.new(5, 5, 5) local overlapParams = OverlapParams.new() overlapParams.FilterType = Enum.RaycastFilterType.Exclude overlapParams.FilterDescendantsInstances = {} overlapParams.BruteForceAllSlow = constantPool[34] local function surfaceFrame(position, normal, part) local unit = normal.Unit local toPart = part.Position - position local perpendicular = toPart - unit * toPart:Dot(unit) if perpendicular.Magnitude < 0.0001 then local lookVector = part.CFrame.LookVector perpendicular = lookVector - unit * lookVector:Dot(unit) if perpendicular.Magnitude < 0.0001 then perpendicular = Vector3.xAxis - unit * unit:Dot(Vector3.xAxis) end end local unit2 = perpendicular.Unit:Cross(unit).Unit return { SurfacePosition = position, Right = unit2, Up = unit, Forward = unit:Cross(unit2).Unit } end local function oscillate(range, frequency) local n = 6.2831853071795862 * frequency return range.Min + (range.Max - range.Min) * (math.sin(os.clock() * n) + 1) * constantPool[101] end local function currentDepthUp() local projectileBreaker = ragebotConfig.Data.Ragebot.Evasion.ProjectileBreaker return oscillate(projectileBreaker.DepthUp, projectileBreaker.DepthUpFrequency) end local function currentDepthForward() local projectileBreaker = ragebotConfig.Data.Ragebot.Evasion.ProjectileBreaker return oscillate(projectileBreaker.DepthForward, projectileBreaker.DepthForwardFrequency) end local function surfaceOffsetCFrame(surface, up, forward) return CFrame.fromMatrix( surface.SurfacePosition - surface.Up * 0.01 - surface.Up * up + surface.Forward * forward, surface.Right, surface.Up, -surface.Forward ) end local function isBlocker(part) return part.Name == 'Barriers' or part:HasTag('OutOfBoundsPart') or part:HasTag('KillBrick') end local function touchesBlocker(position) for _, part in workspace:GetPartBoundsInBox(CFrame.new(position), vector, overlapParams), nil, nil do if isBlocker(part) then return true end end return false end local function findTopSurface(part) if part.Transparency == 1 then return nil end local size = part.Size if size.X * size.Y * size.Z < 64 then return nil end local n = size.Magnitude + 1000 for i = 1, constantPool[175] do local point, normal = surfaceNormal(part, part.Position + random:NextUnitVector() * n) if normal == nil or normal.Y < 0.98 then continue end return surfaceFrame(point, normal, part) end local point, normal = surfaceNormal(part, part.Position + Vector3.yAxis * n) if normal ~= nil and normal.Y >= 0.98 then return surfaceFrame(point, normal, part) end return nil end local function fallbackPoint(root) local projectileBreaker = ragebotConfig.Data.Ragebot.Evasion.ProjectileBreaker local fallbackBaseRadius = projectileBreaker.FallbackBaseRadius local n = fallbackBaseRadius + fallbackBaseRadius * projectileBreaker.FallbackRadiusRandomFactor local position = root.Position local randomFrame = randomPointAround( projectileBreaker.FallbackAnchorFromCharacter and position or Vector3.new(constantPool[186], position.Y, constantPool[186]), fallbackBaseRadius, n ) local axis = random:NextInteger(constantPool[63], 3) local position2 = randomFrame.Position local x = position2.X local y = position2.Y local z = position2.Z if axis == 1 then x = 1073741824 elseif axis == constantPool[56] then y = 1073741824 else z = 1073741824 end return randomFrame - position2 + Vector3.new(x, y, z) end local ProjectileBreakerTeleport = {} ProjectileBreakerTeleport.__index = ProjectileBreakerTeleport ProjectileBreakerTeleport.new = function(fighters, context) local trove = Trove.new('ragebot.ProjectileBreakerTeleport') local breaker = { _trove = trove, _fighters = fighters, _nextPositionCooldown = -constantPool[63], _poolEnvironmentId = nil, _pool = {}, _processedPartSet = {}, } setmetatable(breaker, ProjectileBreakerTeleport) trove:Add(context:ObserveContext('ragebot.ProjectileBreakerTeleport', function(contextInfo, contextTrove) breaker:_BindEnvironment(contextInfo.FighterState.EnvironmentId) contextTrove:Connect(contextInfo.FighterState.EnvironmentIdChanged, function(environmentId) breaker:_BindEnvironment(environmentId) end) end)) trove:Connect(context.ContextRemoved, function() breaker:_BindEnvironment(nil) end) return breaker end ProjectileBreakerTeleport.Destroy = function(self) self._trove:Destroy() end ProjectileBreakerTeleport._BindEnvironment = function(self, poolEnvironmentId) self._poolEnvironmentId = poolEnvironmentId self._pool = {} self._processedPartSet = {} end ProjectileBreakerTeleport.Compute = function(self, root) local nextPositionCooldown = self._nextPositionCooldown if os.clock() < nextPositionCooldown then local lastBreakSurface = self._lastBreakSurface if lastBreakSurface ~= nil then return surfaceOffsetCFrame(lastBreakSurface, currentDepthUp(), currentDepthForward()) end end if not self:_HasProjectileThreat() then self._lastBreakSurface = nil return fallbackPoint(root) end local surface = self:_BreakLine() if surface ~= nil then self._lastBreakSurface = surface return surfaceOffsetCFrame(surface, currentDepthUp(), currentDepthForward()) end return fallbackPoint(root) end ProjectileBreakerTeleport._HasProjectileThreat = function(self) for _, fighterState in self._fighters.EnemyByPlayer, nil, nil do local gun = fighterState.ItemObserver:EquippedItemAsGun() if gun ~= nil and not gun.IsRaycast and gun.Name == 'Slingshot' then return true end end return false end ProjectileBreakerTeleport._ScanBatch = function(self, environmentId) local projectileBreaker = ragebotConfig.Data.Ragebot.Evasion.ProjectileBreaker local upMid = (projectileBreaker.DepthUp.Min + projectileBreaker.DepthUp.Max) * constantPool[101] local forwardMid = (projectileBreaker.DepthForward.Min + projectileBreaker.DepthForward.Max) * 0.5 local scanned = constantPool[186] local function scanPart(part) if not part:IsA('BasePart') or self._processedPartSet[part] then return end self._processedPartSet[part] = true scanned += 1 local surface = findTopSurface(part) if surface == nil then return end if touchesBlocker(surfaceOffsetCFrame(surface, upMid, forwardMid).Position) then return end table.insert(self._pool, surface) end for _, root in collectionService:GetTagged('RaycastWhitelist' .. environmentId) do if isBlocker(root) then continue end scanPart(root) if scanned >= constantPool[83] or #self._pool >= 30 then return end for _, descendant in root:GetDescendants() do if isBlocker(descendant) then continue end scanPart(descendant) if scanned >= 64 or #self._pool >= constantPool[192] then return end end end end ProjectileBreakerTeleport._BreakLine = function(self) local poolEnvironmentId = self._poolEnvironmentId if poolEnvironmentId == nil then return nil end if #self._pool < 30 then self:_ScanBatch(poolEnvironmentId) end if #self._pool < 30 then return nil end local repositionInterval = ragebotConfig.Data.Ragebot.Evasion.ProjectileBreaker.RepositionInterval self._nextPositionCooldown = os.clock() + repositionInterval return self._pool[random:NextInteger(1, #self._pool)] end ProjectileBreakerTeleport.ResetState = function(self) self._nextPositionCooldown = -1 self._lastBreakSurface = nil end return ProjectileBreakerTeleport end modules.jH = function() local cached = modules.cache.jH if not cached then cached = { c = buildProjectileBreakerTeleport() } modules.cache.jH = cached end return cached.c end end do -- jI RandomEvasion local function buildRandomEvasion() local ragebotConfig = modules.bG() local randomPointAround = modules.jF() local random = Random.new() return { compute = function(root) local random2 = ragebotConfig.Data.Ragebot.Evasion.Random local baseRadius = random2.BaseRadius local n = baseRadius + baseRadius * random2.RadiusRandomFactor local position = root.Position local randomFrame = randomPointAround(random2.AnchorFromCharacter and position or Vector3.new(0, position.Y, 0), baseRadius, n) local axis = random:NextInteger(constantPool[63], 3) local position2 = randomFrame.Position local x = position2.X local y = position2.Y local z = position2.Z if axis == 1 then x = 1073741824 elseif axis == 2 then y = 1073741824 else z = 1073741824 end return randomFrame - position2 + Vector3.new(x, y, z) end, } end modules.jI = function() local cached = modules.cache.jI if not cached then cached = { c = buildRandomEvasion() } modules.cache.jI = cached end return cached.c end end do -- jJ SpatialLimitGate local function buildSpatialLimitGate() local ragebotConfig = modules.bG() modules.cF() modules.jB() local Trove = modules.k() local function isOutsideLimit(position) return math.abs(position.X) >= 4194304 or math.abs(position.Y) >= 4194304 or math.abs(position.Z) >= 4194304 end local SpatialLimitGate = {} SpatialLimitGate.__index = SpatialLimitGate SpatialLimitGate.new = function(fighters) local gate = { _trove = Trove.new('ragebot.SpatialLimitGate'), _measurementByFighterState = {} } setmetatable(gate, SpatialLimitGate) gate:_Initialize(fighters) return gate end SpatialLimitGate._Initialize = function(self, fighters) self._trove:Add( fighters:ObserveRemoteStates( function(fighterState) self._measurementByFighterState[fighterState] = { ExpectedDuration = 1 } end, function(fighterState) self._measurementByFighterState[fighterState] = nil end ) ) end SpatialLimitGate.Tick = function(self, target) local now2 = os.clock() local fighterState = target.FighterState local measurement = self._measurementByFighterState[fighterState] local limitEntryTime = measurement.LimitEntryTime local canShoot = fighterState.ItemObserver:GetEquippedAmmoState() ~= false if not isOutsideLimit(target.AliveState.RootPart.Position) then if limitEntryTime ~= nil then if canShoot then measurement.ExpectedDuration = now2 - limitEntryTime end measurement.LimitEntryTime = nil end return false end if limitEntryTime == nil then measurement.LimitEntryTime = now2 limitEntryTime = now2 end if canShoot then if measurement.ExpectedDuration - ragebotConfig.Data.Ragebot.Stability <= now2 - limitEntryTime then return false end end return constantPool[34] end SpatialLimitGate.Destroy = function(self) self._trove:Destroy() end return SpatialLimitGate end modules.jJ = function() local cached = modules.cache.jJ if not cached then cached = { c = buildSpatialLimitGate() } modules.cache.jJ = cached end return cached.c end end do -- jK TranslocateEvasion local function buildTranslocateEvasion() local ragebotConfig = modules.bG() local randomPointAround = modules.jF() local collectionService = cloneref(game:GetService('CollectionService')) return { compute = function(root, hasTargets) if not hasTargets then return randomPointAround(root.Position, 10000, 1e9) end local killPart = nil for _, part in collectionService:GetTagged('OutOfBoundsPart') do if part:GetAttribute('KillDelay') == 0 then killPart = part break else killPart = nil end end if killPart == nil then return randomPointAround(root.Position, 10000, 1e9) end return killPart.CFrame * CFrame.new(0, -killPart.Size.Y / 2 + ragebotConfig.Data.Ragebot.Evasion.Translocate.Offset, 0) end, } end modules.jK = function() local cached = modules.cache.jK if not cached then cached = { c = buildTranslocateEvasion() } modules.cache.jK = cached end return cached.c end end do -- jL RingField: ring cells and random points local function buildRingField() local jitterRange = 1121 local random = Random.new() local function randomUint32() return random:NextInteger(0, 65535) * 65536 + random:NextInteger(constantPool[186], 65535) end local seedA = randomUint32() local seedB = randomUint32() local seedC = randomUint32() local seedD = randomUint32() local function hash(a, b) local h1 = (bit32.bxor(a, b, seedA) + seedB) % 4294967296 local h2 = (bit32.bxor(h1, bit32.lrotate(h1, 7)) + seedC) % 4294967296 local h3 = (bit32.bxor(h2, bit32.rrotate(h2, 11)) + seedD) % 4294967296 return (bit32.bxor(h3, bit32.lrotate(h3, 17))) end local function makeRing(min, max) local yMin = min + 400 + constantPool[83] return { Min = min, Max = max, Cells = (max - min) / 2048, YMin = yMin, YSpan = max - 400 - 64 - yMin + 1, Salt = randomUint32(), } end local normalRing = makeRing(65536, 524288) local immuneRing = makeRing(134217728, 1073741824) local function cellHash(ring, cellX, cellZ, extra) local bxor = bit32.bxor local salt = ring.Salt return hash(bit32.bxor(cellX, bit32.lshift(cellZ, constantPool[118])), bxor(extra, salt)) end local function cellJitter(ring, cellX, cellZ, extra) return cellHash(ring, cellX, cellZ, extra) % jitterRange - 560 end local function cellCenter(ring, cellX, cellZ) local base = ring.Min + 1024 local ySpan = ring.YSpan return Vector3.new( base + cellX * 2048 + cellJitter(ring, cellX, cellZ, 324508639), ring.YMin + cellHash(ring, cellX, cellZ, 826366246) % ySpan, base + cellZ * 2048 + cellJitter(ring, cellX, cellZ, 610839776) ) end local function randomPointInRing(ring) local lastCell = ring.Cells - 1 local nextInteger = random.NextInteger local zero = constantPool[186] local center = cellCenter(ring, random:NextInteger(0, lastCell), nextInteger(random, zero, lastCell)) local radius = 200 * random:NextNumber() ^ 0.33333333333333331 local unitZ = random:NextNumber(-1, 1) local planar = math.sqrt(1 - unitZ * unitZ) local angle = random:NextNumber(0, 6.2831853071795862) return center + Vector3.new(radius * planar * math.cos(angle), radius * unitZ, radius * planar * math.sin(angle)) end local function isInRing(ring, position) local min = ring.Min local max = ring.Max local x = position.X local y = position.Y local z = position.Z if x <= min or x >= max or y <= min or y >= max or z <= min or z >= max then return false end local cellX = math.floor((x - min) / 2048) local cellZ = math.floor((z - min) / 2048) return (position - cellCenter(ring, cellX, cellZ)).Magnitude < 400 end local function randomOrientation(position) local cframe = CFrame.fromOrientation local nextNumber = random.NextNumber return CFrame.new(position) * cframe( random:NextNumber(-3.1415926535897931, 3.1415926535897931), random:NextNumber(-3.1415926535897931, 3.1415926535897931), nextNumber(random, -3.1415926535897931, 3.1415926535897931) ) end return { SphereRadius = 400, isInAnyRing = function(position) return isInRing(normalRing, position) or isInRing(immuneRing, position) end, isInNormalRing = function(position) return isInRing(normalRing, position) end, isInImmuneRing = function(position) if not integrityFlagB then return end return isInRing(immuneRing, position) end, getNormal = function() return randomOrientation(randomPointInRing(normalRing)) end, getImmune = function() return randomOrientation(randomPointInRing(immuneRing)) end, } end modules.jL = function() local cached = modules.cache.jL if not cached then cached = { c = buildRingField() } modules.cache.jL = cached end return cached.c end end do -- jM WeaponChooser: next weapon action local function buildWeaponChooser() local ragebotConfig = modules.bG() modules.cU() modules.cX() local function slotName(item) local slot = item.Index return slot == constantPool[63] and 'Primary' or slot == 2 and 'Secondary' or slot == 3 and 'Melee' or nil end local function isSlotEnabled(slot) return ragebotConfig.Data.Ragebot.Weapons.Enabled[slot] end return { getAction = function(context) local weapons = ragebotConfig.Data.Ragebot.Weapons local onEmpty = weapons.OnEmpty local huge = math.huge local huge2 = math.huge local anyEnabled = false local bestItem = nil local emptyGunItem = nil for _, item in context.ItemBehaviors:GetItems(), nil, nil do local slot = slotName(item) if not (slot == nil or not isSlotEnabled(slot)) then anyEnabled = constantPool[34] local huge3 = table.find(weapons.Priority, slot) or math.huge if item.__type == 'Gun' and item:GetAmmo() == 0 then if not (item:GetAmmoReserve() <= 0) then if huge3 < huge2 then if onEmpty == 'Reload' then bestItem = item huge = huge3 else huge2 = huge3 emptyGunItem = item end end end elseif bestItem == nil or huge3 < huge then bestItem = item huge = huge3 end end end if not anyEnabled then return nil end if bestItem ~= nil then local needsReload = bestItem.__type == 'Gun' if needsReload then local zero = constantPool[186] needsReload = bestItem:GetAmmo() == zero end if bestItem:IsEquipped() then if needsReload then return { Type = 'Reload', Item = bestItem } end return { Type = 'Attack', Item = bestItem } end return { Type = 'Swap', Item = bestItem } end if onEmpty == 'Swap' then return nil end if emptyGunItem ~= nil then if emptyGunItem:IsEquipped() then return { Type = 'Reload', Item = emptyGunItem } end return { Type = 'Swap', Item = emptyGunItem } end return nil end, } end modules.jM = function() local cached = modules.cache.jM if not cached then cached = { c = buildWeaponChooser() } modules.cache.jM = cached end return cached.c end end do -- jN getShieldState local function buildGetShieldState() modules.cU() modules.jx() return function(itemBehaviors) local shield = itemBehaviors:FindMeleeByName('Riot Shield') if shield == nil then return 'None' end return shield:IsEquipped() and 'Equipped' or 'Unequipped' end end modules.jN = function() local cached = modules.cache.jN if not cached then cached = { c = buildGetShieldState() } modules.cache.jN = cached end return cached.c end end do -- jO RagebotController: update loop, plans, reload transport local function buildRagebotController() modules.cJ() local ragebotConfig = modules.bG() local defensivePose = modules.jz() modules.cF() local GunPlanner = modules.jD() modules.cU() local KeybindBridge = modules.c4() local MeleePlanner = modules.jE() modules.gA() modules.cX() modules.bM() local ProjectileBreakerTeleport = modules.jH() local RandomEvasion = modules.jI() local ServerOrigin = modules.cG() local SpatialLimitGate = modules.jJ() modules.g_() local TargetSelector = modules.jB() local TranslocateEvasion = modules.jK() local Trove = modules.k() local RingField = modules.jL() local WeaponChooser = modules.jM() local getShieldState = modules.jN() local fallenPartsDestroyHeight = workspace.FallenPartsDestroyHeight local function idleImmunePlan() return { CFrame = RingField.getImmune(), ShouldSkipDefense = true } end local RagebotController = {} RagebotController.__index = RagebotController RagebotController.new = function(fighters, playerTags, fighterContext, stateHook, partGlue) local ragebot = Trove.new('ragebot') local spatialGate = ragebot:Add(SpatialLimitGate.new(fighters)) local controller = { _trove = ragebot, _enabled = false, _lastTargetWorld = nil, _lastDefensiveViewAngles = nil, _playerContext = fighterContext, _targetSelection = TargetSelector.new(fighters, playerTags), _spatialLimitGate = spatialGate, _hitscanStrategy = GunPlanner.new(partGlue), _meleeStrategy = MeleePlanner.new(partGlue), _projectileBreakerTeleport = ragebot:Add(ProjectileBreakerTeleport.new(fighters, fighterContext)), _stateHook = stateHook, _reloadGun = nil, _reloadReadyAt = nil, _reloadAcknowledgementDeadline = nil, _pendingDepletionAmmo = nil, } setmetatable(controller, RagebotController) controller:_Initialize() return controller end RagebotController._Initialize = function(self) self._trove:Add(self._playerContext:ObserveContext('ragebot', function(innerContext) if not integrityFlagA then return end self._innerContext = innerContext self:_ClearReloadTransport() end)) self._trove:Connect(self._playerContext.ContextRemoved, function() self:_Reset() self._innerContext = nil end) self._trove:Add(KeybindBridge:ObserveEnabledKeybind({ 'Ragebot' }, function(enabled) self:SetEnabled(enabled) self:_Reset() end)) end RagebotController.SetEnabled = function(self, enabled) if self._enabled == enabled then return end self._enabled = enabled gameNewIndex(workspace, 'FallenPartsDestroyHeight', enabled and (0 / 0) or fallenPartsDestroyHeight) protectedCall(setFFlag, 'DFIntS2PhysicsSenderRate', enabled and '120' or '15') protectedCall(setFFlag, 'DFIntAssemblyHistoryBufferSize', enabled and '2147483648' or '15') protectedCall(setFFlag, 'DFIntAssemblyHistorySkipSize', enabled and constantPool[18] or '8') if not enabled then self:_ClearReloadTransport() end end RagebotController.Update = function(self, deltaTime) local innerContext = self._innerContext if innerContext == nil then self:_Reset() return end local fighterState = innerContext.FighterState if fighterState.EnvironmentId == nil or not self._enabled then self:_Reset() return end local state = fighterState.Character.State if not state.Alive then self:_Reset() return end local characterController = innerContext.CharacterController local clientCFrame = characterController:GetClientCFrame() local mode = ragebotConfig.Data.Ragebot.Evasion.Mode local action = WeaponChooser.getAction(innerContext) if mode == 'Translocate' and self._reloadGun == nil and (action == nil or action.Type ~= 'Reload') then self:_ApplyForcedCrouch(constantPool[153]) characterController:SetServerCFrame(TranslocateEvasion.compute(clientCFrame, self._targetSelection:HasTargets())) return end local target = self._targetSelection:GetTarget() if target ~= nil then self._lastTargetWorld = target.AliveState.RootPart.Position else self._lastTargetWorld = nil end local plan = self:_Plan(deltaTime, action, target, state.RootPart, clientCFrame, mode) self:_ApplyPlan(plan, target, innerContext) local reloadGun = self._reloadGun if reloadGun ~= nil and self._reloadReadyAt == nil and self._pendingDepletionAmmo == nil and not reloadGun:IsReloading() then self._reloadReadyAt = os.clock() + ServerOrigin.getEstimatedReplicationDelay() end self:_ApplyForcedCrouch(plan.ShouldForceCrouch == true) local gun = innerContext.ItemBehaviors:EquippedItemAsGun() local ammo = gun ~= nil and gun:GetAmmo() or 0 local shotRequestCount = gun ~= nil and gun.ShotRequestCount or 0 local weaponAction = plan.WeaponAction if weaponAction ~= nil then weaponAction() if gun ~= nil and (gun ~= nil and gun.ShotRequestCount - shotRequestCount or 0) > 0 and gun:GetExpectedAmmoAfterPendingShots() <= 0 then innerContext.CharacterController:SetServerCFrame(RingField.getImmune()) local now2 = os.clock() self._reloadGun = gun self._reloadReadyAt = now2 + ServerOrigin.getEstimatedReplicationDelay() self._pendingDepletionAmmo = ammo self._reloadAcknowledgementDeadline = now2 + ServerOrigin.getEstimatedRemoteDelay() + ServerOrigin.getEstimatedReplicationDelay() end end end RagebotController._Plan = function(self, deltaTime, action, target, ourPart, clientCFrame, mode) local canFire = target ~= nil and not self._spatialLimitGate:Tick(target) local transportPlan = self:_PlanReloadTransport(action) if transportPlan ~= nil then return transportPlan end if action == nil then return self:_EvadePlan(clientCFrame, mode) end if action.Type == 'Swap' then local item = action.Item local plan = self:_EvadePlan(clientCFrame, mode) plan.WeaponAction = function() item:Equip() end return plan end if action.Type == 'Reload' then return idleImmunePlan() end if target == nil then return self:_EvadePlan(clientCFrame, mode) end local item = action.Item if item.__type == 'Gun' then if item:IsReloading() then return idleImmunePlan() end local cframe, weaponAction = self._hitscanStrategy:Plan(deltaTime, target, item, ourPart, canFire) return { CFrame = cframe, WeaponAction = weaponAction, ShouldForceCrouch = true, IsAimPose = weaponAction ~= nil } end if item.__type == 'Melee' then local cframe, viewAngles, weaponAction = self._meleeStrategy:Plan(deltaTime, target, item, ourPart, canFire) return { CFrame = cframe, ViewAngles = viewAngles, WeaponAction = weaponAction, ShouldSkipDefense = constantPool[34], ShouldForceCrouch = true, } end return {} end RagebotController._ClearReloadTransport = function(self) self._reloadGun = nil self._reloadReadyAt = nil self._reloadAcknowledgementDeadline = nil self._pendingDepletionAmmo = nil end RagebotController._PlanReloadTransport = function(self, action) local reloadGun = self._reloadGun local now2 = os.clock() if reloadGun ~= nil then if (self._innerContext ~= nil and self._innerContext.ItemBehaviors:EquippedItemAsGun() or nil) ~= reloadGun or action ~= nil and (action.Type == 'Swap' or action.Type == 'Attack') and action.Item ~= reloadGun then self:_ClearReloadTransport() return nil end if reloadGun:IsReloading() then self._reloadReadyAt = nil self._pendingDepletionAmmo = nil self._reloadAcknowledgementDeadline = nil return idleImmunePlan() end local ammo = reloadGun:GetAmmo() if self._pendingDepletionAmmo ~= nil then local reloadAcknowledgementDeadline = self._reloadAcknowledgementDeadline if not (ammo <= 0 and reloadGun:GetAmmoReserve() > 0) then local expired = reloadAcknowledgementDeadline == nil or now2 >= reloadAcknowledgementDeadline if not expired then local zero = constantPool[186] expired = reloadGun:GetExpectedAmmoAfterPendingShots() > zero end if expired then self:_ClearReloadTransport() return nil end return idleImmunePlan() end self._pendingDepletionAmmo = nil self._reloadAcknowledgementDeadline = nil end if ammo > 0 or reloadGun:GetAmmoReserve() <= 0 then self:_ClearReloadTransport() return nil end local reloadReadyAt = self._reloadReadyAt if reloadReadyAt == nil then reloadReadyAt = now2 + ServerOrigin.getEstimatedReplicationDelay() self._reloadReadyAt = reloadReadyAt end local plan = idleImmunePlan() local reloadAcknowledgementDeadline = self._reloadAcknowledgementDeadline if now2 >= reloadReadyAt and (reloadAcknowledgementDeadline == nil or now2 >= reloadAcknowledgementDeadline) then plan.WeaponAction = function() if reloadGun:Reload() then self._reloadAcknowledgementDeadline = os.clock() + ServerOrigin.getEstimatedRemoteDelay() + ServerOrigin.getEstimatedReplicationDelay() end end end return plan end if action == nil or action.Type ~= 'Reload' then return nil end self._reloadGun = action.Item self._reloadReadyAt = nil self._reloadAcknowledgementDeadline = nil self._pendingDepletionAmmo = nil return idleImmunePlan() end RagebotController._EvadePlan = function(self, clientCFrame, mode) if mode == 'Off' then return {} end if mode == 'ProjectileBreaker' then return { CFrame = self._projectileBreakerTeleport:Compute(clientCFrame), ShouldSkipDefense = constantPool[34] } end return { CFrame = RandomEvasion.compute(clientCFrame) } end RagebotController._ApplyPlan = function(self, plan, target, innerContext) local characterController = innerContext.CharacterController local cFrame = plan.CFrame if cFrame == nil or target == nil or plan.ShouldSkipDefense then characterController:SetServerCFrame(cFrame) characterController:SendViewAngles(20, plan.ViewAngles) return end local aliveState = target.AliveState local shieldState = getShieldState(innerContext.ItemBehaviors) local defensiveCFrame = defensivePose.getDefensiveCFrame(cFrame, shieldState, target.FighterState, aliveState.RootPart) characterController:SetServerCFrame(defensiveCFrame) if plan.IsAimPose or plan.ShouldDefendInPlace then self._lastDefensiveViewAngles = defensivePose.getDefensiveViewAngles(shieldState, target.FighterState) end characterController:SendViewAngles(constantPool[9], plan.ViewAngles or self._lastDefensiveViewAngles) end RagebotController.GetLastTargetWorld = function(self) return self._lastTargetWorld end RagebotController._ApplyForcedCrouch = function(self, forced) if forced then self._stateHook:SetForced('IsCrouching', true) else self._stateHook:ClearForced('IsCrouching') end end RagebotController._Reset = function(self) self:_ClearReloadTransport() self._lastTargetWorld = nil self._lastDefensiveViewAngles = nil self:_ApplyForcedCrouch(false) self._meleeStrategy:ResetState() self._projectileBreakerTeleport:ResetState() local innerContext = self._innerContext if innerContext == nil then return end innerContext.CharacterController:SetServerCFrame(nil) innerContext.CharacterController:SendViewAngles(20, nil) end RagebotController.Destroy = function(self) self._trove:Destroy() end return RagebotController end modules.jO = function() local cached = modules.cache.jO if not cached then cached = { c = buildRagebotController() } modules.cache.jO = cached end return cached.c end end modules.aL = function() return nil end modules.aM = function() return nil end modules.aN = function() return nil end modules.aO = function() return nil end modules.aP = function() return nil end modules.aQ = function() return nil end modules.jx = function() return nil end --========================================================================== -- Standalone bootstrap. Mirrors what the hub's main (j1) does for the ragebot: -- fighters -> player context -> state hook / part glue -> ragebot, ticked on -- Heartbeat in the same order (context heartbeat, fighters, ragebot, view angles). --========================================================================== local GlobalTrove = modules.n() modules.b().use({ Report = function(_, e) local detail = type(e) == 'table' and (e.Detail or e.Operation) or e warn('[Zkxhub Ragebot] ' .. tostring(detail)) end, }) local RunService = game:GetService('RunService') local Rage = Zkxhub Rage.IsReady = false Rage.Status = 'Starting' local function path(p) if type(p) == 'string' then local out = {} for part in string.gmatch(p, '[^%.]+') do out[#out + 1] = part end p = out end return p end function Rage.Get(p) return modules.bG():Get(path(p)) end function Rage.Set(p, v) modules.bG():Set(path(p), v) end function Rage.SetEnabled(on) Rage.Set({ 'Ragebot', 'Keybind', 'Kind' }, 'Always') Rage.Set({ 'Ragebot', 'Enabled' }, on and true or false) end function Rage.SetBind(key, kind) Rage.Set({ 'Ragebot', 'Keybind', 'Kind' }, kind or 'Toggle') Rage.Set({ 'Ragebot', 'Keybind', 'Bind' }, key) end Rage.Config = modules.bG().Data.Ragebot local function boot() local registry = GlobalTrove:Add(modules.bK().new()) local tags = GlobalTrove:Add(modules.bM().new(registry)) local fighters = GlobalTrove:Add(modules.cF().new()) local context = GlobalTrove:Add(modules.cX().new(fighters)) local stateHook = GlobalTrove:Add(modules.g_().new()) local partGlue = GlobalTrove:Add(modules.gA().new()) local ragebot = GlobalTrove:Add(modules.jO().new(fighters, tags, context, stateHook, partGlue)) Rage.Tags = tags -- the hub's hacker detector (module gg), minus its notification: feeds Ragebot.PrioritizeHackers local speedTimer, tagged = {}, {} local function detect(dt) for _, state in fighters.StateByPlayer do if tagged[state] then continue end local alive = state.Character.State local hacking = state:GetCameraRotation().X > math.tau if not hacking and alive.Alive and alive.Velocity.Magnitude > 200 then speedTimer[state] = (speedTimer[state] or 0) + dt hacking = speedTimer[state] > 0.25 else speedTimer[state] = 0 end if hacking then tagged[state] = true tags:Add(state.Player, 'Hacker') end end end GlobalTrove:Add(fighters.RemoteStateRemoved:Connect(function(state) tagged[state] = nil speedTimer[state] = nil end)) GlobalTrove:Add(RunService.Heartbeat:Connect(function(dt) context:HeartbeatUpdate() fighters:Update(dt) detect(dt) ragebot:Update(dt) context:FlushViewAngles() end)) Rage.IsReady = true Rage.Status = 'Ready' end function Zkxhub.Unload() if Zkxhub.destroyed then return end Zkxhub.destroyed = true pcall(function() Rage.SetEnabled(false) end) for _, f in ipairs(Zkxhub.cleanups) do pcall(f) end pcall(function() GlobalTrove:Destroy() end) if getgenv().ZkxhubRagebot == Zkxhub then getgenv().ZkxhubRagebot = nil end end function Zkxhub.Start() if Zkxhub.started or Zkxhub.destroyed then return end Zkxhub.started = true task.spawn(function() local lp = game:GetService('Players').LocalPlayer local ps = lp and lp:FindFirstChild('PlayerScripts') if lp and not ps then pcall(function() lp:WaitForChild('PlayerScripts', 30) end) ps = lp:FindFirstChild('PlayerScripts') end local ctrl = ps and ps:FindFirstChild('Controllers') if ps and not ctrl then Rage.Status = 'Waiting for PlayerScripts.Controllers' print('[Zkxhub Ragebot] waiting for PlayerScripts.Controllers ...') pcall(function() ps:WaitForChild('Controllers', 30) end) ctrl = ps:FindFirstChild('Controllers') end if not ctrl then Rage.Status = 'Aborted: PlayerScripts.Controllers not found (Rivals only)' warn('[Zkxhub Ragebot] ABORTED: PlayerScripts.Controllers not found. This is built for Rivals.') return end Rage.Status = 'Waiting for the game to finish loading' task.wait(3) -- game controller modules error when required before the game finished replicating them: -- require the heaviest one until it loads cleanly (a successful require is cached) local pdc = ctrl:FindFirstChild('PlayerDataController') local canary = ctrl:FindFirstChild('FighterController') local ready, lastErr = false, '?' if pdc and canary then for i = 1, 90 do local ok, res = pcall(require, pdc) if ok then if type(res) == 'table' and res.CurrentData ~= nil then local ok2, err2 = pcall(require, canary) if ok2 then ready = true break end lastErr = tostring(err2):sub(1, 300) else lastErr = 'CurrentData=' .. tostring(res and res.CurrentData) end else lastErr = tostring(res):sub(1, 300) end if i == 1 or i % 5 == 0 then print('[Zkxhub Ragebot] not ready (' .. i .. '/90): ' .. lastErr) end task.wait(2) end else lastErr = 'PlayerDataController or FighterController missing under Controllers' end if not ready then Rage.Status = 'Aborted: FighterController never initialized' warn('[Zkxhub Ragebot] ABORTED: FighterController never initialized. Last error: ' .. tostring(lastErr)) return end if Zkxhub.destroyed then return end local ok, err = pcall(boot) if not ok then Rage.Status = 'Boot failed: ' .. tostring(err):sub(1, 120) warn('[Zkxhub Ragebot] boot failed: ' .. tostring(err)) end end) end -- a host script can set getgenv().ZkxhubRagebotManualStart = true and call Rage.Start() when it wants the ragebot if getgenv().ZkxhubRagebotManualStart == true then Rage.Status = 'Not started (starts when the ragebot is first enabled)' else Zkxhub.Start() end return Zkxhub end -- ====================================================================== -- Ranked charm spoofer -- ====================================================================== __ZkhHub_shared.rank_charm_spoofer = function() -- Ranked Charm Spoofer. -- Every "Season N" charm packs one model per rank in its Extra folder. When a charm is built, the game reads -- EquippedData.Metadata.SeasonELO / SeasonLeaderboardRank, asks SeasonLibrary:FormatSeasonRankCharm for the rank -- that matches, and deletes every other model. Changing that metadata (and optionally the charm name) before the -- charm is built changes which rank model you see. It only changes what this client draws for your own weapons. -- in match : ClientViewModel:Get('Charm') feeds Charm.new(viewModel, data, attachment) -- menus : StaticViewModel:SetCharm(data) from the equipment preview (UI.Equipment.FloatingModel) local Players = cloneref(game:GetService('Players')) local ReplicatedStorage = cloneref(game:GetService('ReplicatedStorage')) local LP = Players.LocalPlayer local module = {} local state = { Enabled = false, CharmName = nil, -- nil = keep the equipped charm (only Season charms change rank) Rank = 'Archnemesis', Leaderboard = 1, ForceAll = false, Classes = nil, Catalog = nil, Live = setmetatable({}, { __mode = 'k' }), GetHooked = false, SetCharmHooked = false, OriginalGet = nil, OwnedGet = false, OriginalSetCharm = nil, RefreshPending = false, } module.Status = 'Not started' local function resolveClasses() if state.Classes then return state.Classes end local playerScripts = LP:FindFirstChild('PlayerScripts') local modules = playerScripts and playerScripts:FindFirstChild('Modules') local assets = playerScripts and playerScripts:FindFirstChild('Assets') if not (modules and assets) then return nil end local ok, classes = pcall( function() return { Charm = require(modules.Charm), ClientViewModel = require(modules.ClientReplicatedClasses.ClientFighter.ClientItem.ClientViewModel), StaticViewModel = require(modules.StaticModel.StaticViewModel), CharmModules = modules.Charms, CharmAssets = assets.Charms, SeasonLibrary = require(ReplicatedStorage.Modules.SeasonLibrary), } end ) if ok then state.Classes = classes end return state.Classes end function module.GetCatalog() if state.Catalog then return state.Catalog end local classes = resolveClasses() if not classes then return nil end local library = classes.SeasonLibrary local season = library.CurrentSeason local profile = type(season) == 'table' and season.RankProfile or nil if not (type(profile) == 'table' and type(profile.RanksOrder) == 'table' and type(profile.Ranks) == 'table') then return nil end local rankNames, ranks = {}, {} for _, rankName in ipairs(profile.RanksOrder) do local data = profile.Ranks[rankName] if type(data) == 'table' then rankNames[#rankNames + 1] = rankName ranks[rankName] = { Name = rankName, ELO = data.RequiredELO, BoardCap = data.RequiredELOLeaderboardRanking, } end end local versions = {} for version in pairs(library.SeasonsByVersion) do versions[#versions + 1] = version end table.sort(versions) local labels, charmByLabel = {}, {} for _, version in ipairs(versions) do local charmName = 'Season ' .. tostring(version) local seasonData = library.SeasonsByVersion[version] if type(seasonData) == 'table' and classes.CharmAssets:FindFirstChild(charmName) then local label = string.format('Season %s (%s)', tostring(version), tostring(seasonData.Name)) labels[#labels + 1] = label charmByLabel[label] = charmName end end state.Catalog = { RankNames = rankNames, Ranks = ranks, SeasonLabels = labels, CharmByLabel = charmByLabel } return state.Catalog end local function isSeasonCharm(name) return type(name) == 'string' and name:match('^Season %d+$') ~= nil end local function buildMetadata() local catalog = module.GetCatalog() local rank = catalog and catalog.Ranks[state.Rank] if not rank then return nil end if type(rank.ELO) ~= 'number' or rank.ELO < 0 then return { SeasonELO = nil, SeasonLeaderboardRank = nil } -- Unranked end local board = nil if rank.BoardCap ~= nil then board = math.clamp(math.floor(tonumber(state.Leaderboard) or 1), 1, rank.BoardCap) end return { SeasonELO = rank.ELO, SeasonLeaderboardRank = board } end local function latestCharmName() local catalog = module.GetCatalog() local labels = catalog and catalog.SeasonLabels local last = labels and labels[#labels] return last and catalog.CharmByLabel[last] or nil end -- data: the weapon's charm payload ({Name, Metadata}) or nil when nothing is equipped. Returns a new payload, or nil to leave it. local function overridePayload(data) local metadata = buildMetadata() if not metadata then return nil end local current = type(data) == 'table' and data.Name or nil local target = state.CharmName if target == nil then if isSeasonCharm(current) then target = current elseif current == nil and state.ForceAll then target = latestCharmName() end elseif current == nil and not state.ForceAll then return nil end if target == nil then return nil end local payload = type(data) == 'table' and table.clone(data) or {} local merged = type(payload.Metadata) == 'table' and table.clone(payload.Metadata) or {} merged.SeasonELO = metadata.SeasonELO merged.SeasonLeaderboardRank = metadata.SeasonLeaderboardRank payload.Name = target payload.Metadata = merged return payload end local function ownsViewModel(viewModel) local ok, owner = pcall(function() local item = viewModel.ClientItem local fighter = item and item.ClientFighter return fighter and fighter.Player end) return ok and owner == LP end local function installGet(classes) if state.GetHooked then return true end local class = classes.ClientViewModel local original = class.Get if type(original) ~= 'function' then return false end state.OwnedGet = rawget(class, 'Get') ~= nil state.OriginalGet = original rawset(class, 'Get', function(self, key, ...) local value = original(self, key, ...) if key == 'Charm' then local ok, patched = pcall(function() if ownsViewModel(self) then state.Live[self] = true if state.Enabled then return overridePayload(value) end end return nil end) if ok and patched ~= nil then return patched end end return value end) state.GetHooked = true return true end local function installSetCharm(classes) if state.SetCharmHooked then return true end local class = classes.StaticViewModel local original = rawget(class, 'SetCharm') if type(original) ~= 'function' then return false end state.OriginalSetCharm = original rawset(class, 'SetCharm', function(self, payload) if state.Enabled then local ok, patched = pcall(function() for level = 1, 12 do local source = debug.info(level, 's') if type(source) == 'string' and source:find('FloatingModel', 1, true) then return overridePayload(payload) end end return nil end) if ok and patched ~= nil then payload = patched end end return original(self, payload) end) state.SetCharmHooked = true return true end function module.Init() local classes = resolveClasses() if not classes then module.Status = 'Waiting for the game to load' return false end local okGet = installGet(classes) local okSet = installSetCharm(classes) if okGet and okSet then module.Status = state.Enabled and 'Active' or 'Ready' return true end module.Status = 'Hook failed (' .. (okGet and '' or 'weapon ') .. (okSet and '' or 'preview') .. ')' return false end local function rebuildCharm(viewModel, classes) local attachment = viewModel._charm_pivot_attachment if not (attachment and attachment.Parent) then return false end local data = viewModel:Get('Charm') if type(data) ~= 'table' or type(data.Name) ~= 'string' then return false end local old = viewModel.Charm if old then pcall(function() old:Destroy() end) end local subclass = classes.CharmModules:FindFirstChild(data.Name, true) local class = subclass and require(subclass) or classes.Charm local charm = class.new(viewModel, data, attachment) viewModel.Charm = charm charm:SetParent(viewModel.Model) return true end -- Rebuilds the charm of every first-person / world viewmodel of yours that is already on screen. function module.Refresh() local classes = state.Classes if not classes then return 0 end local count = 0 for viewModel in pairs(state.Live) do local alive = false pcall(function() alive = viewModel._destroyed ~= true and viewModel.Model ~= nil and viewModel.Model.Parent ~= nil end) if alive then count += 1 task.spawn(function() pcall(rebuildCharm, viewModel, classes) end) else state.Live[viewModel] = nil end end return count end function module.QueueRefresh() if state.RefreshPending then return end state.RefreshPending = true task.delay(0.2, function() state.RefreshPending = false module.Refresh() end) end -- options: Enabled (bool), CharmLabel (string|nil), Rank (string), Leaderboard (number), ForceAll (bool) function module.Configure(options) local catalog = module.GetCatalog() local changed = false local function set(key, value) if state[key] ~= value then state[key] = value changed = true end end if options.Enabled ~= nil then set('Enabled', options.Enabled == true) end if options.CharmLabel ~= nil then local charmName = nil if options.CharmLabel ~= 'Keep equipped' then charmName = catalog and catalog.CharmByLabel[options.CharmLabel] or nil if charmName == nil then local version = tostring(options.CharmLabel):match('^Season (%d+)') charmName = version and ('Season ' .. version) or nil end end set('CharmName', charmName) end if type(options.Rank) == 'string' then set('Rank', options.Rank) end if options.Leaderboard ~= nil then set('Leaderboard', tonumber(options.Leaderboard) or 1) end if options.ForceAll ~= nil then set('ForceAll', options.ForceAll == true) end if state.Enabled and not (state.GetHooked and state.SetCharmHooked) then module.Init() end if module.Status == 'Ready' or module.Status == 'Active' then module.Status = state.Enabled and 'Active' or 'Ready' end if changed then module.QueueRefresh() end return changed end -- test seam: lets the offline tests hand in fake game classes function module._UseClassesForTest(classes) state.Classes = classes state.Catalog = nil end function module.Unload() state.Enabled = false local classes = state.Classes if classes and state.GetHooked then rawset(classes.ClientViewModel, 'Get', state.OwnedGet and state.OriginalGet or nil) state.GetHooked = false end if classes and state.SetCharmHooked then rawset(classes.StaticViewModel, 'SetCharm', state.OriginalSetCharm) state.SetCharmHooked = false end module.Refresh() module.Status = 'Unloaded' end return module end -- ====================================================================== -- UI kit (ZkxUIKit): polish for Obsidian windows -- ====================================================================== __ZkhHub_shared.zkx_ui_kit = function() --[[ ZkxUIKit - UI polish for Obsidian based scripts (works on the stock library and on the ZkxHub build) local Kit = loadstring(readfile("ZkxUIKit.lua"))() -- after Library:CreateWindow(...) (and ThemeManager:ApplyToTab(...) if you use it): local ui = Kit.Apply(Library, Window, ThemeManager, { Title = "MyHub", -- optional: replaces the window title text Logo = "rbxassetid://123456789", -- optional: only used by the splash Accent = Color3.fromRGB(125, 85, 255), }) ui.ThemeFade.Set(false) -- the pieces can be switched off again (ui.Glow / ui.Blur exist when you asked for them) -- a loading splash for the part of your script that takes time (just the logo, a thin bar and the word Loading): local splash = Kit.Splash({ Logo = "rbxassetid://123456789" }) -- Text = "Loading" by default splash.Progress(0.4) splash.Done() -- starts the fade out splash.Wait() -- returns once it is gone: do this before you show your main window What Apply does: logo + title at the left of the sidebar header (title in Heavy Italic), the hub font (Gotham Heavy) as the default entry of the Font Face list, Obsidian's own transitions switched on (tab swipe, section collapse, dropdowns, keybind pickers) and theme colour changes that blend instead of snapping. Opt in with Apply(..., { Glow = {}, Blur = {} }): a soft accent glow behind the window, a blur behind the open window (both cost fps, off by default). ]] local Kit = { Version = 1 } Kit._services = nil -- tests may replace services here local function svc(name) if Kit._services and Kit._services[name] then return Kit._services[name] end return game:GetService(name) end local function uiParent() if type(gethui) == 'function' then local ok, holder = pcall(gethui) if ok and holder then return holder end end return svc('CoreGui') end local function tween(inst, seconds, props, style, direction) local ok, tw = pcall( function() return svc('TweenService'):Create( inst, TweenInfo.new(seconds, style or Enum.EasingStyle.Quad, direction or Enum.EasingDirection.Out), props ) end ) if ok and tw then tw:Play() return tw end for key, value in pairs(props) do pcall(function() inst[key] = value end) end return nil end local function elevate() if type(setthreadidentity) == 'function' then pcall(setthreadidentity, 8) end end -- ------------------------------------------------------------------ fonts -- Kit.Font('Heavy', false) -> Gotham Heavy, Kit.Font('Heavy', true) -> Gotham Heavy Italic function Kit.Font(weight, italic) return Font.new( 'rbxasset://fonts/families/GothamSSm.json', Enum.FontWeight[weight or 'Heavy'], italic and Enum.FontStyle.Italic or Enum.FontStyle.Normal ) end -- ------------------------------------------------------------------ finding obsidian's parts function Kit.FindMain(Library) local gui = Library and Library.ScreenGui local main = gui and gui:FindFirstChild('Main') return main, gui end -- logo + title row of the sidebar header (the ZkxHub bundle hands them out as Window.TitleHolder / TitleLabel / IconLabel, stock obsidian does not) local function findTitleRow(Window, Library) if Window and Window.TitleHolder then return Window.TitleHolder, Window.TitleLabel, Window.IconLabel end local main = Kit.FindMain(Library) if not main then return nil end for _, bar in ipairs(main:GetChildren()) do if bar:IsA('Frame') and bar.Size.Y.Offset == 48 then for _, holder in ipairs(bar:GetChildren()) do if holder:IsA('Frame') and holder:FindFirstChildOfClass('UIListLayout') then local label, icon for _, item in ipairs(holder:GetChildren()) do if item:IsA('TextLabel') and item.Text ~= '' then label = item elseif item:IsA('ImageLabel') then icon = item end end if label then return holder, label, icon end end end end end return nil end -- ------------------------------------------------------------------ title row function Kit.TitleRow(Window, o) o = o or {} elevate() local holder, label, icon = findTitleRow(Window, o.Library) if not holder then return nil end local layout = holder:FindFirstChildOfClass('UIListLayout') if layout then layout.HorizontalAlignment = Enum.HorizontalAlignment.Left end local pad = Instance.new('UIPadding') pad.PaddingLeft = UDim.new(0, o.Padding or 14) pad.Parent = holder if icon and o.IconSize then icon.Size = o.IconSize end if label then if o.Title then label.Text = o.Title end local font = o.TitleFont or Kit.Font('Heavy', true) label.FontFace = font label.AutomaticSize = Enum.AutomaticSize.X label.Size = UDim2.new(0, 0, 1, 0) if o.TitleSize then label.TextSize = o.TitleSize end -- obsidian re-applies its theme font through the registry: keep the title's own face local registry = o.Library and o.Library.Registry and o.Library.Registry[label] if registry then registry.FontFace = function() return font end end end return { Holder = holder, Label = label, Icon = icon } end -- ------------------------------------------------------------------ obsidian's own transitions -- ToggleWindow is off: stock obsidian fades thousands of instances on every open/close (a lot of fps); the ZkxHub bundle has a cheap fade-out only Kit.DefaultAnimations = { ToggleWindow = false, TabSwitch = true, Groupbox = true, Dropdown = true, KeyPicker = true } function Kit.Animations(Window, o) o = o or {} if Window and type(Window.SetAnimations) == 'function' then Window:SetAnimations( o.Animations or Kit.DefaultAnimations, o.TabTransitionTime or 0.24, o.TabSwipeOffset or 22, o.TabSwipeFrom or 'bottom' ) return true end return false end -- ------------------------------------------------------------------ the hub font as an entry of the Font Face list (stock obsidian) -- the ZkxHub bundle already has this built in (ThemeManager.ResolveFont); for stock obsidian it is done at runtime function Kit.ApplyFont(Library, ThemeManager, font, name) name = name or 'Gotham Heavy' font = font or Kit.Font('Heavy', false) Library:SetFont(font) if ThemeManager and ThemeManager.ResolveFont then return end local fontOption = Library.Options and Library.Options.FontFace if fontOption then local values = table.clone(fontOption.Values or {}) if not table.find(values, name) then table.insert(values, 1, name) end pcall(fontOption.SetValues, fontOption, values) fontOption:OnChanged(function(value) if value == name then Library:SetFont(font) else local ok, enumFont = pcall(function() return Enum.Font[value] end) if ok and enumFont then Library:SetFont(enumFont) end end end) pcall(fontOption.SetValue, fontOption, name) end -- applying a theme puts the theme's own font back: take ours again if ThemeManager and not ThemeManager.__ZkxFontPinned then ThemeManager.__ZkxFontPinned = true local original = ThemeManager.ApplyTheme if type(original) == 'function' then ThemeManager.ApplyTheme = function(self, ...) local ok, err = original(self, ...) task.defer(function() if fontOption then pcall(fontOption.SetValue, fontOption, name) end Library:SetFont(font) end) return ok, err end end end end -- ------------------------------------------------------------------ loading splash -- Minimal on purpose: the game behind it blurred and dimmed, the logo (with one gentle glint sweeping over it now and then), a thin progress line -- and the word "Loading". -- Kit.Splash({ Logo, Accent, Text, Blur = 24, Dim = 0.4 }) -> { Progress(fraction), Done(), Wait(seconds), Closed } -- Done() fades the splash out; Wait() returns once it is gone (call both before showing your main window). function Kit.Splash(o) o = o or {} elevate() local accent = o.Accent or Color3.fromRGB(125, 85, 255) local bodyFont = o.Font or Kit.Font('Heavy', false) local label = o.Text or 'Loading' local runService = svc('RunService') local gui = Instance.new('ScreenGui') gui.Name = 'ZkxSplash' gui.IgnoreGuiInset = true gui.ResetOnSpawn = false gui.DisplayOrder = 2147483000 gui.Parent = uiParent() local root = Instance.new('CanvasGroup') root.BackgroundColor3 = Color3.fromRGB(255, 255, 255) root.BackgroundTransparency = o.Dim or 0.4 -- how much of the (blurred) game shows through root.BorderSizePixel = 0 root.Size = UDim2.new(1, 0, 1, 0) root.GroupTransparency = 1 root.Parent = gui local backdrop = Instance.new('UIGradient') backdrop.Color = ColorSequence.new(Color3.fromRGB(12, 12, 15), Color3.fromRGB(6, 6, 8)) backdrop.Rotation = 90 backdrop.Parent = root local stage = Instance.new('Frame') stage.AnchorPoint = Vector2.new(0.5, 0.5) stage.Position = UDim2.new(0.5, 0, 0.5, 0) stage.Size = UDim2.fromOffset(300, 300) stage.BackgroundTransparency = 1 stage.Parent = root local stageScale = Instance.new('UIScale') stageScale.Scale = 0.96 stageScale.Parent = stage local logoHolder = Instance.new('Frame') logoHolder.AnchorPoint = Vector2.new(0.5, 0) logoHolder.Position = UDim2.new(0.5, 0, 0, 20) logoHolder.Size = UDim2.fromOffset(150, 150) logoHolder.BackgroundTransparency = 1 logoHolder.Parent = stage local shineGradient if o.Logo and o.Logo ~= '' then local logo = Instance.new('ImageLabel') logo.BackgroundTransparency = 1 logo.Image = o.Logo logo.ScaleType = Enum.ScaleType.Fit logo.Size = UDim2.new(1, 0, 1, 0) logo.Parent = logoHolder -- a white copy of the logo with a moving gradient on it: the glint follows the logo's own shape local shine = Instance.new('ImageLabel') shine.BackgroundTransparency = 1 shine.Image = o.Logo shine.ScaleType = Enum.ScaleType.Fit shine.ImageColor3 = Color3.fromRGB(255, 255, 255) shine.Size = UDim2.new(1, 0, 1, 0) shine.ZIndex = 2 shine.Parent = logoHolder shineGradient = Instance.new('UIGradient') shineGradient.Rotation = 20 shineGradient.Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 1), NumberSequenceKeypoint.new(0.42, 1), NumberSequenceKeypoint.new(0.5, 0.45), NumberSequenceKeypoint.new(0.58, 1), NumberSequenceKeypoint.new(1, 1), }) shineGradient.Offset = Vector2.new(-1, 0) shineGradient.Parent = shine -- fetch the logo right away so it is there when the splash fades in task.spawn(function() pcall(function() svc('ContentProvider'):PreloadAsync({ logo, shine }) end) end) end local track = Instance.new('Frame') track.AnchorPoint = Vector2.new(0.5, 0) track.Position = UDim2.new(0.5, 0, 0, 212) track.Size = UDim2.fromOffset(220, 2) track.BackgroundColor3 = Color3.fromRGB(255, 255, 255) track.BackgroundTransparency = 0.88 track.BorderSizePixel = 0 track.Parent = stage local fill = Instance.new('Frame') fill.Size = UDim2.new(0, 0, 1, 0) fill.BackgroundColor3 = accent fill.BorderSizePixel = 0 fill.Parent = track local status = Instance.new('TextLabel') status.BackgroundTransparency = 1 status.Text = label status.RichText = true status.FontFace = bodyFont status.TextSize = 13 status.TextColor3 = Color3.fromRGB(255, 255, 255) status.TextTransparency = 0.45 status.AnchorPoint = Vector2.new(0.5, 0) status.Position = UDim2.new(0.5, 0, 0, 228) status.Size = UDim2.new(1, 0, 0, 18) status.Parent = stage local api = { Gui = gui, Finished = false, Closed = false, _outro = function() end } local target, shown = 0, 0 local lastProgress local started = os.clock() local finishing = false local connection connection = runService.RenderStepped:Connect(function(dt) local t = os.clock() - started if shineGradient then local phase = (t % 3.6) / 3.6 shineGradient.Offset = Vector2.new(phase < 0.5 and (-1 + phase / 0.5 * 2) or 1, 0) end -- "Loading", "Loading.", "Loading..", "Loading..." and around again (rich text keeps the width constant, the text does not jitter) local dots = math.floor(t / 0.45) % 4 local text = label for i = 1, 3 do text ..= string.format('.', i <= dots and 0 or 1) end status.Text = text -- a glide towards the goal; the goal creeps a little ahead of the real target while a stage is slow, so the bar never stands still local goal = target if not finishing and lastProgress then goal = math.min(target + math.min(0.04, (os.clock() - lastProgress) * 0.012), 0.97) end shown += (goal - shown) * math.min(1, dt * 3) fill.Size = UDim2.new(math.clamp(shown, 0, 1), 0, 1, 0) if finishing and shown > 0.985 and not api.Finished then api.Finished = true api._outro() end end) -- the game behind the splash is blurred (removed again when the splash goes) local blur local blurSize = o.Blur == nil and 24 or o.Blur if blurSize and blurSize > 0 then local okBlur, effect = pcall(function() local made = Instance.new('BlurEffect') made.Name = 'ZkxSplashBlur' made.Size = 0 made.Parent = workspace.CurrentCamera or svc('Lighting') return made end) if okBlur then blur = effect tween(blur, 0.5, { Size = blurSize }) end end tween(root, 0.45, { GroupTransparency = 0 }) tween(stageScale, 0.7, { Scale = 1 }, Enum.EasingStyle.Quint) function api.SetStatus(text) if not finishing and type(text) == 'string' and text ~= '' then label = text end end function api.Progress(fraction) if finishing then return end -- the bar rests a little before the end until Done is called target = math.max(target, math.clamp(tonumber(fraction) or 0, 0, 0.97)) lastProgress = os.clock() end function api._outro() tween(root, 0.4, { GroupTransparency = 1 }, Enum.EasingStyle.Quad, Enum.EasingDirection.In) if blur then tween(blur, 0.4, { Size = 0 }, Enum.EasingStyle.Quad, Enum.EasingDirection.In) end task.delay(0.45, function() connection:Disconnect() pcall(function() gui:Destroy() end) if blur then pcall(function() blur:Destroy() end) end api.Closed = true end) end function api.Done() if finishing then return end finishing = true target = 1 -- never hang on a bar that cannot move (no frames, or the game paused) task.delay(1, function() if not api.Finished then api.Finished = true api._outro() end end) end function api.Wait(seconds) local limit = os.clock() + (seconds or 5) while not api.Closed and os.clock() < limit do task.wait(0.05) end return api.Closed end -- never leave the splash up if the script dies half way task.delay(o.MaxTime or 90, function() api.Done() end) return api end -- ------------------------------------------------------------------ glow behind the window function Kit.Glow(Library, o) o = o or {} local main, gui = Kit.FindMain(Library) if not main then return { Set = function() end, Enabled = false } end local controller = { Enabled = o.Enabled ~= false, Layers = {} } -- its own gui one display order below obsidian's: layers behind the window without negative z indexes local glowGui = Instance.new('ScreenGui') glowGui.Name = 'ZkxGlow' glowGui.ResetOnSpawn = false glowGui.IgnoreGuiInset = gui.IgnoreGuiInset glowGui.DisplayOrder = gui.DisplayOrder - 1 glowGui.Parent = gui.Parent local pads = o.Pads or { 6, 13, 21 } local alphas = o.Transparency or { 0.88, 0.93, 0.965 } for i, pad in ipairs(pads) do local layer = Instance.new('Frame') layer.Name = 'ZkxGlow' .. i layer.BorderSizePixel = 0 layer.BackgroundColor3 = Library.Scheme and Library.Scheme.AccentColor or Color3.fromRGB(125, 85, 255) layer.BackgroundTransparency = alphas[i] or 0.95 layer.Visible = false layer.Parent = glowGui local corner = Instance.new('UICorner') corner.CornerRadius = UDim.new(0, (Library.CornerRadius or 6) + pad) corner.Parent = layer if Library.Registry then Library.Registry[layer] = { BackgroundColor3 = 'AccentColor' } end controller.Layers[i] = { Frame = layer, Pad = pad } end local function sync() for _, item in ipairs(controller.Layers) do local layer, pad = item.Frame, item.Pad layer.AnchorPoint = main.AnchorPoint layer.Position = UDim2.new(main.Position.X.Scale, main.Position.X.Offset, main.Position.Y.Scale, main.Position.Y.Offset) layer.Size = UDim2.new(main.Size.X.Scale, main.Size.X.Offset + pad * 2, main.Size.Y.Scale, main.Size.Y.Offset + pad * 2) layer.Visible = controller.Enabled and main.Visible end end for _, property in ipairs({ 'Position', 'Size', 'Visible', 'AnchorPoint' }) do main:GetPropertyChangedSignal(property):Connect(sync) end function controller.Set(value) controller.Enabled = value ~= false sync() end if Library.OnUnload then Library:OnUnload(function() pcall(function() glowGui:Destroy() end) end) end sync() return controller end -- ------------------------------------------------------------------ blur behind the open window function Kit.Blur(Library, o) o = o or {} local controller = { Enabled = o.Enabled ~= false, Amount = o.Size or 10 } local camera = workspace.CurrentCamera local effect = Instance.new('BlurEffect') effect.Name = 'ZkxMenuBlur' effect.Size = 0 effect.Parent = camera or svc('Lighting') local shown = false local function refresh() local wanted = controller.Enabled and Library.Toggled == true if wanted ~= shown then shown = wanted tween(effect, 0.3, { Size = wanted and controller.Amount or 0 }) end end -- obsidian has no "opened" signal; a boolean compare per frame is enough local connection = svc('RunService').RenderStepped:Connect(refresh) function controller.Set(value) controller.Enabled = value ~= false refresh() end function controller.Destroy() connection:Disconnect() pcall(function() effect:Destroy() end) end if Library.OnUnload then Library:OnUnload(controller.Destroy) end return controller end -- ------------------------------------------------------------------ theme colours that blend instead of snapping local SCHEME_COLORS = { 'BackgroundColor', 'MainColor', 'AccentColor', 'OutlineColor', 'FontColor' } function Kit.ThemeFade(Library, o) o = o or {} local duration = o.Duration or 0.45 local controller = { Enabled = o.Enabled ~= false, Fading = false, _stepping = false } local original = Library.UpdateColorsUsingRegistry if type(original) ~= 'function' then return controller end local displayed = {} for _, key in ipairs(SCHEME_COLORS) do displayed[key] = Library.Scheme[key] end local target = {} local startColors = {} local elapsed, lastStep = 0, 0 local connection local function stop() if connection then connection:Disconnect() connection = nil end controller.Fading = false end local function step(alpha) for _, key in ipairs(SCHEME_COLORS) do local value = startColors[key]:Lerp(target[key], alpha) Library.Scheme[key] = value displayed[key] = value end -- through the current (outermost) wrapper, so anything hooked on top of this runs on every step too Library:UpdateColorsUsingRegistry() end Library.UpdateColorsUsingRegistry = function(self, ...) if controller.Fading and controller._stepping then return original(self, ...) end if not controller.Enabled then for _, key in ipairs(SCHEME_COLORS) do displayed[key] = Library.Scheme[key] end return original(self, ...) end local changed = false for _, key in ipairs(SCHEME_COLORS) do local new = Library.Scheme[key] target[key] = new if new ~= displayed[key] then changed = true end end if not changed then return original(self, ...) end -- start (or restart) a blend from what is on screen now to the new colours for _, key in ipairs(SCHEME_COLORS) do startColors[key] = displayed[key] Library.Scheme[key] = displayed[key] end stop() controller.Fading = true elapsed, lastStep = 0, -1 connection = svc('RunService').RenderStepped:Connect(function(dt) elapsed += dt if elapsed - lastStep < 0.035 and elapsed < duration then return end lastStep = elapsed local alpha = math.clamp(elapsed / duration, 0, 1) alpha = 1 - (1 - alpha) * (1 - alpha) controller._stepping = true step(alpha) controller._stepping = false if elapsed >= duration then stop() end end) return original(self, ...) end function controller.Set(value) controller.Enabled = value ~= false if not controller.Enabled and controller.Fading then -- finish at once stop() for _, key in ipairs(SCHEME_COLORS) do Library.Scheme[key] = target[key] or Library.Scheme[key] displayed[key] = Library.Scheme[key] end original(Library) end end if Library.OnUnload then Library:OnUnload(stop) end return controller end -- ------------------------------------------------------------------ everything at once function Kit.Apply(Library, Window, ThemeManager, o) o = o or {} local result = {} local function try(name, fn) local ok, value = pcall(fn) if ok then result[name] = value else warn('[ZkxUIKit] ' .. name .. ': ' .. tostring(value)) end end o.Library = o.Library or Library try('TitleRow', function() return Kit.TitleRow(Window, o) end) try('Font', function() return Kit.ApplyFont(Library, ThemeManager, o.Font, o.FontName) end) try('Animations', function() return Kit.Animations(Window, o) end) -- glow and blur only when asked for: Apply(..., { Glow = {}, Blur = {} }) if o.Glow then try('Glow', function() return Kit.Glow(Library, o.Glow) end) end if o.Blur then try('Blur', function() return Kit.Blur(Library, o.Blur) end) end try('ThemeFade', function() return Kit.ThemeFade(Library, o.ThemeFade) end) return result end -- ------------------------------------------------------------------ hub helpers (not part of the original kit) Kit.HubFontName = 'Gotham Heavy' Kit.FontNames = { 'Gotham Heavy', 'BuilderSans', 'Code', 'Fantasy', 'Gotham', 'Jura', 'Roboto', 'RobotoMono', 'SourceSans' } Kit.LogoAsset = 'rbxassetid://91068904566139' Kit.Invite = 'https://discord.gg/bxu2WMjNjN' function Kit.NormalizeFontName(name) if type(name) == 'string' and table.find(Kit.FontNames, name) then return name end return Kit.HubFontName end -- name from the Font list -> a Font value function Kit.ResolveFont(name) name = Kit.NormalizeFontName(name) if name == Kit.HubFontName then return Kit.Font('Heavy', false) end local ok, font = pcall(function() return Font.fromEnum(Enum.Font[name]) end) return ok and font or Kit.Font('Heavy', false) end -- ------------------------------------------------------------------ ZkxVader overrides (this build only) -- the font list leads with the Vaderhaxx pixel font; Obsidian-specific polish becomes a no-op (the fork has its own transitions and title) Kit.HubFontName = 'ProggyClean' Kit.FontNames = { 'ProggyClean', 'Gotham Heavy', 'Code', 'RobotoMono', 'Jura', 'Gotham', 'SourceSans', 'BuilderSans', 'Roboto', 'Fantasy' } local originalResolveFont = Kit.ResolveFont function Kit.ResolveFont(name) name = Kit.NormalizeFontName(name) if name == 'ProggyClean' then local ok, library = pcall(RequireSharedModule, 'obsidian_library') if ok and library and library.PixelFont then return library.PixelFont end return Font.fromEnum(Enum.Font.Code) end if name == 'Gotham Heavy' then return Kit.Font('Heavy', false) end local ok, font = pcall(function() return Font.fromEnum(Enum.Font[name]) end) return ok and font or Font.fromEnum(Enum.Font.Code) end function Kit.TitleRow() return nil end function Kit.Animations() return nil end function Kit.ApplyFont() return nil end function Kit.Glow() return nil end function Kit.Blur() return nil end function Kit.ThemeFade(Library, o) local fade = { Enabled = not (type(o) == 'table' and o.Enabled == false) } Library.ThemeFade = fade.Enabled function fade.Set(value) fade.Enabled = value == true Library.ThemeFade = fade.Enabled end function fade.Destroy() end return fade end -- ------------------------------------------------------------------ ZkxVader loading card (replaces the stock splash in this build) -- A centred card in the menu's own style (layered outlines, pixel font, purple accent): a shimmering title, the 3D ZK logo turning in the middle, -- a status line, and a progress bar with a moving highlight. Same interface as the stock splash: -- Kit.Splash({ Logo, Accent, Text, Title, Dim }) -> { SetStatus(text), Progress(fraction), Done(), Wait(seconds), Closed } -- Done() fades the card out; Wait() returns once it is gone. -- The logo is a real 3D model (Blender, lit, in its own colours) baked into a sprite sheet of its turn; the card plays it as an endless slow spin. -- The sheets ship inside this file as base64, are written once to ZkxHub/vader and loaded with getcustomasset. Without file support, `Logo` is shown flat. do -- baked spin of the ZK logo (Blender render, 160 frames of 112x99 on 2 sheets), PNGs as base64 local SPIN_FRAMES, SPIN_PER_SHEET, SPIN_COLUMNS, SPIN_CELL_W, SPIN_CELL_H = 160, 80, 9, 112, 99 local SPIN_SHEETS = { { tag = 'fb89e6be', bytes = 278696, data = 'iVBORw0KGgoAAAANSUhEUgAAA/AAAAN7CAMAAADV9oEnAAADAFBMVEX28fzs4/fn2/Xd2O3c0O/ZyPLRyuPPw+LPvenGvNzHtePFruq9ttG5r9C3q8+9pOe3pdCvo82ro7y0mOOsldmsm8aplMWgpLOboK2dm7OUmqeek7mPk6Kpit2ghdOfjLybhLyUirCRgrOLjJuKhJmcfdCZdtKRd8WPb8eSfK6JfKiMdauJcKqCh5WAgo1/gI18fZF+fYSBeJt0eIp4eIB+cZ1vcYZ0c3txcHltb3iMacaDZbyCa6F/ZKR3aJx2YZ9xa4J0Y4xoa3tra3RqanJpY4NnZXJnaG1lZGxjYmt/XL14XKh4UrZvWKRtUaZsTKdzXI5tWopnWoplWnppU4hkUoJlTYpfZHJfYGteX2lcXWpcXGJZWmxaWmRZWWFWWV1dVHlUVWdUVWFVVVxRU1pdTYhbTXdYTW9ST2BPTl9LTl1PT1dNT1RNTFVKTVRKSlNGSlRrRqxmPqpjR5RhP5pZQ5BXPJBWRH5SPH9WR21OR2VRQGtMP2VJR1lJSE9JP2FGSFREQ1dERlBERU5CQ01EPWJEPVZCPlA+Pk5DRElAQEg+P0Y+PEU6O0NdNp5WNJVSNYZONndJNHhDM3NFNWM/NGBCOFNAM1Q8NU86NUo4NkQ3Njs3MUhSLJBKLHtDLHI9K28/LGA5K106LU82LEpJJYI+JXA9HnQ5Jl44IGI0ND8zMEIzMzcxMDYyLkEvLjQyK0gwKzwtLDMqKy8yJ1AwJ0MtKDorKDIoJy4mJywyIV4wIVEvIkcpIEYsIzwpIjklIjYlHzcnJC4kJColIC4jIikiHywgISUgHiYxG18rGVkrGkwlGUgkHDckGDogHC0gGDUeGi4dGigeGyYbGCYZGSYcFy0aFyYeHCMdGiMaGR8cFyQaFyUaFyEZFiQYFiAWFxslFUYaFSwbFSUYFSQaFSMYFSMXFCMWFB4kEUodEEAcEjMbDjcXDjEWECgUCS4XEyEVEh8SERsSDh0RCiEODRUMChULCBUKBhcFBQoFARMBAQQAAwACAQAAAAAAAABA2ZUbAAABAHRSTlP///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8AU/cHJQABAABJREFUeNrs/XlQVFe7+I8C0sg8g1FkVAQBE14IvoktIEaN0gwKUREEAQEHpkAziiICKvMgEgRlEGRUiUQgOcxJOHnTEAGpiqZuqnIuhqp8QUXx9VR97/nj9n3WWnvv3rtpFIjJH/d3VhAT2/TD3r0/65mfJSf+3/W/63/X/2OWHPo2PT89P//H/Dz1beFa8MfT8P+89b1/p/7yy5cv0a+XL6dfDU9ieTLXPPopqLfH/8mRNzm9ZHnzL8nXy5evhvEL05OTP//28/RbFlveY/Gjt8qbfrxA3jwl79Eks36b/u233xaVCf8LfX2Tb5U3je4hEUeJpX7GR+Pj40TaQ1rqb0SyLIn4kxCPj79N3uPpefrHk3wMeA0N/TBOL8mV/vrb5K+//vrbr7+RNT39m0Tm9PSjh2+9Po4w9Cf0IzY4+MPwOFsiSyq61l9pmazrffv1TTNypNdg1/jwD2hRAse58n7FF/sbvlJa3M9vl7f4Gq4YGpaIQ/I4AqmrJHeW3NDp6YfjL1cO/DXdtYutdeZSa8MG8w02Nl3iB77nhl698a3rD+1Gy8fH18f3oK9vcHBEqs86/Mp5WfKkxdjY2Gwky8HBpkLccPDc0Jv3mNLkExEn0EpNTT1z5sz58xfLff+BX7mku9ZoLfqSlmKDpNhgSQ7UcrK3d3JzShOfTb70ZnkPUq5euEit8i++qKiouNbpeBy/dMOIK2aztBAkxsnJxQ0vdJOSxcnJl7vfuMd0plRc466GBscf8Uv3zM0t4MuGWVgCWS60lAPwSRw8eDAYPoeIE8mPDiXfGHqTvMnSUpa8CrQaToSQ18470WJoGS5YzAEfHyyDCIlAH0TamTNnz8Md6vcM7xr+7Q03tC+l9FL5xUvll9D9PH8GreDdFEQR+4kIJGT/fokQEAEy0mCBECSlHC/4UcvrnQ91/fAGeZOlKWln0MMCP6rv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' }, { tag = '3e7bed5f', bytes = 276387, data = 'iVBORw0KGgoAAAANSUhEUgAAA/AAAAN7CAMAAADV9oEnAAADAFBMVEX17vvq4fbm2fTd1e3azu3Xxu/Px+PNweLNvObEu9zGteHDruS6tNG3rc+zqcu4ouG0o8qsocuoobuyluSqk9qolsKeorKYnquembSSmKeckbiNkaCoiN+fhdadirqag7mQiauOgq2IipWGg5GbfdKXddKPcsiRe66He6WJcauAhZN9f4x7e4l+d5Zxdoh2dn5+b6N4b5BtcIFycXlvbndsbXWJZsqFZ7N9ZLR4ZaB7ZpNxZoxra3RrZn5laHdnZ3BlZHRlZmtjYmthYWp/XLt2W6p4VbR1TrZtV6RsT6ZrSqdyW4xsWollWoZiXHBpU4hjUoJkTIldYnBdXmlcXWlcXGJZWmtaWmRZWWFWWV1aVHRUVWZUVWFVVV1UU1tRU1pbTIhaTHVYTGxRTGhRUFtPT1pQS19LTlxKSlxPT1VNTVRLTVJLSlNIS1JHSFFpRKxkPKliRJZhPJpZQpBVQn9ZOo5QOoRVRWxMRWJHRVdHRk1QQGlOO25KPWVHPGBERlJERU5CQVNBQ01CQ0lAQEg+PkZCOl9BOlI9O0s9PEM6OkI4OkBcNJxUMpJQNn9PMYJHMYBHMnFFM2Y+MmNBNlM7Nko/MVU6MU44NUQ3Njw3MUhQK41JK3tAKnU9K2E4K09HJX48JXM+HnU7JWU4JVk4HmM0NTszMjsyMDsxLzsyK0YvKzstLDMqKy8yJlMwJkUuJzspJjorJzIpJy8nJiwkJSo0IGAvIF0uIUwsIT8pITslIDknIjAkIS0jISkiHywgHycfICEwG2IvGlUoGVUpGkckGEQkHDUjGDggGjUeFjYeGS0eGikbFykfHCYbGCYaFyYZFiYeHCMdGiMaGR8cFyQaFyUaFyIZFiQZFiAWFxslFUwdFTEZFSkYFSQaFSMYFSMZFSAXFCMWFB8kEUkdEEIdEjYbDjgXDjMWDyoUCS8ZEiQWEyEVEiEUEhwSERwTDiARDhwRCiIODRcMChYLCBYLBxgHBg0JBBcFBA0DAwcEABMAAAMDAQAAAAAAAABfPOnuAAABAHRSTlP///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8AU/cHJQABAABJREFUeNrs/QlUVFe68I07oSgCMjmATEYRBwhgZAiiGGYiBg2TygJUiIoNRTHFAaVkEqQAQRlCMRRDoYJhyIBFwCZdxaSE+2b1ur1W7odyl02VgP1qaPp23/Ttu279n73PUOcUgxSdZK3v/93dxqQTPY/7nP3bz7OfaS9T/O/43/G/4/8zY9mLkWkYfxyf/iP59/Hp6ddv/W3jY4qRMfiV6FfPGuN4TL8YJ8eLFy/QX2Nj6HeOjfxxTGX88e3yFGOK4edIGlPeOFveOD0IyS+I3zg8NjLy/cj333+vFDi9OHnEY/HzKSFMKawBIseI39g78t0IY/zww9jIouT1juBXNefDGUMpj5rfcC8ahNBh/PPYW8X98bliePjFAmOOPwXxO0eGB4cf9yoHyP1uZPxt8p4jecTLJ55P/X183kH+zuHhwcHHj1kSv+sde7u871XkUaKm5xZDj+Hu3u7HKgJ7R94qD1597wtCGvV96JU5nyji+3XNnmDvWyeIPvXw2AtifbIwWEAUAbziks5mGPqblWPjyYkFpU33fHJyWFFuaU0NG3rYU8OVGAHkCAwMPHlyEG8VZ5E8faY8/ZNTC8vrvXGyW5FmxxKh+vyTeJyBcRbG1atXL10fIeUZG2Mx+GdjCwuLze5vFt7NeitPtiqSAwgZAW5MCbSAS9euXbt+/XoOjLs5d9GoxF9p/LKehTExLKjh+uPC8xtuvnRHcexTLOL0SXoKIAKef/Mm8fiycpXRQfzuys2kIEs88AcZXFDen5605ZQMfnjt6tVreBJ4DnPLoMfduzm9BBCt1ubWltS3t7OzsbN736Z04dU52FZ+p9Y57W5OWdlsEZWkACwD/S8n59IlmP9dEsDGM5Z2MFxcXN5//330QY4EHLm04BY6/E1beW2aUxpLTFkZEnCXEJADXw7JOHsWfdMAdzcHBzfynY1VN7q6fPjh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' }, } local SPIN_PERIOD = 4.0 -- seconds for one full turn (constant speed) local function decodeBase64(data) local alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' local lookup = {} for index = 1, 64 do lookup[string.byte(alphabet, index)] = index - 1 end local pad = data:sub(-2) == '==' and 2 or (data:sub(-1) == '=' and 1 or 0) local band, rshift = bit32.band, bit32.rshift local out, count = {}, 0 for index = 1, #data, 4 do local a, b, c, d = string.byte(data, index, index + 3) local n = lookup[a] * 262144 + lookup[b] * 4096 + (lookup[c] or 0) * 64 + (lookup[d] or 0) count += 1 out[count] = string.char(rshift(n, 16), band(rshift(n, 8), 255), band(n, 255)) end local joined = table.concat(out) return joined:sub(1, #joined - pad) end -- the cached sprite sheets as content ids (one per sheet), or nil when this executor cannot write files local function spinSheets() if type(isfile) ~= 'function' or type(writefile) ~= 'function' or type(getcustomasset) ~= 'function' then return nil end local ok, assets = pcall(function() local list = {} for index, sheet in ipairs(SPIN_SHEETS) do local path = 'ZkxHub/vader/zk_spin_' .. sheet.tag .. '.png' if not isfile(path) then if type(isfolder) == 'function' and type(makefolder) == 'function' then if not isfolder('ZkxHub') then makefolder('ZkxHub') end if not isfolder('ZkxHub/vader') then makefolder('ZkxHub/vader') end end task.wait() -- one sheet per frame on the first run, so decoding never stalls the card writefile(path, decodeBase64(sheet.data)) end list[index] = getcustomasset(path) end return list end) return ok and assets or nil end -- degrees of turn at time t: a steady, endless spin local function spinAngle(t) return (t / SPIN_PERIOD * 360) % 360 end function Kit.Splash(o) o = o or {} elevate() local runService = svc('RunService') local accent = o.Accent or Color3.fromRGB(125, 85, 255) local title = o.Title or 'ZkxHub' local label = o.Text or 'Loading' local white = Color3.fromRGB(255, 255, 255) -- the pixel font once the library has cached it (first run: the stock monospace font) local font = Font.fromEnum(Enum.Font.Code) pcall(function() if type(isfile) == 'function' and type(getcustomasset) == 'function' and isfile('ZkxHub/vader/ProggyClean.json') then font = Font.new(getcustomasset('ZkxHub/vader/ProggyClean.json'), Enum.FontWeight.Regular) end end) local function new(class, props, parent) local instance = Instance.new(class) for key, value in pairs(props) do instance[key] = value end instance.Parent = parent return instance end local function text(props, parent) props.BackgroundTransparency = 1 props.FontFace = font props.TextSize = props.TextSize or 12 props.TextColor3 = props.TextColor3 or white return new('TextLabel', props, parent) end -- 1px nested outlines: black > grey > dark grey > panel local function layer(parent, color) return new('Frame', { BackgroundColor3 = color, BorderSizePixel = 0, Position = UDim2.fromOffset(1, 1), Size = UDim2.new(1, -2, 1, -2) }, parent) end local gui = new('ScreenGui', { Name = 'ZkxSplash', IgnoreGuiInset = true, ResetOnSpawn = false, DisplayOrder = 2147483000 }, uiParent()) local dim = new('Frame', { Size = UDim2.fromScale(1, 1), BackgroundColor3 = Color3.new(0, 0, 0), BackgroundTransparency = 1, BorderSizePixel = 0 }, gui) local camera = workspace.CurrentCamera local view = camera and camera.ViewportSize or Vector2.new(1280, 720) local width, height = math.clamp(view.X - 24, 240, 360), math.clamp(view.Y - 24, 210, 290) local card = new('CanvasGroup', { AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(0.5, 0.5), Size = UDim2.fromOffset(width, height), BackgroundColor3 = Color3.new(0, 0, 0), BorderSizePixel = 0, GroupTransparency = 1, }, dim) local cardScale = new('UIScale', { Scale = 0.94 }, card) local inner = layer(layer(layer(card, Color3.fromRGB(56, 56, 56)), Color3.fromRGB(46, 46, 46)), Color3.fromRGB(21, 21, 21)) new('UIGradient', { Rotation = 90, Color = ColorSequence.new(Color3.fromRGB(31, 25, 50), Color3.fromRGB(15, 15, 17)) }, inner) -- title (shimmers: the gradient carries accent -> white -> accent, the text itself is white) local titleLabel = text({ Text = title, TextSize = 14, AnchorPoint = Vector2.new(0.5, 0), Position = UDim2.new(0.5, 0, 0, 14), Size = UDim2.new(1, 0, 0, 18) }, inner) local titleGradient = new('UIGradient', { Color = ColorSequence.new({ ColorSequenceKeypoint.new(0, accent), ColorSequenceKeypoint.new(0.4, accent), ColorSequenceKeypoint.new(0.5, white), ColorSequenceKeypoint.new(0.6, accent), ColorSequenceKeypoint.new(1, accent), }), Offset = Vector2.new(-1.2, 0), }, titleLabel) -- the logo: the sprite sheets of the 3D turn (the baked frames are the logo at 1:1 on a normal card). Two labels show neighbouring frames, -- the upper one fades in as the turn moves on to it, so the spin glides instead of stepping. The flat logo only when the sheets cannot be loaded. local logoW = math.floor(math.clamp(height - 178, 64, SPIN_CELL_W)) local logoH = math.floor(logoW * SPIN_CELL_H / SPIN_CELL_W + 0.5) local centerY = math.floor((40 + height - 84) / 2) local logoHolder = new('Frame', { AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.new(0.5, 0, 0, centerY), Size = UDim2.fromOffset(logoW, logoH), BackgroundTransparency = 1, }, inner) local function spinLabel(zindex) return new('ImageLabel', { -- not fully transparent: Roblox does not even fetch an image drawn at transparency 1, so it would never report as loaded BackgroundTransparency = 1, Size = UDim2.fromScale(1, 1), ScaleType = Enum.ScaleType.Stretch, ImageTransparency = 0.99, ZIndex = zindex, ImageRectSize = Vector2.new(SPIN_CELL_W, SPIN_CELL_H), ImageRectOffset = Vector2.new(0, 0), }, logoHolder) end local spin, spinNext = spinLabel(1), spinLabel(2) local sheetAssets = {} -- frame -> which sheet, and where on it (the label switches sheet when its frame lives on another one; the sheets are all loaded by then) local function showFrame(imageLabel, frame) local asset = sheetAssets[math.floor(frame / SPIN_PER_SHEET) + 1] if asset and imageLabel.Image ~= asset then imageLabel.Image = asset end local index = frame % SPIN_PER_SHEET imageLabel.ImageRectOffset = Vector2.new((index % SPIN_COLUMNS) * SPIN_CELL_W, math.floor(index / SPIN_COLUMNS) * SPIN_CELL_H) end local spinReady, spinFrame = false, -1 local function showFlat() if o.Logo and o.Logo ~= '' then local flat = new('ImageLabel', { BackgroundTransparency = 1, Image = o.Logo, ScaleType = Enum.ScaleType.Fit, Size = UDim2.fromScale(1, 1), ImageTransparency = 1 }, logoHolder) tween(flat, 0.35, { ImageTransparency = 0 }) task.spawn(function() pcall(function() svc('ContentProvider'):PreloadAsync({ flat }) end) end) end end task.spawn(function() task.wait() -- let the card draw first; decoding the sheets (first run only) takes a moment local assets = spinSheets() if not assets then showFlat() return end sheetAssets = assets spin.Image = assets[1] spinNext.Image = assets[1] -- every sheet gets a 1 px label that keeps it loaded (a sheet nobody draws yet is not fetched); the first sheet is the two spin labels themselves local probes = { spin } for index = 2, #assets do probes[index] = new('ImageLabel', { BackgroundTransparency = 1, Size = UDim2.fromOffset(1, 1), ImageTransparency = 0.99, Image = assets[index] }, logoHolder) end pcall(function() svc('ContentProvider'):PreloadAsync(probes) end) -- a sheet is loaded when its label says so, or the content provider reports the fetch as done (either signal is enough) local function fetched(index) if probes[index].IsLoaded then return true end local ok, status = pcall(function() return svc('ContentProvider'):GetAssetFetchStatus(assets[index]) end) return ok and status == Enum.AssetFetchStatus.Success end local function loaded() for index = 1, #assets do if not fetched(index) then return false end end return true end local deadline = os.clock() + 4 while not loaded() and os.clock() < deadline do task.wait(0.05) end if loaded() then spinReady = true tween(spin, 0.35, { ImageTransparency = 0 }) else local states = {} for index = 1, #assets do local okStatus, status = pcall(function() return svc('ContentProvider'):GetAssetFetchStatus(assets[index]) end) states[index] = 'sheet ' .. index .. ' IsLoaded=' .. tostring(probes[index].IsLoaded) .. ' fetch=' .. tostring(okStatus and status or 'n/a') end showFlat() end end) -- status line and progress bar local status = text({ Text = label, RichText = true, TextTransparency = 0.15, AnchorPoint = Vector2.new(0.5, 0), Position = UDim2.new(0.5, 0, 0, height - 68), Size = UDim2.new(1, -24, 0, 16) }, inner) local barWidth = width - 56 local barOuter = new('Frame', { AnchorPoint = Vector2.new(0.5, 0), Position = UDim2.new(0.5, 0, 0, height - 46), Size = UDim2.fromOffset(barWidth, 16), BackgroundColor3 = Color3.new(0, 0, 0), BorderSizePixel = 0, }, inner) local barInner = layer(layer(barOuter, Color3.fromRGB(56, 56, 56)), Color3.fromRGB(12, 12, 12)) local fill = new('Frame', { Size = UDim2.new(0, 0, 1, 0), BackgroundColor3 = white, BorderSizePixel = 0, ZIndex = 2 }, barInner) local light = Color3.fromRGB(math.min(255, accent.R * 255 + 70), math.min(255, accent.G * 255 + 70), math.min(255, accent.B * 255 + 70)) local fillGradient = new('UIGradient', { Color = ColorSequence.new({ ColorSequenceKeypoint.new(0, accent), ColorSequenceKeypoint.new(0.5, light), ColorSequenceKeypoint.new(1, accent) }), }, fill) local percent = text({ Text = '0%', ZIndex = 3, Size = UDim2.fromScale(1, 1) }, barInner) percent.TextStrokeTransparency = 0.45 percent.TextStrokeColor3 = Color3.new(0, 0, 0) local api = { Gui = gui, Finished = false, Closed = false, _outro = function() end } local target, shown = 0, 0 local lastProgress local started = os.clock() local finishing = false local connection connection = runService.RenderStepped:Connect(function(dt) local now = os.clock() local t = now - started -- title shimmer: a bright band every 5 seconds local phase = (t % 5) / 0.9 titleGradient.Offset = Vector2.new(phase < 1 and (-1.2 + phase * 2.4) or 1.2, 0) -- the logo: the turn sits between two baked frames; the upper label shows the next one at the matching strength if spinReady then local position = spinAngle(t) / 360 * SPIN_FRAMES local frame = math.floor(position) % SPIN_FRAMES if frame ~= spinFrame then spinFrame = frame showFrame(spin, frame) showFrame(spinNext, (frame + 1) % SPIN_FRAMES) end spinNext.ImageTransparency = math.min(0.995, 1 - (position - math.floor(position))) end -- the status line, exactly as given, then "Loading", "Loading.", "Loading..", "Loading..." (rich text keeps the width, the text does not jitter) local shownLabel = label:gsub('&', '&'):gsub('<', '<'):gsub('>', '>') local dots = math.floor(t / 0.45) % 4 for index = 1, 3 do shownLabel ..= string.format('.', index <= dots and 0 or 1) end status.Text = shownLabel -- a glide towards the goal; the goal creeps a little ahead of the real target while a stage is slow, so the bar never stands still local goal = target if not finishing and lastProgress then goal = math.min(target + math.min(0.04, (now - lastProgress) * 0.012), 0.97) end shown += math.max(0, goal - shown) * math.min(1, dt * 3) -- never backwards, even when a repeated Progress call restarts the creep fill.Size = UDim2.new(math.clamp(shown, 0, 1), 0, 1, 0) fillGradient.Offset = Vector2.new((t * 0.7) % 2 - 1, 0) percent.Text = string.format('%d%%', math.floor(math.clamp(shown, 0, 1) * 100 + 0.5)) if finishing and shown > 0.985 and not api.Finished then api.Finished = true api._outro() end end) -- optional blur behind the card (off by default: the card is small and the game stays visible, like the preview) local blur local blurSize = o.Blur or 0 if blurSize > 0 then local okBlur, effect = pcall(function() local made = Instance.new('BlurEffect') made.Name = 'ZkxSplashBlur' made.Size = 0 made.Parent = workspace.CurrentCamera or svc('Lighting') return made end) if okBlur then blur = effect tween(blur, 0.5, { Size = blurSize }) end end tween(dim, 0.35, { BackgroundTransparency = o.Dim or 0.55 }) tween(card, 0.4, { GroupTransparency = 0 }) tween(cardScale, 0.6, { Scale = 1 }, Enum.EasingStyle.Quint) function api.SetStatus(newText) if not finishing and type(newText) == 'string' and newText ~= '' and newText ~= label then label = newText end end function api.Progress(fraction) if finishing then return end -- the bar rests a little before the end until Done is called target = math.max(target, math.clamp(tonumber(fraction) or 0, 0, 0.97)) lastProgress = os.clock() end function api._outro() tween(card, 0.35, { GroupTransparency = 1 }, Enum.EasingStyle.Quad, Enum.EasingDirection.In) tween(cardScale, 0.35, { Scale = 1.05 }, Enum.EasingStyle.Quad, Enum.EasingDirection.In) tween(dim, 0.4, { BackgroundTransparency = 1 }) if blur then tween(blur, 0.4, { Size = 0 }, Enum.EasingStyle.Quad, Enum.EasingDirection.In) end task.delay(0.45, function() connection:Disconnect() pcall(function() gui:Destroy() end) if blur then pcall(function() blur:Destroy() end) end api.Closed = true end) end function api.Done() if finishing then return end finishing = true target = 1 -- never hang on a bar that cannot move (no frames, or the game paused) task.delay(1.6, function() if not api.Finished then api.Finished = true api._outro() end end) end function api.Wait(seconds) local limit = os.clock() + (seconds or 5) while not api.Closed and os.clock() < limit do task.wait(0.05) end return api.Closed end -- never leave the splash up if the script dies half way task.delay(o.MaxTime or 90, function() api.Done() end) return api end end return Kit end -- ====================================================================== -- UI extras: font, fades, scale, cursor, splash -- ====================================================================== __ZkhHub_shared.ui_extras = function() -- Menu polish from the ZkxHub UI kit, as settings in the UI Settings tab. Everything here is saved in UISettings.json -- (not in the feature configs), so the look follows you from config to config. local module = {} module.Ids = { Font = 'UIFontFace', ThemeFade = 'UIThemeFade', Animations = 'UIAnimations', Scale = 'UIScale', Cursor = 'UICustomCursor', Splash = 'UILoadingSplash', Watermark = 'UIWatermark', WatermarkFields = 'UIWatermarkFields', NotifyPlacement = 'UINotifyPlacement', NotifyStyle = 'UINotifyStyle', } module.AllIds = { 'UIFontFace', 'UIThemeFade', 'UIAnimations', 'UIScale', 'UICustomCursor', 'UILoadingSplash', 'UIWatermark', 'UIWatermarkFields', 'UINotifyPlacement', 'UINotifyStyle' } module.ToggleIds = { 'UIThemeFade', 'UIAnimations', 'UICustomCursor', 'UILoadingSplash', 'UIWatermark' } module.Scales = { '50%', '75%', '90%', '100%', '110%', '125%', '150%', '175%', '200%' } local ANIMATIONS_OFF = { ToggleWindow = false, TabSwitch = false, Groupbox = false, Dropdown = false, KeyPicker = false } local function boolean(value, fallback) if type(value) == 'boolean' then return value end return fallback end local function scaleValue(value) if type(value) == 'string' and table.find(module.Scales, value) then return value end return '100%' end -- ZkxVader watermark details: UISettings.json keeps them as one comma separated text (it stores only text and switches) local WATERMARK_FIELDS_DEFAULT = 'FPS,Ping' local WATERMARK_FIELDS_NONE = 'None' local function parseWatermarkFields(saved, valid) local picked = {} if type(saved) == 'string' and saved ~= WATERMARK_FIELDS_NONE then for name in string.gmatch(saved, '[^,]+') do if table.find(valid, name) then table.insert(picked, name) end end end return picked end -- ZkxVader notifications: where the stack sits and which look it has; UISettings.json keeps both as plain text local NOTIFY_PLACEMENT_DEFAULT = 'Top Right' local NOTIFY_STYLE_DEFAULT = 'Premium' local function notifyChoice(saved, valid, fallback) if type(saved) == 'string' and table.find(valid, saved) then return saved end return fallback end function module.build(args) local Kit = RequireSharedModule('zkx_ui_kit') local ids = module.Ids local library, window, themeManager = args.library, args.window, args.theme_manager local options, toggles = args.options, args.toggles local persisted = args.persisted or {} local preserved = args.preserved_ids or {} local save = args.save or function() end local self = {} -- font: the hub font plus the stock list, kept across theme changes args.theme_group:AddDropdown(ids.Font, { Text = 'Font', Values = Kit.FontNames, Default = Kit.NormalizeFontName(persisted.font_face), Multi = false, AllowNull = false, Tooltip = 'The text font of the whole menu. Gotham Heavy is the hub font.', }) local function currentFontName() local option = options[ids.Font] return Kit.NormalizeFontName(option and option.Value) end local function applyFont() pcall(library.SetFont, library, Kit.ResolveFont(currentFontName())) end options[ids.Font]:OnChanged(function() applyFont() save() end) -- applying a theme puts the theme's own font back: take the chosen one again if themeManager and not themeManager.__ZkxFontPinned and type(themeManager.ApplyTheme) == 'function' then themeManager.__ZkxFontPinned = true local original = themeManager.ApplyTheme themeManager.ApplyTheme = function(manager, ...) local ok, err = original(manager, ...) task.defer(applyFont) return ok, err end end local group = args.group local function toggleValue(id) local toggle = toggles[id] return toggle ~= nil and toggle.Value == true end group:AddToggle(ids.ThemeFade, { Text = 'Smooth Theme Changes', Default = boolean(persisted.theme_fade, true), Tooltip = 'Colour changes blend instead of snapping.', Callback = function(value) if self.Fade then self.Fade.Set(value == true) end save() end, }) group:AddToggle(ids.Animations, { Text = 'Menu Animations', Default = boolean(persisted.animations, true), Tooltip = 'Tab swipe, section collapse, dropdown and keybind picker transitions.', Callback = function() self.ApplyAnimations() save() end, }) group:AddToggle(ids.Cursor, { Text = 'Custom Cursor', Default = boolean(persisted.custom_cursor, true), Tooltip = "Draws the menu's own cursor while the menu is open.", Callback = function() self.ApplyCursor() save() end, }) group:AddDropdown(ids.Scale, { Text = 'UI Scale', Values = module.Scales, Default = scaleValue(persisted.ui_scale), Multi = false, AllowNull = false, Tooltip = 'Size of the whole menu.', }) options[ids.Scale]:OnChanged(function() self.ApplyScale() save() end) group:AddToggle(ids.Splash, { Text = 'Loading Splash', Default = boolean(persisted.splash, true), Tooltip = 'Shows the logo while the hub loads. Applies the next time the script starts.', Callback = function() save() end, }) group:AddToggle(ids.Watermark, { Text = 'Watermark', Default = boolean(persisted.watermark, true), Tooltip = 'Shows ZkxHub and the details you pick below in the corner.', Callback = function() self.ApplyWatermark() save() end, }) -- which details the watermark shows next to its name local function watermarkFieldsText() local option = options[ids.WatermarkFields] if not option or not library.GetWatermarkFieldIds then return persisted.watermark_fields or WATERMARK_FIELDS_DEFAULT end local picked = {} for _, name in ipairs(library:GetWatermarkFieldIds()) do if option.Value[name] then table.insert(picked, name) end end return #picked > 0 and table.concat(picked, ',') or WATERMARK_FIELDS_NONE end if library.GetWatermarkFieldIds then local fieldIds = library:GetWatermarkFieldIds() local fieldBox = group:AddDependencyBox() fieldBox:AddDropdown(ids.WatermarkFields, { Text = 'Watermark Details', Values = fieldIds, Default = parseWatermarkFields(persisted.watermark_fields, fieldIds), Multi = true, AllowNull = true, Tooltip = 'What the watermark shows next to the name: username, executor, fps, ping, the map, the server region and the time.', }) fieldBox:SetupDependencies({ { toggles[ids.Watermark], true } }) options[ids.WatermarkFields]:OnChanged(function() self.ApplyWatermark() save() end) end -- notifications: placement (Trello #32) and style (Trello #33); both live in UISettings.json, not in configs local function notifyText(id, fallback) local option = options[id] if option and type(option.Value) == 'string' and option.Value ~= '' then return option.Value end return fallback end if library.SetNotifyPlacement and library.NotifyPlacementNames then local placements, styles = library.NotifyPlacementNames, library.NotifyStyleNames group:AddDropdown(ids.NotifyPlacement, { Text = 'Notification Placement', Values = placements, Default = notifyChoice(persisted.notify_placement, placements, NOTIFY_PLACEMENT_DEFAULT), Multi = false, AllowNull = false, Tooltip = 'Where the notifications stack on the screen.', }) group:AddDropdown(ids.NotifyStyle, { Text = 'Notification Style', Values = styles, Default = notifyChoice(persisted.notify_style, styles, NOTIFY_STYLE_DEFAULT), Multi = false, AllowNull = false, Tooltip = 'Premium: translucent card, accent edge, coloured numbers. Classic: the original boxed look.', }) local function previewNotify() self.ApplyNotify() save() library:Notify({ Title = 'Notifications', Description = string.format('%s, %s style', notifyText(ids.NotifyPlacement, NOTIFY_PLACEMENT_DEFAULT), notifyText(ids.NotifyStyle, NOTIFY_STYLE_DEFAULT)), Time = 3, }) end options[ids.NotifyPlacement]:OnChanged(previewNotify) options[ids.NotifyStyle]:OnChanged(previewNotify) end group:AddButton({ Text = 'Turn Off All Toggles', DoubleClick = true, Tooltip = 'Switches off every feature toggle. With Auto Save Config on, the selected config is saved that way.', Func = function() local count = 0 for id, toggle in pairs(toggles) do if not preserved[id] and type(toggle) == 'table' and toggle.Value == true then if pcall(toggle.SetValue, toggle, false) then count += 1 end end end library:Notify({ Title = 'Toggles', Description = string.format('Turned off %d toggle(s).', count), Time = 4 }) end, }) if args.tab and args.tab.AddRightGroupbox then local community = args.tab:AddRightGroupbox('Community') community:AddLabel(Kit.Invite, true) community:AddButton('Copy Invite', function() local copy = setclipboard or toclipboard if type(copy) == 'function' then pcall(copy, Kit.Invite) library:Notify({ Title = 'Community', Description = 'Invite copied.', Time = 3 }) else library:Notify({ Title = 'Community', Description = Kit.Invite, Time = 8 }) end end) end function self.ApplyAnimations() if window and type(window.SetAnimations) == 'function' then local animations = toggleValue(ids.Animations) and Kit.DefaultAnimations or ANIMATIONS_OFF pcall(window.SetAnimations, window, animations, 0.24, 22, 'bottom') end end function self.ApplyScale() local option = options[ids.Scale] local percent = tonumber(string.match(tostring(option and option.Value or '100%'), '%d+')) or 100 pcall(library.SetDPIScale, library, percent) end function self.ApplyWatermark() local picker = options[ids.WatermarkFields] if picker and library.SetWatermarkFields then library:SetWatermarkFields(picker.Value) end if library.SetWatermarkVisible then library:SetWatermarkVisible(toggleValue(ids.Watermark)) end end function self.ApplyNotify() local placement, style = options[ids.NotifyPlacement], options[ids.NotifyStyle] if placement and library.SetNotifyPlacement then library:SetNotifyPlacement(placement.Value) end if style and library.SetNotifyStyle then library:SetNotifyStyle(style.Value) end end function self.ApplyCursor() library.ShowCustomCursor = toggleValue(ids.Cursor) self.ApplyWatermark() self.ApplyNotify() end function self.Reapply() applyFont() self.ApplyAnimations() self.ApplyScale() self.ApplyCursor() end -- called once the default theme has been applied, so the start-up colours do not blend function self.Finish() local ok, fade = pcall(Kit.ThemeFade, library, { Enabled = toggleValue(ids.ThemeFade) }) if ok then self.Fade = fade end self.Reapply() end function self.Snapshot() local scale = options[ids.Scale] return { font_face = currentFontName(), theme_fade = toggleValue(ids.ThemeFade), animations = toggleValue(ids.Animations), ui_scale = scaleValue(scale and scale.Value), custom_cursor = toggleValue(ids.Cursor), splash = toggleValue(ids.Splash), watermark = toggleValue(ids.Watermark), watermark_fields = watermarkFieldsText(), notify_placement = notifyText(ids.NotifyPlacement, NOTIFY_PLACEMENT_DEFAULT), notify_style = notifyText(ids.NotifyStyle, NOTIFY_STYLE_DEFAULT), } end return self end return module end -- ====================================================================== -- Teleport rage (Automation > Ragebot) -- ====================================================================== __ZkhHub_shared.rivals_ragebot_kernel = function() -- Teleport ragebot. Each physics step the local root is placed above the chosen enemy and shots are sent -- straight to the server from that spot; while reloading / swapping weapons (and once nobody is left) the -- character is thrown far away so it cannot be hit. Turning it off returns you to where you enabled it. -- Same bridge surface as before: ZkhHubRagebot, UpdateZkhHubRagebot, ResetZkhHubRagebot, -- DestroyZkhHubRagebot, SetRageConfigKey. return { init = function(ctx) local RivalsRuntimeBridge = ctx.RivalsRuntimeBridge local Options = ctx.Options local Toggles = ctx.Toggles local Players = cloneref(game:GetService('Players')) local ReplicatedStorage = cloneref(game:GetService('ReplicatedStorage')) local RunService = cloneref(game:GetService('RunService')) local Workspace = cloneref(workspace) local lp = Players.LocalPlayer local Rage = {} local state = { active = false, target = nil, conn = nil, hbConn = nil, charConn = nil, ammoThread = nil, fastThread = nil, home = nil, holdCF = nil, lastTpPos = nil, voiding = false, wasBusy = false, reloadVoidUntil = 0, ammoActionAt = 0, equipAt = 0, noclipAt = 0, collide = setmetatable({}, { __mode = 'k' }), lastVoidPos = nil, fpdhOriginal = nil, rescueAt = 0, aaPhase = 0, } local targetAt, matchAt, matchCache, rangeAt, rangeCache, errors = 0, 0, false, 0, false, 0 local reportedErrors = {} -- ---------------------------------------------------------------- settings (all read live from the menu) local function tog(id, default) local toggle = Toggles and Toggles[id] if toggle == nil then return default end return toggle.Value == true end local function num(id, lo, hi, default) local option = Options and Options[id] local value = option and tonumber(option.Value) if value == nil or value ~= value then value = default end return math.clamp(value, lo, hi) end local function str(id, default) local option = Options and Options[id] if option and type(option.Value) == 'string' and option.Value ~= '' then return option.Value end return default end local function reportOnce(tag, err) errors = errors + 1 if reportedErrors[tag] then return end reportedErrors[tag] = true warn('[ZkxHub] ragebot ' .. tag .. ': ' .. tostring(err)) end -- ---------------------------------------------------------------- game handles (resolved lazily, never blocking) local FC, Utility, EnumLibrary, UseItemRemote, DuelController, VIM local START_SHOOTING = 'StartShooting' local lastResolve = 0 local function requireNode(root, ...) local node = root for _, name in ipairs({ ... }) do node = node and node:FindFirstChild(name) end if not node then return nil end local ok, module = pcall(require, node) return (ok and type(module) == 'table') and module or nil end local function ensureGame() if FC and UseItemRemote then return true end local now = os.clock() if now - lastResolve < 1 then return false end lastResolve = now if not FC then FC = requireNode(lp:FindFirstChild('PlayerScripts'), 'Controllers', 'FighterController') end if not Utility then Utility = requireNode(ReplicatedStorage, 'Modules', 'Utility') end if not EnumLibrary then EnumLibrary = requireNode(ReplicatedStorage, 'Modules', 'EnumLibrary') if EnumLibrary then local ok, token = pcall(function() return EnumLibrary:ToEnum('StartShooting') end) if ok and type(token) == 'string' then START_SHOOTING = token end end end if not UseItemRemote then local remotes = ReplicatedStorage:FindFirstChild('Remotes') local replication = remotes and remotes:FindFirstChild('Replication') local fighter = replication and replication:FindFirstChild('Fighter') local remote = fighter and fighter:FindFirstChild('UseItem') if remote then UseItemRemote = remote end end return FC ~= nil and UseItemRemote ~= nil end local function localFighter() return FC and FC.LocalFighter end local function equippedItem() local fighter = localFighter() return fighter and fighter.EquippedItem end local function itemGet(item, key) if not item then return nil end local ok, value = pcall(function() return item:Get(key) end) return ok and value or nil end local function getRoot(character) return character and character:FindFirstChild('HumanoidRootPart') end local function getVIM() if VIM then return VIM end local ok, manager = pcall(function() return Instance.new('VirtualInputManager') end) if ok and manager then VIM = manager end return VIM end local slotKey = { [1] = Enum.KeyCode.One, [2] = Enum.KeyCode.Two, [3] = Enum.KeyCode.Three, [4] = Enum.KeyCode.Four } local function pressKey(keyCode) task.spawn(function() local manager = getVIM() if not manager then return end pcall(function() manager:SendKeyEvent(true, keyCode, false, game) end) task.wait(0.03) pcall(function() manager:SendKeyEvent(false, keyCode, false, game) end) end) end -- ---------------------------------------------------------------- match / target helpers local function isShootingRange() local now = os.clock() if now - rangeAt < 0.5 then return rangeCache end rangeAt = now rangeCache = false local function matches(value) if value == nil then return false end local text = tostring(value):lower():gsub('[%s_%-]', '') return text:find('shootingrange', 1, true) ~= nil or text:find('firingrange', 1, true) ~= nil end for _, object in ipairs({ Workspace, lp }) do for _, attribute in ipairs({ 'Map', 'MapName', 'Mode', 'GameMode', 'Arena', 'Environment', 'EnvironmentName', 'ShootingRange', }) do local value = object:GetAttribute(attribute) if (value == true and attribute == 'ShootingRange') or matches(value) then rangeCache = true return true end end end return false end local function scanWeapon(player) local viewModels = Workspace:FindFirstChild('ViewModels') if not viewModels then return '' end for _, model in ipairs(viewModels:GetChildren()) do if model:IsA('Model') then local split = model.Name:find(' - ', 1, true) if split and model.Name:sub(1, split - 1) == player.Name then return model.Name:sub(split + 3):lower() end end end return '' end local function playerIsDead(player) local character = player and player.Character local humanoid = character and character:FindFirstChildOfClass('Humanoid') return not character or not humanoid or humanoid.Health <= 0 or not getRoot(character) end local function isInvincible(player) local character = player and player.Character local root = getRoot(character) if not root then return true end for _, object in ipairs(root:GetChildren()) do if object:IsA('Attachment') and object.Name == 'Attachment' then return true end end return character:FindFirstChild('InvincibilityParticles', true) ~= nil end local function isKatana(player) return scanWeapon(player):find('katana', 1, true) ~= nil end local function sameMatch(player) return player:GetAttribute('EnvironmentID') == lp:GetAttribute('EnvironmentID') end local function isTeammate(player) local root = getRoot(player.Character) if root and root:FindFirstChild('TeammateLabel') then return true end local mine, theirs = lp:GetAttribute('TeamID'), player:GetAttribute('TeamID') if mine ~= nil and theirs ~= nil then return mine == theirs end return lp.Team ~= nil and player.Team == lp.Team end local function isNearOtherMatch(position, ignore) if typeof(position) ~= 'Vector3' then return false end for _, player in ipairs(Players:GetPlayers()) do if player ~= lp and player ~= ignore and not sameMatch(player) then local root = getRoot(player.Character) if root and (root.Position - position).Magnitude <= 1000 then return true end end end return false end local function shouldSkip(player) if player == lp or playerIsDead(player) then return true end if not sameMatch(player) or isInvincible(player) then return true end local root = getRoot(player.Character) if root and isNearOtherMatch(root.Position, player) then return true end return root ~= nil and root:FindFirstChild('TeammateLabel') ~= nil end local function getBestTarget() local root = getRoot(lp.Character) if not root then return nil end local best, bestValue = nil, math.huge local lowestHealth = str('RBTM', 'Closest') == 'Lowest Health' local priority = tog('RBAP', true) for _, player in ipairs(Players:GetPlayers()) do if not shouldSkip(player) then local character = player.Character local targetRoot = getRoot(character) local humanoid = character and character:FindFirstChildOfClass('Humanoid') if targetRoot and humanoid then local value = lowestHealth and humanoid.Health or (targetRoot.Position - root.Position).Magnitude if priority then if tog('RBPV', true) and targetRoot.Position.Magnitude > 1000000 then value = value - 2000000000 end if tog('RBPA', true) and scanWeapon(player) ~= '' then value = value - 1000000000 end end if value < bestValue then bestValue, best = value, player end end end end return best end local function hasValidTarget() return state.target ~= nil and not playerIsDead(state.target) and not isInvincible(state.target) end local function shouldShoot() return hasValidTarget() and not isKatana(state.target) end local function anyEnemyAlive() for _, player in ipairs(Players:GetPlayers()) do if player ~= lp and sameMatch(player) and not playerIsDead(player) and not isTeammate(player) then return true end end return false end local function isLobby() local gui = lp:FindFirstChild('PlayerGui') local main = gui and gui:FindFirstChild('MainGui') local frame = main and main:FindFirstChild('MainFrame') local lobby = frame and frame:FindFirstChild('Lobby') local currency = lobby and lobby:FindFirstChild('Currency') return currency ~= nil and currency.Visible == true end local function getDuel() if not DuelController then DuelController = requireNode(lp:FindFirstChild('PlayerScripts'), 'Controllers', 'DuelController') end if DuelController and DuelController.GetDuel then local ok, duel = pcall(DuelController.GetDuel, DuelController, lp) if ok then return duel end end return nil end local function inMatch() local now = os.clock() if now - matchAt < 0.25 then return matchCache end matchAt = now local result = false if ensureGame() and not isLobby() and not isShootingRange() then local character = lp.Character local humanoid = character and character:FindFirstChildOfClass('Humanoid') if character and getRoot(character) and humanoid and humanoid.Health > 0 then result = getDuel() ~= nil or localFighter() ~= nil end end matchCache = result return result end -- ---------------------------------------------------------------- weapons / ammo local function itemIsReloading(item) if type(item) ~= 'table' then return false end local now = tick() local cooldown = rawget(item, '_reload_cooldown') if type(cooldown) == 'number' and now < cooldown then return true end local noAmmo = rawget(item, '_shoot_cooldown_no_ammo') return type(noAmmo) == 'number' and now < noAmmo end local function isEquipping() local item = equippedItem() local cooldown = type(item) == 'table' and rawget(item, '_equip_cooldown') or nil return type(cooldown) == 'number' and tick() < cooldown end local function isUsingShotgun() local item = equippedItem() local name = item and tostring(item.Name or ''):lower() or '' return name:find('shotgun', 1, true) ~= nil or name:find('shotkey', 1, true) ~= nil or name:find('broomstick', 1, true) ~= nil end local function equippedSlot() return tonumber(itemGet(equippedItem(), 'Slot')) end local function preferredSlot() local choice = str('RBPW', 'primary') return (choice == 'secondary' and 2) or (choice == 'melee' and 3) or 1 end local function autoEquipPreferred() if not tog('RBAE', true) then return end local want = preferredSlot() local slot = equippedSlot() if slot and slot ~= want and tick() >= state.ammoActionAt and tick() >= state.equipAt then state.equipAt = tick() + 0.5 pressKey(slotKey[want]) end end -- true while the weapon is busy (reloading, empty and swapping) local function handleAmmo() local fighter = localFighter() local item = fighter and fighter.EquippedItem if not fighter or not item then return false end local ammo = itemGet(item, 'Ammo') or 0 local slot = itemGet(item, 'Slot') or 1 local now = tick() local reloading = false pcall(function() reloading = fighter:Get('Reloading') == true end) if reloading or itemIsReloading(item) then state.ammoActionAt = math.max(state.ammoActionAt, now + 0.1) return true end if type(ammo) ~= 'number' or ammo > 0 then return false end if now < state.ammoActionAt then return true end if not tog('RBAS', true) then return true end local primary = preferredSlot() == 2 and 2 or 1 local secondary = primary == 1 and 2 or 1 if slot == primary then state.ammoActionAt = now + 0.45 pressKey(slotKey[secondary]) return true end if slot == secondary then state.ammoActionAt = now + 0.6 pressKey(slotKey[primary]) task.delay(0.18, function() if not state.active then return end local after = equippedItem() if after and (itemGet(after, 'Slot') or 1) == primary and (itemGet(after, 'Ammo') or 0) <= 0 then pressKey(Enum.KeyCode.R) end end) return true end state.ammoActionAt = now + 0.5 pressKey(Enum.KeyCode.R) return true end -- ---------------------------------------------------------------- firing local function encodeCFrame(cf) if Utility and Utility.EncodeCFrame then local ok, encoded = pcall(function() return Utility:EncodeCFrame(cf) end) if ok and encoded ~= nil then return encoded end end local rx, ry, rz = cf:ToOrientation() return { ['\0'] = cf.X, ['\1'] = cf.Y, ['\2'] = cf.Z, ['\3'] = rx, ['\4'] = ry, ['\5'] = rz } end local function buildShotData(fromPosition, part) local look = CFrame.new(fromPosition, part.Position) local data = { ['\1'] = { ['\0'] = encodeCFrame(look), ['\1'] = encodeCFrame(look), ['\2'] = part, ['\3'] = encodeCFrame(part.CFrame:ToObjectSpace(CFrame.new(part.Position))), }, } if tog('RBNS', true) then data['\2'] = true end return data end local function doFire(part) local fighter = localFighter() local item = fighter and fighter.EquippedItem if not item or not part or not UseItemRemote then return false end local camera = Workspace.CurrentCamera local fromPosition = state.lastTpPos or (camera and camera.CFrame.Position) or part.Position local any = false for _ = 1, math.floor(num('RBSA', 1, 10, 1)) do local fired = false local ammo = itemGet(item, 'Ammo') or 0 if type(ammo) == 'number' and ammo > 0 then local objectId = itemGet(item, 'ObjectID') if objectId then fired = pcall( function() UseItemRemote:FireServer(objectId, START_SHOOTING, buildShotData(fromPosition, part), nil) end ) end end if not fired and type(item.UseItem) == 'function' then fired = pcall(function() item:UseItem() end) end any = any or fired end return any end -- ---------------------------------------------------------------- movement helpers local function noclipTick() if not tog('RBNC', true) then return end local now = tick() if now - state.noclipAt < 0.2 then return end state.noclipAt = now local character = lp.Character if not character then return end for _, part in ipairs(character:GetDescendants()) do if part:IsA('BasePart') and part.CanCollide then if state.collide[part] == nil then state.collide[part] = true end part.CanCollide = false end end end local function restoreCollide() for part in pairs(state.collide) do pcall(function() part.CanCollide = true end) end table.clear(state.collide) end -- Throws the root far away every frame (several jumps per frame) so nothing can hit it. local function throwVoid(root) local humanoid = lp.Character and lp.Character:FindFirstChildOfClass('Humanoid') if not humanoid or humanoid.Health <= 0 then return end if humanoid:GetState() ~= Enum.HumanoidStateType.Physics then pcall(function() humanoid:ChangeState(Enum.HumanoidStateType.Physics) end) end local iterations = math.floor(num('RBVI', 1, 500, 500)) local minDistance = num('RBVN', 1, 500000, 1) local maxDistance = math.max(num('RBVD', 1, 500000, 50000), minDistance) for _ = 1, iterations do local base = state.lastVoidPos or root.Position local distance = minDistance + math.random() * (maxDistance - minDistance) local sx = math.random() < 0.5 and 1 or -1 local sy = math.random() < 0.5 and 1 or -1 local sz = math.random() < 0.5 and 1 or -1 local p = Vector3.new( base.X + sx * distance * math.random(), base.Y + sy * distance * math.random(), base.Z + sz * distance * math.random() ) if math.abs(p.X) > 5e6 or math.abs(p.Y) > 5e6 or math.abs(p.Z) > 5e6 then p = Vector3.new(math.random(-1000000, 1000000), math.random(-1000000, 1000000), math.random(-1000000, 1000000)) end root.CFrame = CFrame.new(p) state.lastVoidPos = p end root.AssemblyLinearVelocity = Vector3.zero root.AssemblyAngularVelocity = Vector3.zero end local function leaveVoid() if state.voiding then state.voiding = false state.lastVoidPos = nil local humanoid = lp.Character and lp.Character:FindFirstChildOfClass('Humanoid') if humanoid and humanoid:GetState() == Enum.HumanoidStateType.Physics and not tog('RBHV', true) then pcall(function() humanoid:ChangeState(Enum.HumanoidStateType.GettingUp) end) end end end -- ---------------------------------------------------------------- main step local function step(dt) if not state.active then return end if isShootingRange() then state.target = nil return end noclipTick() local root = getRoot(lp.Character) if not root then return end if not inMatch() then state.target = nil return end local now = tick() if state.target and (playerIsDead(state.target) or isInvincible(state.target)) then state.target = nil end if now - targetAt >= 0.05 or not state.target then targetAt = now local best = getBestTarget() if best then state.target = best end end if not state.target then if tog('RBVK', true) and not anyEnemyAlive() then state.voiding = true else leaveVoid() end return end local targetCharacter = state.target.Character local targetRoot = getRoot(targetCharacter) local head = targetCharacter and (targetCharacter:FindFirstChild('Head') or targetRoot) if not targetRoot or not head then state.target = nil return end if isNearOtherMatch(targetRoot.Position, state.target) then state.target = nil return end autoEquipPreferred() -- reloading / swapping: hide in the void for the duration (longer for shotguns) local busy = handleAmmo() or isEquipping() if busy then if not state.wasBusy then state.wasBusy = true if isUsingShotgun() then state.reloadVoidUntil = math.max(state.reloadVoidUntil, now + num('RBSV', 5, 30, 5)) end end state.reloadVoidUntil = math.max(state.reloadVoidUntil, now + 0.05) else state.wasBusy = false end if tog('RBVR', true) and now < state.reloadVoidUntil then state.voiding = true return end leaveVoid() local targetPosition = targetRoot.Position + Vector3.new(0, num('RBOY', -5, 50, 1), 0) if isNearOtherMatch(targetPosition, state.target) then return end local faceCF if (head.Position - targetPosition).Magnitude > 0.1 then faceCF = CFrame.new(targetPosition, head.Position) else faceCF = CFrame.new(targetPosition) end if tog('RBAA', false) then state.aaPhase = state.aaPhase + dt * 20 faceCF = faceCF * CFrame.Angles(0, math.rad(math.sin(state.aaPhase) * 70), 0) end root.AssemblyLinearVelocity = Vector3.zero root.AssemblyAngularVelocity = Vector3.zero root.CFrame = faceCF state.lastTpPos = targetPosition state.holdCF = faceCF end local function heartbeat() if not state.active then return end local root = getRoot(lp.Character) local humanoid = lp.Character and lp.Character:FindFirstChildOfClass('Humanoid') if not root or not humanoid or humanoid.Health <= 0 then return end if state.voiding then local ok, err = pcall(throwVoid, root) if not ok then reportOnce('void', err) end return end if state.holdCF and tog('RBHV', true) then if not humanoid.PlatformStand then humanoid.PlatformStand = true end root.AssemblyLinearVelocity = Vector3.zero root.AssemblyAngularVelocity = Vector3.zero root.CFrame = state.holdCF end local now = tick() if now >= state.rescueAt then state.rescueAt = now + 0.1 if root.Position.Y < -100 and not state.voiding then root.CFrame = CFrame.new(root.Position.X, 100, root.Position.Z) root.AssemblyLinearVelocity = Vector3.zero end end end -- ---------------------------------------------------------------- fast shoot (cooldowns, recoil, spread) local infoCache = setmetatable({}, { __mode = 'k' }) local FAST_KEYS = { 'ShootCooldown', 'QuickShotCooldown', 'SpinCooldown', 'DashCooldown', 'Cooldown', 'BuildCooldown', 'AttackCooldown', 'HeavyAttackCooldown', 'BurstCooldown', } local function remember(info, key) if info[key] == nil then return nil end local saved = infoCache[info] if not saved then saved = {} infoCache[info] = saved end if saved[key] == nil then saved[key] = info[key] end return saved[key] end local function applyFast(info, fast) local recoil = remember(info, 'ShootRecoil') if recoil ~= nil then info.ShootRecoil = fast and 0 or recoil end local spread = remember(info, 'ShootSpread') if spread ~= nil then info.ShootSpread = fast and 0 or spread end local speed = remember(info, 'ProjectileSpeed') if speed ~= nil then info.ProjectileSpeed = fast and 99999999 or speed end for _, key in ipairs(FAST_KEYS) do local original = remember(info, key) if original ~= nil then info[key] = fast and 0 or original end end end local function restoreFast() for info in pairs(infoCache) do pcall(applyFast, info, false) end end local function fastTick() if not tog('RBFS', true) then restoreFast() return end local fighter = localFighter() local items = type(fighter) == 'table' and rawget(fighter, 'Items') or nil if type(items) == 'table' then for _, item in pairs(items) do local info = type(item) == 'table' and item.Info or nil if type(info) == 'table' then pcall(applyFast, info, true) end end end end -- ---------------------------------------------------------------- start / stop local function startAmmoLoop() if state.ammoThread then return end state.ammoThread = task.spawn(function() while state.active do if not isShootingRange() and state.target ~= nil then if not handleAmmo() and shouldShoot() then local character = state.target.Character local head = character and (character:FindFirstChild('Head') or getRoot(character)) if head then pcall(doFire, head) end end end task.wait(num('RBTD', 0.01, 1, 0.04)) end state.ammoThread = nil end) end local function startFastLoop() if state.fastThread then return end state.fastThread = task.spawn(function() while state.active do pcall(fastTick) task.wait(0.2) end restoreFast() state.fastThread = nil end) end function Rage.enable() if state.active then return end ensureGame() local root = getRoot(lp.Character) state.home = root and root.CFrame or nil state.active = true state.target, state.holdCF, state.lastTpPos, state.voiding, state.wasBusy = nil, nil, nil, false, false state.reloadVoidUntil, state.ammoActionAt, state.equipAt = 0, 0, 0 pcall(function() state.fpdhOriginal = Workspace.FallenPartsDestroyHeight Workspace.FallenPartsDestroyHeight = 0 / 0 end) startAmmoLoop() startFastLoop() state.hbConn = RunService.Heartbeat:Connect(function() local ok, err = pcall(heartbeat) if not ok then reportOnce('heartbeat', err) end end) state.conn = RunService.Stepped:Connect(function(_, dt) local ok, err = pcall(step, dt) if not ok then reportOnce('step', err) end end) state.charConn = lp.CharacterAdded:Connect(function(character) state.target, state.holdCF, state.lastTpPos, state.voiding = nil, nil, nil, false local hrp = character:WaitForChild('HumanoidRootPart', 10) if hrp then state.home = hrp.CFrame end end) end function Rage.disable() if not state.active then return end state.active = false if state.conn then state.conn:Disconnect() state.conn = nil end if state.hbConn then state.hbConn:Disconnect() state.hbConn = nil end if state.charConn then state.charConn:Disconnect() state.charConn = nil end state.target, state.holdCF, state.lastTpPos, state.voiding = nil, nil, nil, false state.wasBusy, state.reloadVoidUntil, state.lastVoidPos = false, 0, nil restoreCollide() restoreFast() local root = getRoot(lp.Character) if root and state.home then root.AssemblyLinearVelocity = Vector3.zero root.AssemblyAngularVelocity = Vector3.zero root.CFrame = state.home end state.home = nil local humanoid = lp.Character and lp.Character:FindFirstChildOfClass('Humanoid') if humanoid then pcall(function() humanoid.PlatformStand = false end) if humanoid:GetState() == Enum.HumanoidStateType.Physics then pcall(function() humanoid:ChangeState(Enum.HumanoidStateType.GettingUp) end) end end pcall(function() if state.fpdhOriginal ~= nil then Workspace.FallenPartsDestroyHeight = state.fpdhOriginal end state.fpdhOriginal = nil end) end function Rage.unload() Rage.disable() restoreFast() end Rage.state = state -- ---------------------------------------------------------------- bridge (same surface the rest of the script uses) local wasEnabled = false local function readRageToggle() local toggle = Toggles and Toggles['P8S4T1'] if not (toggle and toggle.Value == true) then return false end local keyPicker = Options and Options['P8S4T1K'] if keyPicker and type(keyPicker.GetState) == 'function' then return keyPicker:GetState() == true end return true end RivalsRuntimeBridge.ZkhHubRagebot = Rage RivalsRuntimeBridge.SetRageConfigKey = function() end RivalsRuntimeBridge.UpdateZkhHubRagebot = function() local should = readRageToggle() if should ~= wasEnabled then wasEnabled = should if should then pcall(Rage.enable) else pcall(Rage.disable) end end end RivalsRuntimeBridge.ResetZkhHubRagebot = function() wasEnabled = false pcall(Rage.disable) end RivalsRuntimeBridge.DestroyZkhHubRagebot = function() wasEnabled = false pcall(Rage.unload) end end, } end -- ====================================================================== -- Error reporter -- ====================================================================== __ZkhHub_shared.error_reporter = function() local DUPLICATE_COOLDOWN_SECONDS = 30 local MAX_NOTIFICATIONS_PER_MINUTE = 6 local module = {} local game_context = 'Unknown' local last_seen_at = {} local recent_notifications = {} function module.set_game(name) game_context = name or 'Unknown' end local function is_rate_limited() local now = os.clock() local cutoff = now - 60 local recent = {} for _, t in ipairs(recent_notifications) do if t > cutoff then table.insert(recent, t) end end recent_notifications = recent return #recent_notifications >= MAX_NOTIFICATIONS_PER_MINUTE end local function truncate(str, max) if #str <= max then return str end return str:sub(1, max - 3) .. '...' end function module.report(err_msg, traceback, feature) local message = tostring(err_msg) local key = (feature or '?') .. '|' .. message local now = os.clock() local last = last_seen_at[key] if last and (now - last) < DUPLICATE_COOLDOWN_SECONDS then return end last_seen_at[key] = now local label = '[' .. game_context .. ']' if feature then label = label .. '[' .. tostring(feature) .. ']' end warn(label .. ' ' .. message) if traceback then warn(tostring(traceback)) end if is_rate_limited() then return end table.insert(recent_notifications, now) if Library and type(Library.Notify) == 'function' then pcall( function() Library:Notify({ Title = feature and ('Error: ' .. tostring(feature)) or 'Error', Description = truncate(message, 200) .. ' (see console for traceback)', Time = 8, }) end ) end end return module end -- ====================================================================== -- Safe executor wrappers -- ====================================================================== local function safe_get_gc(callback) local gc = setmetatable(getgc(true), { __mode = 'v' }) for _, value in pairs(gc) do local ok, result = pcall(callback, value) if ok and result then break end end end local function safe_require(module) return require(cloneref(module)) end local function safe_fire_server(remote, ...) cloneref(remote):FireServer(...) end local function safe_invoke_server(remote, ...) return cloneref(remote):InvokeServer(...) end local function safe_fire_signal(signal, ...) firesignal(cloneref(signal), ...) end local function safe_get_attribute(instance, attr) return cloneref(instance):GetAttribute(attr) end local function safe_set_attribute(instance, attr, value) cloneref(instance):SetAttribute(attr, value) end local function safe_find(parent, name) return cloneref(parent):FindFirstChild(name) end local function safe_is_a(instance, class) return cloneref(instance):IsA(class) end -- ====================================================================== -- Game section (Rivals) -- ====================================================================== local __game_ok, __game_err = xpcall(function() local SaveManager, ThemeManager __ZkhHub_genv.ZkxLoad = function(fraction, status) local splash = __ZkhHub_genv.__ZkxSplash if splash then if status then pcall(splash.SetStatus, status) end if fraction then pcall(splash.Progress, fraction) end pcall(task.wait) end end if game.GameId == Games['RIVALS'].ID and Games['RIVALS'].State then local splashWanted = true -- Silent Execute (auto_show off) keeps the whole start-up quiet: no loading screen either pcall(function() local saved = RequireSharedModule('script_paths').load_ui_settings() splashWanted = saved.splash ~= false and saved.auto_show ~= false if saved.auto_show == false then print('[ZkxHub] Silent Execute is on: loading screen skipped') end end) if splashWanted then local okKit, Kit = pcall(RequireSharedModule, 'zkx_ui_kit') if okKit then local okSplash, made = pcall(Kit.Splash, { Logo = Kit.LogoAsset, Text = 'Loading menu library' }) if okSplash then __ZkhHub_genv.__ZkxSplash = made end end end end Library = RequireSharedModule('obsidian_library') -- (ZkxVader build) base look: ZkxHub purple on the Vaderhaxx grays Library.Scheme.AccentColor = Color3.fromRGB(125, 85, 255) Library.Palette.accent = Library.Scheme.AccentColor Library.ColorMigrations = { BackgroundColor = { from = Color3.fromHex('0f0f0f'), to = Color3.fromHex('151515') }, MainColor = { from = Color3.fromHex('191919'), to = Color3.fromHex('2e2e2e') }, OutlineColor = { from = Color3.fromHex('282828'), to = Color3.fromHex('383838') }, } SaveManager = RequireSharedModule('obsidian_save_manager') ThemeManager = RequireSharedModule('obsidian_theme_manager') __ZkhHub_genv.ZkxLoad(0.08, 'Preparing settings') Library.NotifyOnError = false Library.ShowCustomCursor = false do local OriginalNotify = rawget(Library, 'Notify') if type(OriginalNotify) == 'function' then Library.Notify = function(self, ...) __ZkxHubElevateIdentity() local args = table.pack(...) local ok, result = pcall(function() return OriginalNotify(self, table.unpack(args, 1, args.n)) end) if ok then return result end return nil end end end -- Executor support gate do local Caps = __ZkhHub_genv.ZkxHubCaps if type(Caps) == 'table' then local NotifiedFeatures = {} Caps.gate = function(feature, ...) local missing = nil for _, name in ipairs({ ... }) do if not Caps.has(name) then missing = missing and (missing .. ', ' .. name) or name end end if not missing then return true end if not NotifiedFeatures[feature] then NotifiedFeatures[feature] = true pcall( function() Library:Notify({ Title = 'Executor Support', Description = feature .. " won't work on this executor -- it doesn't have " .. missing .. '.', Time = 8, }) end ) end return false end end end local Toggles = Library.Toggles local Options = Library.Options local ScriptPaths = RequireSharedModule('script_paths') SaveManager:SetLibrary(Library) SaveManager:SetIgnoreIndexes({ 'MenuKeybind' }) SaveManager:SetFolder(ScriptPaths.storage_root()) pcall(ScriptPaths.ensure_runnable_copy) ThemeManager:SetLibrary(Library) ThemeManager:SetFolder(ScriptPaths.storage_root()) ErrorReporter = RequireSharedModule('error_reporter') function Library:SafeCallback(Func, ...) if not (Func and typeof(Func) == 'function') then return end local args = table.pack(...) local Result = table.pack(xpcall(function() return Func(table.unpack(args, 1, args.n)) end, function(err) local traceback = debug.traceback(tostring(err), 2) if ErrorReporter and type(ErrorReporter.report) == 'function' then ErrorReporter.report(tostring(err), traceback, 'library_callback') end return err end)) if not Result[1] then return nil end return table.unpack(Result, 2, Result.n) end local function InstallUnloadWrapper() function Library:Unload() if Library.Unloaded then return end local previousIdentity = nil pcall(function() if type(getthreadidentity) == 'function' then previousIdentity = getthreadidentity() end end) if type(setthreadidentity) == 'function' then pcall(setthreadidentity, 7) end for Index = #Library.Signals, 1, -1 do local Connection = table.remove(Library.Signals, Index) pcall(function() if Connection and Connection.Connected then Connection:Disconnect() end end) end for _, Callback in ipairs(Library.UnloadSignals or {}) do Library:SafeCallback(Callback) end Library.Unloaded = true if Library.ActiveLoading then pcall(function() Library.ActiveLoading:Destroy() end) Library.ActiveLoading = nil end if Library.ScreenGui then pcall(function() Library.ScreenGui:Destroy() end) Library.ScreenGui = nil end pcall(function() __ZkhHub_genv.Library = nil end) if previousIdentity ~= nil and type(setthreadidentity) == 'function' then pcall(setthreadidentity, previousIdentity) end end end InstallUnloadWrapper() -- Anti-AFK do local Players = game:GetService('Players') local LocalPlayer = Players.LocalPlayer local Token = {} local Slot = __ZkhHub_genv.__ZkxHubAntiAfk if type(Slot) == 'table' and Slot.Connection then pcall(function() Slot.Connection:Disconnect() end) end local function Nudge() pcall(function() local Vim = Instance.new('VirtualInputManager') Vim:SendKeyEvent(true, Enum.KeyCode.F15, false, game) Vim:SendKeyEvent(false, Enum.KeyCode.F15, false, game) end) end if LocalPlayer then local Connection = LocalPlayer.Idled:Connect(Nudge) __ZkhHub_genv.__ZkxHubAntiAfk = { Token = Token, Connection = Connection, } task.spawn(function() while true do task.wait(300) local Live = __ZkhHub_genv.__ZkxHubAntiAfk if type(Live) ~= 'table' or Live.Token ~= Token then return end Nudge() end end) end end local queue_on_teleport = (type(queue_on_teleport) == 'function' and queue_on_teleport) or (type(queueonteleport) == 'function' and queueonteleport) or (type(syn) == 'table' and type(syn.queue_on_teleport) == 'function' and syn.queue_on_teleport) or function() end local GameId = game.GameId local GameName = 'Unknown' for i, v in pairs(Games) do if GameId == v.ID then GameName = i break end end local TitleTemplate = ScriptPaths.format_title(GameName) local AutoShowConfig = ScriptPaths.ensure_auto_show_file() ErrorReporter.set_game(GameName) if GameId == Games['RIVALS'].ID and Games['RIVALS'].State then local ConnectionRegistry = RequireSharedModule('connection_registry') local UISettings = RequireSharedModule('ui_settings') local GameBootstrap = RequireSharedModule('game_bootstrap') __ZkhHub_genv.ZkxLoad(0.14, 'Creating window') -- ====================================================================== -- Services, player refs and game modules -- ====================================================================== -- Services local Players = game:GetService('Players') local ReplicatedStorage = game:GetService('ReplicatedStorage') local RunService = game:GetService('RunService') local UserInputService = game:GetService('UserInputService') local Workspace = game:GetService('Workspace') local Lighting = game:GetService('Lighting') -- Player refs local LP = Players.LocalPlayer local CurrentRivalsPlace = game['Place' .. 'Id'] local RIVALS_SHARED_MATCH_PLACE = 129604661913557 local AimbotInputState = { LeftMouse = false, RightMouse = false, TouchCount = 0, FreecamActive = false, } AimbotInputState.SyntheticInputBlocked = function() if AimbotInputState.FreecamActive then return true end if Library and Library.Toggled then return true end if game:GetService('GuiService').MenuIsOpen then return true end if UserInputService:GetFocusedTextBox() ~= nil then return true end return false end local AimbotTriggerbotLastShotAt = 0 local MeleeFastFire = { Enabled = true, IntervalSec = 0, LastShotAt = 0, RagebotStuds = 10, AttackMaxStuds = 15 } local AimbotVisibilityCache = setmetatable({}, { __mode = 'k' }) local AIMBOT_VISIBLE_CACHE_WINDOW = 1 / 30 local ESP_HIDDEN_CACHE_WINDOW = 1 / 30 local AIMBOT_HIDDEN_CACHE_WINDOW = 1 / 60 local TRIGGERBOT_HIDDEN_CACHE_WINDOW = 0 local AIMBOT_OFFSCREEN_SELECTION_PENALTY = 1e6 local ESP_VISIBILITY_CACHE_PROFILE = { Id = 'ESP', HiddenWindow = ESP_HIDDEN_CACHE_WINDOW, VisibleWindow = AIMBOT_VISIBLE_CACHE_WINDOW, } local AIMBOT_VISIBILITY_CACHE_PROFILE = { Id = 'AIMBOT', HiddenWindow = AIMBOT_HIDDEN_CACHE_WINDOW, VisibleWindow = 0, } local TRIGGERBOT_VISIBILITY_CACHE_PROFILE = { Id = 'TRIGGERBOT', HiddenWindow = TRIGGERBOT_HIDDEN_CACHE_WINDOW, VisibleWindow = 0, } -- ====================================================================== -- Aimbot state: silent aim, camera aim, triggerbot -- ====================================================================== local AimbotSilentState = { Item = nil, LastShot = nil, LastShotChangedAt = 0, ShotSignalAt = 0, ProjectileShotAt = 0, HookTarget = nil, HookTargetCapturedAt = 0, ShotPrimeDepth = 0, } local AimbotSilentConnections = { Shot = nil, ProjectileShot = nil, HookedItem = nil, OriginalStartShooting = nil, WrappedStartShooting = nil, TriggerbotHoldingMouse = false, TriggerbotHoldX = nil, TriggerbotHoldY = nil, TriggerbotTarget = nil, TriggerbotTargetAcquiredAt = 0, CameraAimRandomTarget = nil, CameraAimRandomPart = nil, } local AimbotStatusNotification = nil local AimbotStatusKey = nil local AimbotStatusDescription = nil local RuntimeIssueNotification = nil local RuntimeIssueDescription = nil local RuntimeIssueReportedAt = {} local RuntimeIssueNotificationUnavailable = false local RIVALS_RUNTIME_ERROR_DEDUPE_WINDOW = 2 local UpdateRivalsPickupFeatures local function DestroyRuntimeIssueNotification() if RuntimeIssueNotification and type(RuntimeIssueNotification.Destroy) == 'function' then pcall(function() RuntimeIssueNotification:Destroy() end) elseif RuntimeIssueNotification and type(RuntimeIssueNotification.Remove) == 'function' then pcall(function() RuntimeIssueNotification:Remove() end) end RuntimeIssueNotification = nil RuntimeIssueDescription = nil RuntimeIssueNotificationUnavailable = false end local function TryShowRivalsRuntimeIssueNotification(description) if RuntimeIssueNotificationUnavailable or not Library or type(Library.Notify) ~= 'function' then return end if RuntimeIssueNotification then if RuntimeIssueDescription ~= description then local ok = pcall(function() RuntimeIssueNotification:ChangeDescription(description) end) if not ok then RuntimeIssueNotification = nil RuntimeIssueNotificationUnavailable = true end end return end local ok, notification = pcall( function() return Library:Notify({ Title = 'RIVALS', Description = description, Persist = true, }) end ) if ok then RuntimeIssueNotification = notification else RuntimeIssueNotification = nil RuntimeIssueNotificationUnavailable = true end end local function ReportRivalsRuntimeIssue(feature, tracebackMessage) local tracebackText = tostring(tracebackMessage or 'Unknown error') local errorMessage = tracebackText:match('([^\n]+)') or tracebackText local description = string.format('%s: %s', tostring(feature or 'runtime'), errorMessage) local now = tick() local lastReportedAt = RuntimeIssueReportedAt[description] if lastReportedAt and (now - lastReportedAt) < RIVALS_RUNTIME_ERROR_DEDUPE_WINDOW then return end RuntimeIssueReportedAt[description] = now TryShowRivalsRuntimeIssueNotification(description) RuntimeIssueDescription = description if ErrorReporter and type(ErrorReporter.report) == 'function' then pcall( function() ErrorReporter.report(errorMessage, tracebackText, string.format('rivals_%s', tostring(feature or 'runtime'))) end ) end end local function GuardRivalsCallback(feature, callback) return function(...) local args = table.pack(...) local ok, result = xpcall( function() return callback(table.unpack(args, 1, args.n)) end, function(err) return debug.traceback(string.format('[%s] %s', tostring(feature), tostring(err)), 2) end ) if not ok then ReportRivalsRuntimeIssue(feature, result) return nil end return result end end local Char, Humanoid, HumanoidRootPart local function InitCharacter() Char = LP.Character if not Char then Humanoid = nil HumanoidRootPart = nil return false end Humanoid = Char:FindFirstChildOfClass('Humanoid') HumanoidRootPart = Char:FindFirstChild('HumanoidRootPart') return Humanoid ~= nil and HumanoidRootPart ~= nil end local function IsAimbotCameraBoundToLocalCharacter(camera) if not camera or not Char or LP.Character ~= Char then return false end local subject = camera.CameraSubject if not subject then return false end return subject == Char or subject:IsDescendantOf(Char) end local function IsAimbotCameraReady() local camera = Workspace.CurrentCamera if not camera then return false end if not InitCharacter() or LP.Character ~= Char then return false end if not Humanoid or Humanoid.Health <= 0 then return false end if HumanoidRootPart and HumanoidRootPart.Anchored then return false end if not IsAimbotCameraBoundToLocalCharacter(camera) then return false end return true end local PlayerDataController = nil local EnumLibrary = nil local FighterController = nil local EnemyController = nil local ReplicatedStateReady = false local ReplicatedStateBootStarted = false local WeaponInfoCache = {} local WeaponInfoConnections = {} local WeaponInfoTrackingConnected = false local WeaponInfoTrackingRetryPending = false local TrackedFightersByPlayer = {} local FighterDataCache = { Cache = {}, Connections = {}, LocalDuel = { IsInDuel = false, IsInShootingRange = false, AttachedFighter = nil, DuelConn = nil, ShootingConn = nil, Seeded = false, }, OnLocalDuelChanged = nil, LocalLoadout = { EquippedItem = nil, Seeded = false, }, } local TrackedRangeTargets = {} local TrackedPracticeDummies = {} local TrackedEnemies = setmetatable({}, { __mode = 'k' }) local AimbotPlayerTrackingConnected = false local AimbotEnemyTrackingConnected = false local LocalFighterController = nil local Connections = nil local DEFAULT_ESP_MAX_DISTANCE = 1000 local DEFAULT_ESP_TEXT_SIZE = 16 local PLAYER_CARD_THEME = { HorizontalBarWidth = 48, HorizontalBarOffsetY = 0, HorizontalBarTextPadding = 0, HorizontalBarTextPaddingY = 0, NameOffsetY = -2, BarTopGap = 2, BarHeight = 17, BarRadius = 11, VerticalBarTextGap = 4, MinLift = 20, MaxLift = 50, FrameColor = Color3.fromRGB(4, 5, 7), NameColor = Color3.fromRGB(252, 252, 252), MetaColor = Color3.fromRGB(196, 203, 212), BarTextColor = Color3.fromRGB(248, 250, 252), ShadowColor = Color3.fromRGB(8, 10, 14), BarBackgroundColor = Color3.fromRGB(20, 24, 31), BarLowColor = Color3.fromRGB(233, 86, 74), BarMidColor = Color3.fromRGB(244, 210, 72), BarHighColor = Color3.fromRGB(100, 255, 50), BarBackgroundTransparency = 0.85, BarInset = 1, BarTextInset = 8, ArrowTransparency = 0.9, ArrowThickness = 2, ArrowLength = 18, ArrowWidth = 8, ArrowEdgeScale = 0.35, LookLineTransparency = 0.42, ThrowableTextTransparency = 0.95, } PLAYER_CARD_THEME.Classic = { BoxOutlineColor = Color3.fromRGB(4, 5, 7), BoxOutlineTransparency = 0.82, BoxOutlineThickness = 3, BoxThickness = 1, BarWidth = 4, BarHeight = 4, BarGap = 3, BarInset = 1, SmoothSpeed = 24, ReactiveLowColor = Color3.new(1, 1, 1), } local AimbotFovCircle = Drawing.new('Circle') AimbotFovCircle.Filled = false AimbotFovCircle.NumSides = 48 AimbotFovCircle.Thickness = 1.5 AimbotFovCircle.Transparency = 0.72 AimbotFovCircle.Color = Color3.fromRGB(255, 70, 70) AimbotFovCircle.Visible = false local MODEL_BOUNDS_SIGNS = { -1, 1 } local RivalsRuntimeBridge = {} RivalsRuntimeBridge.PlayerStatusInfoCache = {} RivalsRuntimeBridge.EspRankProfileCache = nil RivalsRuntimeBridge.ESPClassic = {} RivalsRuntimeBridge.EspPreviewState = nil -- ====================================================================== -- Weapon mods: attack speed, reload, no spread, recoil, melee reach -- ====================================================================== local RivalsModsState = { GunModule = nil, MeleeModule = nil, GunbladeModule = nil, CameraController = nil, CameraShakeOriginalEnabled = nil, CameraThirdPersonCaptured = false, CameraThirdPersonOriginal = nil, CameraViewModelOriginal = nil, MotionSpringValues = { _sliding_spring = 0, _sprinting_spring = 0, _bobbing_speed_spring = 0, _bobbing_value_spring = Vector2.zero, _landing_spring = 0, _jump_spring = 0, _tilt_spring = Vector2.zero, _impulse_position_spring = Vector3.zero, _recoil_spring = Vector3.zero, _unrecoil_spring = Vector3.zero, }, InfoModifierFighter = nil, InfoModifierItemAdded = nil, InfoModifierItemRemoved = nil, OriginalItemInfo = setmetatable({}, { __mode = 'k' }), PendingEnsureHooks = false, NoSpreadClientItem = nil, NoSpreadRemote = nil, ZkhHubInputHookPrototype = nil, ZkhHubInputHook = nil, NoSpreadController = nil, NoSpreadPlayerContext = nil, GrenadeFuseController = nil, OriginalGunStartShooting = nil, OriginalMeleeStartShooting = nil, OriginalGunbladeStartShooting = nil, OriginalGunStartReloading = nil, OriginalGunRecoil = nil, OriginalGunGetAimSpeed = nil, OriginalGunEquip = nil, } local function CallGameAtIdentity2(fn, ...) local prev = type(getthreadidentity) == 'function' and getthreadidentity() or nil if prev ~= nil and prev ~= 2 and type(setthreadidentity) == 'function' then pcall(setthreadidentity, 2) end local args = table.pack(...) local results = table.pack(pcall(fn, table.unpack(args, 1, args.n))) if prev ~= nil and prev ~= 2 and type(setthreadidentity) == 'function' then pcall(setthreadidentity, prev) end return results end local function CallGameOrError(fn, ...) local results = CallGameAtIdentity2(fn, ...) if not results[1] then error(results[2], 0) end return table.unpack(results, 2, results.n) end local function BlendColor(color, target, alpha) return Color3.new( color.R + ((target.R - color.R) * alpha), color.G + ((target.G - color.G) * alpha), color.B + ((target.B - color.B) * alpha) ) end local function CreateRoundedRect() local rect = { horizontal = Drawing.new('Square'), vertical = Drawing.new('Square'), tl = Drawing.new('Circle'), tr = Drawing.new('Circle'), bl = Drawing.new('Circle'), br = Drawing.new('Circle'), } rect.horizontal.Filled = true rect.horizontal.Visible = false rect.vertical.Filled = true rect.vertical.Visible = false for _, key in ipairs({ 'tl', 'tr', 'bl', 'br' }) do local corner = rect[key] corner.Filled = true corner.NumSides = 18 corner.Visible = false end return rect end local function HideRoundedRect(rect) rect.horizontal.Visible = false rect.vertical.Visible = false rect.tl.Visible = false rect.tr.Visible = false rect.bl.Visible = false rect.br.Visible = false end local function RemoveRoundedRect(rect) pcall(function() rect.horizontal:Remove() end) pcall(function() rect.vertical:Remove() end) pcall(function() rect.tl:Remove() end) pcall(function() rect.tr:Remove() end) pcall(function() rect.bl:Remove() end) pcall(function() rect.br:Remove() end) end local function CreateArrowIndicator() local arrow = { left = Drawing.new('Line'), right = Drawing.new('Line'), base = Drawing.new('Line'), } for _, key in ipairs({ 'left', 'right', 'base' }) do local segment = arrow[key] segment.Thickness = PLAYER_CARD_THEME.ArrowThickness segment.Transparency = PLAYER_CARD_THEME.ArrowTransparency segment.Visible = false end return arrow end local function HideArrowIndicator(arrow) arrow.left.Visible = false arrow.right.Visible = false arrow.base.Visible = false end local function RemoveArrowIndicator(arrow) pcall(function() arrow.left:Remove() end) pcall(function() arrow.right:Remove() end) pcall(function() arrow.base:Remove() end) end local function DrawArrowIndicator(arrow, center, direction, color, scale) local magnitude = direction.Magnitude if magnitude <= 0 then HideArrowIndicator(arrow) return end scale = scale or 1 local arrowLength = PLAYER_CARD_THEME.ArrowLength * scale local arrowWidth = PLAYER_CARD_THEME.ArrowWidth * scale local arrowThickness = math.max(PLAYER_CARD_THEME.ArrowThickness * scale, 1) local forward = direction / magnitude local right = Vector2.new(-forward.Y, forward.X) local tip = center + (forward * arrowLength * 0.5) local baseCenter = center - (forward * arrowLength * 0.5) local leftPoint = baseCenter - (right * arrowWidth) local rightPoint = baseCenter + (right * arrowWidth) arrow.left.From = leftPoint arrow.left.To = tip arrow.right.From = tip arrow.right.To = rightPoint arrow.base.From = rightPoint arrow.base.To = leftPoint for _, key in ipairs({ 'left', 'right', 'base' }) do local segment = arrow[key] segment.Color = color segment.Thickness = arrowThickness segment.Visible = true end end local function SetDrawingVisible(drawable, visible) if drawable.Visible ~= visible then drawable.Visible = visible end end local function SetDrawingTextState(drawable, visible, textValue, textSize, centered, color, font) SetDrawingVisible(drawable, visible) if not visible then return end if drawable.Text ~= textValue then drawable.Text = textValue end local roundedSize = math.round(textSize) if drawable.Size ~= roundedSize then drawable.Size = roundedSize end if drawable.Center ~= centered then drawable.Center = centered end if drawable.Color ~= color then drawable.Color = color end if font ~= nil and drawable.Font ~= font then drawable.Font = font end end local function SetRoundedRect(rect, x, y, width, height, radius, color, transparency) if width <= 0 or height <= 0 then HideRoundedRect(rect) return end local r = math.floor(math.max(0, math.min(radius, width * 0.5, height * 0.5))) if r == 0 then rect.horizontal.Color = color rect.horizontal.Transparency = transparency rect.horizontal.Position = Vector2.new(x, y) rect.horizontal.Size = Vector2.new(width, height) rect.horizontal.Visible = true rect.vertical.Visible = false rect.tl.Visible = false rect.tr.Visible = false rect.bl.Visible = false rect.br.Visible = false return end local innerWidth = math.max(width - (r * 2), 0) local innerHeight = math.max(height - (r * 2), 0) rect.horizontal.Color = color rect.horizontal.Transparency = transparency rect.horizontal.Visible = innerWidth > 0 if innerWidth > 0 then rect.horizontal.Position = Vector2.new(x + r, y) rect.horizontal.Size = Vector2.new(innerWidth, height) end rect.vertical.Color = color rect.vertical.Transparency = transparency rect.vertical.Visible = innerHeight > 0 if innerHeight > 0 then rect.vertical.Position = Vector2.new(x, y + r) rect.vertical.Size = Vector2.new(width, innerHeight) end local showCorners = r > 0 for _, key in ipairs({ 'tl', 'tr', 'bl', 'br' }) do local corner = rect[key] corner.Color = color corner.Transparency = transparency corner.Visible = showCorners if showCorners then corner.Radius = r end end if showCorners then rect.tl.Position = Vector2.new(x + r, y + r) rect.tr.Position = Vector2.new(x + width - r, y + r) rect.bl.Position = Vector2.new(x + r, y + height - r) rect.br.Position = Vector2.new(x + width - r, y + height - r) end end RivalsRuntimeBridge.ModuleIssues = {} RivalsRuntimeBridge.BootIssue = nil -- Game modules come from require(). Some executors only hand out the live module cache at LocalScript identity, so a failed -- require is retried there once. Failures are remembered (and not retried every frame) so they can be reported instead of -- silently switching features off. function RivalsRuntimeBridge.RequireGameModule(moduleScript, label) local issues = RivalsRuntimeBridge.ModuleIssues local issue = issues[label] if issue and os.clock() - issue.at < 5 then return nil end local ok, value = pcall(require, moduleScript) if ok and type(value) == 'table' then issues[label] = nil return value end local failure = ok and ('returned ' .. typeof(value)) or tostring(value) if type(getthreadidentity) == 'function' and type(setthreadidentity) == 'function' then local previous = getthreadidentity() pcall(setthreadidentity, 2) local retryOk, retryValue = pcall(require, moduleScript) pcall(setthreadidentity, previous) if retryOk and type(retryValue) == 'table' then issues[label] = nil return retryValue end end issues[label] = { at = os.clock(), err = failure } return nil end local function ResolvePlayerDataController() if PlayerDataController then return PlayerDataController end local playerScripts = LP:WaitForChild('PlayerScripts') local controllers = playerScripts:WaitForChild('Controllers') local playerDataControllerModule = controllers:WaitForChild('PlayerDataController') local controller = RivalsRuntimeBridge.RequireGameModule(playerDataControllerModule, 'PlayerDataController') if not controller then return nil end PlayerDataController = controller return PlayerDataController end local function IsPlayerDataControllerReady() local playerDataController = ResolvePlayerDataController() return playerDataController ~= nil and playerDataController.CurrentData ~= nil end local function ResolveEnumLibrary() if EnumLibrary then return EnumLibrary end local replicatedStorage = game:GetService('ReplicatedStorage') local modules = replicatedStorage:WaitForChild('Modules') local enumLibraryModule = modules:WaitForChild('EnumLibrary') local library = RivalsRuntimeBridge.RequireGameModule(enumLibraryModule, 'EnumLibrary') if not library then return nil end EnumLibrary = library return EnumLibrary end local function BeginReplicatedStateBoot() if ReplicatedStateBootStarted then return end ReplicatedStateBootStarted = true task.spawn(GuardRivalsCallback('ReplicatedStateBoot', function() local attempts = 0 local notified = false -- one failed require used to end the boot for good; keep trying so a slow or fussy executor still gets there while not ReplicatedStateReady and not Library.Unloaded and attempts < 150 do attempts += 1 local reason = nil local attemptOk, attemptErr = pcall(function() local enumLibrary = ResolveEnumLibrary() if not enumLibrary then reason = 'EnumLibrary could not be loaded' elseif type(enumLibrary.WaitForEnumBuilder) == 'function' then enumLibrary:WaitForEnumBuilder() elseif enumLibrary._enum_builder_complete ~= true then reason = 'EnumLibrary has not finished building' end if not reason then local playerDataController = ResolvePlayerDataController() if not playerDataController then reason = 'PlayerDataController could not be loaded' elseif type(playerDataController.WaitUntilLoaded) == 'function' then playerDataController:WaitUntilLoaded() elseif not IsPlayerDataControllerReady() then reason = 'player data is not loaded yet' end end end) if not attemptOk then reason = 'boot step failed: ' .. tostring(attemptErr) end if not reason then task.wait(2) ReplicatedStateReady = true RivalsRuntimeBridge.BootIssue = nil local state = RivalsModsState if state.PendingEnsureHooks and type(RivalsModsState.EnsureHooks) == 'function' then task.defer(RivalsModsState.EnsureHooks) end return end local detail = reason for _, label in ipairs({ 'EnumLibrary', 'PlayerDataController' }) do local issue = RivalsRuntimeBridge.ModuleIssues[label] if issue then detail = detail .. ' (require(' .. label .. '): ' .. tostring(issue.err) .. ')' end end RivalsRuntimeBridge.BootIssue = detail if attempts >= 8 and not notified then notified = true pcall( function() Library:Notify({ Title = 'Executor Support', Description = 'Camera, viewmodel and weapon features could not start: ' .. detail .. '. Open Client Effects and press Copy Executor Report.', Time = 12, }) end ) end task.wait(2) end end)) end local function IsReplicatedStateReady() BeginReplicatedStateBoot() return ReplicatedStateReady end local function ResolveFighterController() if FighterController then return FighterController end if not ReplicatedStateReady then BeginReplicatedStateBoot() return nil end local playerScripts = LP:FindFirstChild('PlayerScripts') local controllers = playerScripts and playerScripts:FindFirstChild('Controllers') local fighterControllerModule = controllers and controllers:FindFirstChild('FighterController') if not fighterControllerModule then return nil end local controller = RivalsRuntimeBridge.RequireGameModule(fighterControllerModule, 'FighterController') if not controller then return nil end FighterController = controller return FighterController end local function ResolveEnemyController() if EnemyController then return EnemyController end if not ReplicatedStateReady then BeginReplicatedStateBoot() return nil end local playerScripts = LP:FindFirstChild('PlayerScripts') local controllers = playerScripts and playerScripts:FindFirstChild('Controllers') local enemyControllerModule = controllers and controllers:FindFirstChild('EnemyController') if not enemyControllerModule then return nil end local controller = RivalsRuntimeBridge.RequireGameModule(enemyControllerModule, 'EnemyController') if not controller then return nil end EnemyController = controller return EnemyController end local function ReadItemValue(item, key) if not item or type(item.Get) ~= 'function' then return nil end local ok, value = pcall(item.Get, item, key) if not ok then return nil end return value end local function ResolveLocalFighter() local fighterController = ResolveFighterController() if not fighterController then return nil end if fighterController.LocalFighter then return fighterController.LocalFighter end if type(fighterController.GetFighter) ~= 'function' then return nil end local fighter = fighterController:GetFighter(LP) if fighter then return fighter end if LP.Character then fighter = fighterController:GetFighter(LP.Character) if fighter then return fighter end end return nil end local function ReadFighterValue(fighter, key) if not fighter or type(fighter.Get) ~= 'function' then return nil end local ok, value = pcall(fighter.Get, fighter, key) if not ok then return nil end return value end local AIMBOT_RIOT_SHIELD_ITEM_NAMES = { ['Riot Shield'] = true, } local AIMBOT_RIOT_SHIELD_FRONT_DOT_THRESHOLD = 0.0 local AIMBOT_RIOT_SHIELD_BACK_DOT_THRESHOLD = 0.0 local function ResolveRivalsInventoryItemName(item) local itemType = typeof(item) if itemType == 'string' then return item ~= '' and item or nil end if itemType == 'Instance' then return item.Name ~= '' and item.Name or nil end if itemType ~= 'table' then return nil end if type(item.Name) == 'string' and item.Name ~= '' then return item.Name end local info = item.Info if type(info) == 'table' and type(info.Name) == 'string' and info.Name ~= '' then return info.Name end if type(item.ItemName) == 'string' and item.ItemName ~= '' then return item.ItemName end if type(item.Type) == 'string' and item.Type ~= '' then return item.Type end return nil end local function ReadRivalsFighterInventory(fighter) if not fighter then return nil end return fighter.Loadout or fighter.Slots or fighter.Items or fighter.EquippedItems end local function ResolveRivalsFighterLoadoutNames(fighter) local loadoutNames = {} local function addName(name) if type(name) == 'string' and name ~= '' then loadoutNames[name] = true end end local inventory = ReadRivalsFighterInventory(fighter) if type(inventory) == 'table' then for _, item in pairs(inventory) do addName(ResolveRivalsInventoryItemName(item)) end end local lastPickedWeapons = fighter and fighter.Data and fighter.Data.LastPickedWeapons if type(lastPickedWeapons) == 'table' then for _, itemName in ipairs(lastPickedWeapons) do addName(itemName) end end return loadoutNames end local function ResolveRivalsFighterEquippedItemName(fighter) local equippedName = ResolveRivalsInventoryItemName(fighter and fighter.EquippedItem) if equippedName then return equippedName end local equippedItemId = fighter and fighter.Data and fighter.Data.EquippedItemID if type(equippedItemId) ~= 'string' or equippedItemId == '' then return nil end local inventory = ReadRivalsFighterInventory(fighter) if type(inventory) ~= 'table' then return nil end for _, item in pairs(inventory) do if type(item) == 'table' then local itemId = ReadItemValue(item, 'ObjectID') if itemId == nil and type(item.Data) == 'table' then itemId = item.Data.ObjectID end if itemId == equippedItemId then return ResolveRivalsInventoryItemName(item) end end end return nil end local function EvaluateRivalsRiotShieldChecker(player, character, observerPosition) observerPosition = typeof(observerPosition) == 'Vector3' and observerPosition or (HumanoidRootPart and HumanoidRootPart.Position) if not player or typeof(observerPosition) ~= 'Vector3' then return nil end character = character or player.Character local rootPart = character and character:FindFirstChild('HumanoidRootPart') if not character or not rootPart or not rootPart.Parent then return nil end local fighter = TrackedFightersByPlayer[player] if not fighter then local fighterController = ResolveFighterController() if fighterController and type(fighterController.GetFighter) == 'function' then fighter = fighterController:GetFighter(player) if not fighter then fighter = fighterController:GetFighter(character) end end end if not fighter then return nil end local loadoutNames = ResolveRivalsFighterLoadoutNames(fighter) local hasShieldInLoadout = loadoutNames['Riot Shield'] == true local equippedName = ResolveRivalsFighterEquippedItemName(fighter) local isShieldEquipped = nil if equippedName then isShieldEquipped = AIMBOT_RIOT_SHIELD_ITEM_NAMES[equippedName] == true end local directionToLocal = observerPosition - rootPart.Position if directionToLocal.Magnitude > 0.001 then directionToLocal = directionToLocal.Unit else directionToLocal = rootPart.CFrame.LookVector end local facingDot = rootPart.CFrame.LookVector:Dot(directionToLocal) local blocksFromFront = false if isShieldEquipped == true and facingDot >= AIMBOT_RIOT_SHIELD_FRONT_DOT_THRESHOLD then blocksFromFront = true end local blocksFromBack = false if hasShieldInLoadout and isShieldEquipped == false and facingDot <= AIMBOT_RIOT_SHIELD_BACK_DOT_THRESHOLD then blocksFromBack = true end return { Fighter = fighter, Character = character, EquippedItemName = equippedName, HasShieldInLoadout = hasShieldInLoadout, IsShieldEquipped = isShieldEquipped, IsShieldEquippedKnown = isShieldEquipped ~= nil, FacingDot = facingDot, BlocksFromFront = blocksFromFront, BlocksFromBack = blocksFromBack, IgnoreTarget = blocksFromFront or blocksFromBack, } end local function ShouldIgnoreRivalsRiotShieldTarget(player, character, localItem, observerPosition) if not player or not (Toggles.P2S1T9 and Toggles.P2S1T9.Value == true) then return false end local localItemName = localItem and localItem.Name if localItemName == 'Flamethrower' or localItemName == 'Exogun' or localItemName == 'Spray' or localItemName == 'Chainsaw' or localItemName == 'Grenade Launcher' then return false end local shieldState = EvaluateRivalsRiotShieldChecker(player, character, observerPosition) return shieldState and shieldState.IgnoreTarget == true end function FighterDataCache.ReadEntity(entity) if not entity then return false end local data = entity.Data if type(data) == 'table' then return data.IsInvincible == true end return false end function FighterDataCache.Attach(fighter, entity) local conns = FighterDataCache.Connections[fighter] if conns and conns.entityChanged and type(conns.entityChanged.Disconnect) == 'function' then pcall(function() conns.entityChanged:Disconnect() end) conns.entityChanged = nil end if not entity then FighterDataCache.Cache[fighter] = false return end FighterDataCache.Cache[fighter] = FighterDataCache.ReadEntity(entity) if type(entity.GetDataChangedSignal) ~= 'function' then return end local ok, signal = pcall(entity.GetDataChangedSignal, entity, 'IsInvincible') if not ok or type(signal) ~= 'table' or type(signal.Connect) ~= 'function' then return end conns = FighterDataCache.Connections[fighter] or {} conns.entityChanged = signal:Connect(function(newValue) FighterDataCache.Cache[fighter] = newValue == true end) FighterDataCache.Connections[fighter] = conns end function FighterDataCache.Track(fighter) if not fighter then return end local conns = FighterDataCache.Connections[fighter] or {} FighterDataCache.Connections[fighter] = conns FighterDataCache.Attach(fighter, fighter.Entity) if not conns.entityAdded and type(fighter.EntityAdded) == 'table' and type(fighter.EntityAdded.Connect) == 'function' then conns.entityAdded = fighter.EntityAdded:Connect(function(newEntity) FighterDataCache.Attach(fighter, newEntity) end) end if not conns.entityRemoved and type(fighter.EntityRemoved) == 'table' and type(fighter.EntityRemoved.Connect) == 'function' then conns.entityRemoved = fighter.EntityRemoved:Connect(function() FighterDataCache.Cache[fighter] = false local existing = FighterDataCache.Connections[fighter] if existing and existing.entityChanged and type(existing.entityChanged.Disconnect) == 'function' then pcall(function() existing.entityChanged:Disconnect() end) existing.entityChanged = nil end end) end end function FighterDataCache.Untrack(fighter) if not fighter then return end local conns = FighterDataCache.Connections[fighter] if conns then for key, conn in pairs(conns) do if conn and type(conn.Disconnect) == 'function' then pcall(function() conn:Disconnect() end) end conns[key] = nil end end FighterDataCache.Connections[fighter] = nil FighterDataCache.Cache[fighter] = nil end function FighterDataCache.AttachLocalDuelWatchers(fighter) if not fighter then return end local state = FighterDataCache.LocalDuel if state.AttachedFighter == fighter then return end if state.DuelConn and type(state.DuelConn.Disconnect) == 'function' then pcall(function() state.DuelConn:Disconnect() end) end if state.ShootingConn and type(state.ShootingConn.Disconnect) == 'function' then pcall(function() state.ShootingConn:Disconnect() end) end state.DuelConn = nil state.ShootingConn = nil state.AttachedFighter = fighter local data = fighter.Data if type(data) == 'table' then state.IsInDuel = data.IsInDuel == true state.IsInShootingRange = data.IsInShootingRange == true else state.IsInDuel = false state.IsInShootingRange = false end if type(fighter.GetDataChangedSignal) ~= 'function' then state.Seeded = true return end local okDuel, sigDuel = pcall(fighter.GetDataChangedSignal, fighter, 'IsInDuel') if okDuel and type(sigDuel) == 'table' and type(sigDuel.Connect) == 'function' then state.DuelConn = sigDuel:Connect(function(newValue) local previousIsInDuel = FighterDataCache.LocalDuel.IsInDuel FighterDataCache.LocalDuel.IsInDuel = newValue == true local onDuelChanged = FighterDataCache.OnLocalDuelChanged if type(onDuelChanged) == 'function' then pcall(onDuelChanged, newValue == true, previousIsInDuel == true) end end) end local okShooting, sigShooting = pcall(fighter.GetDataChangedSignal, fighter, 'IsInShootingRange') if okShooting and type(sigShooting) == 'table' and type(sigShooting.Connect) == 'function' then state.ShootingConn = sigShooting:Connect( function(newValue) FighterDataCache.LocalDuel.IsInShootingRange = newValue == true end ) end state.Seeded = true end local function RegisterAimbotFighter(fighter) local player = fighter and fighter.Player if not player or player == LP then return end TrackedFightersByPlayer[player] = fighter FighterDataCache.Track(fighter) end local function UnregisterAimbotFighter(fighter) local player = fighter and fighter.Player if not player then return end if TrackedFightersByPlayer[player] == fighter then FighterDataCache.Untrack(fighter) TrackedFightersByPlayer[player] = nil end end local function ResolveAimbotEnemyTargetType(enemy) local model = enemy and enemy.Model if not model then return nil, nil end if model.Name == 'Target' then return model, 'Range Targets' end if model.Name == 'Dummy' or model.Name == 'DPS Dummy' then return model, 'Practice Dummies' end return nil, nil end local function RegisterAimbotEnemy(enemy) local model, targetType = ResolveAimbotEnemyTargetType(enemy) if not model or not targetType then return end TrackedEnemies[enemy] = { model = model, targetType = targetType, } if targetType == 'Range Targets' then TrackedRangeTargets[model] = true TrackedPracticeDummies[model] = nil else TrackedPracticeDummies[model] = true TrackedRangeTargets[model] = nil end end local function UnregisterAimbotEnemy(enemy) local tracked = TrackedEnemies[enemy] if tracked then if tracked.targetType == 'Range Targets' then TrackedRangeTargets[tracked.model] = nil else TrackedPracticeDummies[tracked.model] = nil end TrackedEnemies[enemy] = nil return end local model, targetType = ResolveAimbotEnemyTargetType(enemy) if not model or not targetType then return end if targetType == 'Range Targets' then TrackedRangeTargets[model] = nil else TrackedPracticeDummies[model] = nil end end local function EnsureAimbotTargetTracking() if not Connections then return end if not AimbotPlayerTrackingConnected then local fighterController = ResolveFighterController() if fighterController then LocalFighterController = fighterController for _, fighter in ipairs(fighterController.Objects or {}) do RegisterAimbotFighter(fighter) end if fighterController.ObjectAdded and type(fighterController.ObjectAdded.Connect) == 'function' then Connections:register( 'Aimbot_FighterObjectAdded', fighterController.ObjectAdded:Connect( GuardRivalsCallback('Aimbot_FighterObjectAdded', RegisterAimbotFighter) ) ) end if fighterController.ObjectRemoved and type(fighterController.ObjectRemoved.Connect) == 'function' then Connections:register( 'Aimbot_FighterObjectRemoved', fighterController.ObjectRemoved:Connect( GuardRivalsCallback('Aimbot_FighterObjectRemoved', UnregisterAimbotFighter) ) ) end AimbotPlayerTrackingConnected = true end end if not AimbotEnemyTrackingConnected then local enemyController = ResolveEnemyController() if enemyController then for _, enemy in ipairs(enemyController.Objects or {}) do RegisterAimbotEnemy(enemy) end if enemyController.ObjectAdded and type(enemyController.ObjectAdded.Connect) == 'function' then Connections:register( 'Aimbot_EnemyObjectAdded', enemyController.ObjectAdded:Connect(GuardRivalsCallback('Aimbot_EnemyObjectAdded', RegisterAimbotEnemy)) ) end if enemyController.ObjectRemoved and type(enemyController.ObjectRemoved.Connect) == 'function' then Connections:register( 'Aimbot_EnemyObjectRemoved', enemyController.ObjectRemoved:Connect( GuardRivalsCallback('Aimbot_EnemyObjectRemoved', UnregisterAimbotEnemy) ) ) end AimbotEnemyTrackingConnected = true end end end local function IsZombieEventActive() local toggle = Toggles and Toggles.EventZombieIncludeInAimbot if not (toggle and toggle.Value == true) then return false end local ec = ResolveEnemyController() if not ec then return false end for _, e in ipairs(ec.Objects or {}) do local model = e.Model or (e.Entity and rawget(e.Entity, 'Model')) if model and model.Parent then local name = model.Name if name ~= 'Target' and name ~= 'Dummy' and name ~= 'DPS Dummy' then if model:FindFirstChildOfClass('Humanoid') or model:FindFirstChild('HitboxHead') then return true end end end end return false end local function IsAimbotGameReady() local fighter = ResolveLocalFighter() if not fighter then return false end local entity = fighter.Entity local model = entity and entity.Model if not model or model ~= LP.Character then return false end local duelState = FighterDataCache.LocalDuel local inDuelOrRange if duelState.Seeded then inDuelOrRange = duelState.IsInDuel or duelState.IsInShootingRange else inDuelOrRange = ReadFighterValue(fighter, 'IsInDuel') == true or ReadFighterValue(fighter, 'IsInShootingRange') == true end if not inDuelOrRange and not IsZombieEventActive() then return false end return true end local function FormatAmmoValue(value) if type(value) ~= 'number' then return nil end if value == math.huge then return 'INF' end return tostring(math.max(0, math.floor(value + 0.5))) end local function BuildWeaponInfoText(weaponInfo) if not weaponInfo then return nil end local segments = {} if weaponInfo.label and weaponInfo.label ~= '' then segments[#segments + 1] = weaponInfo.label end if weaponInfo.ammoText and weaponInfo.ammoText ~= '' then segments[#segments + 1] = weaponInfo.ammoText end if #segments == 0 then return nil end return table.concat(segments, ' | ') end local function ReadWeaponInfo(player) local observer = WeaponInfoConnections[player] local item = observer and observer.Item if not item then -- no item object (yet): the fighter's replicated data still carries the name of the equipped item local fighter = observer and observer.Fighter local name = fighter and ResolveRivalsInventoryItemName(ReadFighterValue(fighter, 'EquippedItem')) or nil if not name then return nil end return { label = name, ammoText = nil, displayText = name, isReloading = false } end local ammo = observer.Ammo local ammoText = FormatAmmoValue(observer.Ammo) local reserveText = FormatAmmoValue(observer.AmmoReserve) local formattedAmmoText local maxAmmo = nil pcall(function() maxAmmo = item.Info and item.Info.MaxAmmo or nil end) if ammoText and reserveText then formattedAmmoText = string.format('%s/%s', ammoText, reserveText) else formattedAmmoText = ammoText or reserveText end local reloadCooldown = rawget(item, '_reload_cooldown') local reloadRemaining = type(reloadCooldown) == 'number' and (reloadCooldown - tick()) or -1 local isReloading = reloadRemaining > 0 and item.Info ~= nil and item.Info.ReloadType ~= nil local label = isReloading and '*Reloading*' or item.Name local displayText = BuildWeaponInfoText({ label = label, ammoText = formattedAmmoText, }) if displayText and displayText ~= '' then return { label = label, ammoText = formattedAmmoText, displayText = displayText, isReloading = isReloading, ammo = ammo, maxAmmo = maxAmmo, } end return nil end local function InvalidateWeaponInfo(player) WeaponInfoCache[player] = nil end local function DisconnectWeaponInfoConnection(observer, key) if not observer then return end local connection = observer[key] if connection then pcall(function() connection:Disconnect() end) observer[key] = nil end end local function DisconnectWeaponInfoConnections(player) local observer = WeaponInfoConnections[player] if not observer then return end DisconnectWeaponInfoConnection(observer, 'EquippedItemChanged') DisconnectWeaponInfoConnection(observer, 'EquippedChanged') DisconnectWeaponInfoConnection(observer, 'AmmoChanged') DisconnectWeaponInfoConnection(observer, 'AmmoReserveChanged') WeaponInfoConnections[player] = nil end local function ConnectWeaponInfoSignal(signal, observer, key, callback) DisconnectWeaponInfoConnection(observer, key) if not signal or type(signal.Connect) ~= 'function' then return end local guardedCallback = GuardRivalsCallback(string.format('WeaponInfo_%s', tostring(key)), callback) local ok, connection = pcall(signal.Connect, signal, guardedCallback) if ok and connection then observer[key] = connection end end local function AttachWeaponInfoItemObservers(player, observer, item) DisconnectWeaponInfoConnection(observer, 'EquippedChanged') DisconnectWeaponInfoConnection(observer, 'AmmoChanged') DisconnectWeaponInfoConnection(observer, 'AmmoReserveChanged') observer.Item = item observer.Ammo = item and ReadItemValue(item, 'Ammo') or nil observer.AmmoReserve = item and ReadItemValue(item, 'AmmoReserve') or nil if not item then return end ConnectWeaponInfoSignal(item.EquippedChanged, observer, 'EquippedChanged', function() InvalidateWeaponInfo(player) if player == LP and UpdateRivalsPickupFeatures then UpdateRivalsPickupFeatures() end end) local valueChangedEvents = rawget(item, '_value_changed_events') if type(valueChangedEvents) ~= 'table' then return end ConnectWeaponInfoSignal(valueChangedEvents.Ammo, observer, 'AmmoChanged', function(newAmmo) observer.Ammo = type(newAmmo) == 'number' and newAmmo or ReadItemValue(item, 'Ammo') InvalidateWeaponInfo(player) local ammo = observer.Ammo if player == LP and UpdateRivalsPickupFeatures and type(ammo) == 'number' and ammo <= 0 then UpdateRivalsPickupFeatures() end end) ConnectWeaponInfoSignal(valueChangedEvents.AmmoReserve, observer, 'AmmoReserveChanged', function(newReserve) observer.AmmoReserve = type(newReserve) == 'number' and newReserve or ReadItemValue(item, 'AmmoReserve') InvalidateWeaponInfo(player) end) end local function AttachWeaponInfoObservers(player, fighter) if not player then return end if player == LP then fighter = fighter or ResolveLocalFighter() else local fighterController = ResolveFighterController() if not fighter and fighterController and type(fighterController.GetFighter) == 'function' then fighter = fighterController:GetFighter(player) end end if not fighter then DisconnectWeaponInfoConnections(player) return end local observer = WeaponInfoConnections[player] if observer and observer.Fighter == fighter then return end DisconnectWeaponInfoConnections(player) observer = { Fighter = fighter, Item = nil, } WeaponInfoConnections[player] = observer local function refreshItemObservers() local item = fighter.EquippedItem if observer.Item ~= item then AttachWeaponInfoItemObservers(player, observer, item) end InvalidateWeaponInfo(player) if player == LP then FighterDataCache.LocalLoadout.EquippedItem = item FighterDataCache.LocalLoadout.Seeded = true if UpdateRivalsPickupFeatures then UpdateRivalsPickupFeatures() end end end ConnectWeaponInfoSignal(fighter.EquippedItemChanged, observer, 'EquippedItemChanged', refreshItemObservers) refreshItemObservers() end local function RegisterWeaponInfoFighter(fighter) local player = fighter and fighter.Player if not player or player == LP then return end AttachWeaponInfoObservers(player, fighter) end local function UnregisterWeaponInfoFighter(fighter) local player = fighter and fighter.Player if not player or player == LP then return end local observer = WeaponInfoConnections[player] if observer and observer.Fighter ~= fighter then return end DisconnectWeaponInfoConnections(player) InvalidateWeaponInfo(player) end local function EnsureWeaponInfoTracking() if not Connections or WeaponInfoTrackingConnected then return end if not IsReplicatedStateReady() then if not WeaponInfoTrackingRetryPending then WeaponInfoTrackingRetryPending = true task.spawn(GuardRivalsCallback('WeaponInfo_WaitForReplicatedState', function() while not WeaponInfoTrackingConnected and not ReplicatedStateReady do task.wait(0.25) end WeaponInfoTrackingRetryPending = false if not WeaponInfoTrackingConnected then EnsureWeaponInfoTracking() end end)) end return end local fighterController = ResolveFighterController() if not fighterController then return end for _, fighter in ipairs(fighterController.Objects or {}) do RegisterWeaponInfoFighter(fighter) end if fighterController.ObjectAdded and type(fighterController.ObjectAdded.Connect) == 'function' then Connections:register( 'WeaponInfo_FighterObjectAdded', fighterController.ObjectAdded:Connect( GuardRivalsCallback('WeaponInfo_FighterObjectAdded', RegisterWeaponInfoFighter) ) ) end if fighterController.ObjectRemoved and type(fighterController.ObjectRemoved.Connect) == 'function' then Connections:register( 'WeaponInfo_FighterObjectRemoved', fighterController.ObjectRemoved:Connect( GuardRivalsCallback('WeaponInfo_FighterObjectRemoved', UnregisterWeaponInfoFighter) ) ) end WeaponInfoTrackingConnected = true end -- Signals are the fast path. This keeps the ESP weapon / ammo readout alive on executors where they never arrive or where the -- one-time setup failed: it re-attaches observers that were missed (fighter not created yet, a require that failed once), follows -- the equipped item by looking at it and polls the ammo. Throttled per player. RivalsRuntimeBridge.WeaponInfoHealAt = setmetatable({}, { __mode = 'k' }) RivalsRuntimeBridge.HealWeaponInfo = function(player) local healAt = RivalsRuntimeBridge.WeaponInfoHealAt local now = os.clock() if healAt[player] and now < healAt[player] then return end healAt[player] = now + 0.2 if not WeaponInfoTrackingConnected then pcall(EnsureWeaponInfoTracking) end local observer = WeaponInfoConnections[player] if not observer or not observer.Fighter then pcall(AttachWeaponInfoObservers, player) observer = WeaponInfoConnections[player] if not observer then return end end local okEquipped, equipped = pcall(function() return observer.Fighter.EquippedItem end) if okEquipped and equipped ~= observer.Item then pcall(AttachWeaponInfoItemObservers, player, observer, equipped) InvalidateWeaponInfo(player) end local polled = observer.Item if polled then local ammo, reserve = ReadItemValue(polled, 'Ammo'), ReadItemValue(polled, 'AmmoReserve') if ammo ~= observer.Ammo or reserve ~= observer.AmmoReserve then observer.Ammo, observer.AmmoReserve = ammo, reserve InvalidateWeaponInfo(player) end end end local function GetCachedWeaponInfo(player) RivalsRuntimeBridge.HealWeaponInfo(player) local observer = WeaponInfoConnections[player] local item = observer and observer.Item or nil if not item then return ReadWeaponInfo(player) end local reloadCooldown = item and rawget(item, '_reload_cooldown') or nil local isReloading = type(reloadCooldown) == 'number' and reloadCooldown > tick() and item.Info ~= nil and item.Info.ReloadType ~= nil local cached = WeaponInfoCache[player] if cached and cached.item == item and cached.reloadCooldown == reloadCooldown and cached.isReloading == isReloading then return cached.weaponInfo end local weaponInfo = ReadWeaponInfo(player) WeaponInfoCache[player] = { item = item, reloadCooldown = reloadCooldown, isReloading = isReloading, weaponInfo = weaponInfo, } return weaponInfo end local function BuildPlayerWeaponText(weaponInfoText, showWeapon) if showWeapon and weaponInfoText and weaponInfoText ~= '' then return weaponInfoText end return '' end local function BuildPlayerDistanceText(dist, showDistance) if showDistance then return string.format('%dm', math.floor(dist)) end return '' end local function BuildHealthText(humanoid, showHealthNum) if showHealthNum and humanoid then return tostring(math.floor(humanoid.Health + 0.5)) end return '' end local function BuildVerticalHealthText(humanoid) if not humanoid then return '' end local currentHealth = math.max(0, math.floor(humanoid.Health + 0.5)) return tostring(currentHealth) end local function GetHealthBarColor(healthRatio) local ratio = math.clamp(healthRatio or 0, 0, 1) if ratio <= 0.5 then return BlendColor(PLAYER_CARD_THEME.BarLowColor, PLAYER_CARD_THEME.BarMidColor, ratio / 0.5) end return BlendColor(PLAYER_CARD_THEME.BarMidColor, PLAYER_CARD_THEME.BarHighColor, (ratio - 0.5) / 0.5) end -- ====================================================================== -- ESP (classic): player cards and reactive bars -- ====================================================================== do local EspClassic = RivalsRuntimeBridge.ESPClassic local PlayerStatusInfoCache = RivalsRuntimeBridge.PlayerStatusInfoCache local function GetReactiveBarColor(ratio) return BlendColor( PLAYER_CARD_THEME.Classic.ReactiveLowColor, PLAYER_CARD_THEME.BarHighColor, math.clamp(ratio or 0, 0, 1) ) end local function ResolveEspRankProfile() if RivalsRuntimeBridge.EspRankProfileCache then return RivalsRuntimeBridge.EspRankProfileCache end local modules = ReplicatedStorage:FindFirstChild('Modules') local seasonLibraryModule = modules and modules:FindFirstChild('SeasonLibrary') if not seasonLibraryModule then return nil end local ok, seasonLibrary = pcall(require, seasonLibraryModule) if not ok or type(seasonLibrary) ~= 'table' then return nil end local currentSeason = seasonLibrary.CurrentSeason local rankProfile = type(currentSeason) == 'table' and currentSeason.RankProfile or nil local rankProfileName = type(rankProfile) == 'table' and rankProfile.Name or nil local rankProfiles = seasonLibrary.RankProfiles if type(rankProfiles) == 'table' and rankProfileName ~= nil then rankProfile = rankProfiles[rankProfileName] or rankProfile end local ranksOrder = type(rankProfile) == 'table' and rankProfile.RanksOrder or nil local ranks = type(rankProfile) == 'table' and rankProfile.Ranks or nil if type(ranksOrder) ~= 'table' or type(ranks) ~= 'table' then return nil end RivalsRuntimeBridge.EspRankProfileCache = { RanksOrder = ranksOrder, Ranks = ranks, } return RivalsRuntimeBridge.EspRankProfileCache end local function ResolveEspRankName(displayElo) local elo = tonumber(displayElo) if elo == nil then return 'Unranked' end local rankProfile = ResolveEspRankProfile() if not rankProfile then return 'Unranked' end local ranksOrder = rankProfile.RanksOrder local ranks = rankProfile.Ranks local rankCount = #ranksOrder for rankIndex, rankName in ipairs(ranksOrder) do local rankData = ranks[rankName] if type(rankData) == 'table' and type(rankData.RequiredELO) == 'number' and elo < rankData.RequiredELO and rankData.RequiredELOLeaderboardRanking == nil then return ranksOrder[math.clamp(rankIndex - 1, 1, rankCount)] or 'Unranked' end end for rankIndex = rankCount, 1, -1 do local rankName = ranksOrder[rankIndex] local rankData = ranks[rankName] if type(rankData) == 'table' and rankData.RequiredELOLeaderboardRanking == nil then return rankName end end return 'Unranked' end local function InvalidatePlayerStatusInfo(player) RivalsRuntimeBridge.PlayerStatusInfoCache[player] = nil end local function GetCachedPlayerStatusInfo(player) local cached = PlayerStatusInfoCache[player] if cached then return cached end local winStreak = tonumber(player:GetAttribute('StatisticDuelsWinStreak')) or 0 cached = { Rank = ResolveEspRankName(player:GetAttribute('DisplayELO')), WinStreak = math.max(0, math.floor(winStreak)), } PlayerStatusInfoCache[player] = cached return cached end local function DisconnectEspStatusObservers(entry) for _, connection in pairs(entry.statusConnections or {}) do pcall(function() connection:Disconnect() end) end entry.statusConnections = nil end local function AttachEspStatusObservers(player, entry) DisconnectEspStatusObservers(entry) entry.statusConnections = { displayElo = player:GetAttributeChangedSignal('DisplayELO'):Connect( GuardRivalsCallback('ESP_DisplayELOChanged', function() InvalidatePlayerStatusInfo(player) end) ), winStreak = player:GetAttributeChangedSignal('StatisticDuelsWinStreak'):Connect( GuardRivalsCallback('ESP_WinStreakChanged', function() InvalidatePlayerStatusInfo(player) end) ), } InvalidatePlayerStatusInfo(player) end local function BuildEspStatusText(player, settings, entry) local deflectCheck = RivalsRuntimeBridge.IsRivalsKatanaDeflectActiveItem local isDeflecting = false if settings.ShowDeflecting and deflectCheck then local observer = WeaponInfoConnections[player] local fighter = TrackedFightersByPlayer[player] or (observer and observer.Fighter) if deflectCheck(fighter and fighter.EquippedItem or nil) then isDeflecting = true end end local rank = nil local winStreak = nil if settings.ShowRank or settings.ShowWinStreak then local statusInfo = GetCachedPlayerStatusInfo(player) if settings.ShowRank then rank = statusInfo.Rank end if settings.ShowWinStreak then winStreak = statusInfo.WinStreak end end if entry.statusCacheDeflecting == isDeflecting and entry.statusCacheRank == rank and entry.statusCacheWinStreak == winStreak then return entry.statusCacheValue or '' end local labels = {} if isDeflecting then labels[#labels + 1] = 'DEFLECTING' end if rank then labels[#labels + 1] = string.upper(rank) end if winStreak then labels[#labels + 1] = string.format('%d WS', winStreak) end local value = table.concat(labels, '\n') entry.statusCacheDeflecting = isDeflecting entry.statusCacheRank = rank entry.statusCacheWinStreak = winStreak entry.statusCacheValue = value return value end local function SmoothEspRatio(currentRatio, targetRatio, deltaTime) targetRatio = math.clamp(targetRatio or 0, 0, 1) if type(currentRatio) ~= 'number' then return targetRatio end local alpha = 1 - math.exp(-PLAYER_CARD_THEME.Classic.SmoothSpeed * math.max(deltaTime or 0, 0)) local nextRatio = currentRatio + ((targetRatio - currentRatio) * alpha) if math.abs(nextRatio - targetRatio) < 0.001 then return targetRatio end return nextRatio end EspClassic.GetReactiveBarColor = GetReactiveBarColor EspClassic.DisconnectStatusObservers = DisconnectEspStatusObservers EspClassic.AttachStatusObservers = AttachEspStatusObservers EspClassic.BuildStatusText = BuildEspStatusText EspClassic.SmoothRatio = SmoothEspRatio end local function GetPartScreenBounds(part, camera, boundsCache) if not part or not part:IsA('BasePart') or not camera then return nil end boundsCache = boundsCache or {} local partSize = part.Size if boundsCache.Part ~= part or boundsCache.Size ~= partSize then local halfX = partSize.X * 0.5 local halfY = partSize.Y * 0.5 local halfZ = partSize.Z * 0.5 local localCorners = table.create(8) local cornerIndex = 0 for _, xSign in ipairs(MODEL_BOUNDS_SIGNS) do for _, ySign in ipairs(MODEL_BOUNDS_SIGNS) do for _, zSign in ipairs(MODEL_BOUNDS_SIGNS) do cornerIndex = cornerIndex + 1 localCorners[cornerIndex] = Vector3.new(halfX * xSign, halfY * ySign, halfZ * zSign) end end end boundsCache.Part = part boundsCache.Size = partSize boundsCache.LocalCorners = localCorners end local minX, maxX, minY, maxY local projectedCorners = 0 for _, localCorner in ipairs(boundsCache.LocalCorners) do local worldCorner = part.CFrame:PointToWorldSpace(localCorner) local corner = camera:WorldToViewportPoint(worldCorner) if corner.Z > 0 then projectedCorners = projectedCorners + 1 minX = minX and math.min(minX, corner.X) or corner.X maxX = maxX and math.max(maxX, corner.X) or corner.X minY = minY and math.min(minY, corner.Y) or corner.Y maxY = maxY and math.max(maxY, corner.Y) or corner.Y end end if projectedCorners < 2 or not minX or not maxX or not minY or not maxY then return nil end return { MinX = minX, MaxX = maxX, MinY = minY, MaxY = maxY, Width = maxX - minX, Height = maxY - minY, CenterX = minX + ((maxX - minX) * 0.5), CenterY = minY + ((maxY - minY) * 0.5), } end local function ReadCharacterEspParts(character, cache) if not character then return nil end if not cache or cache.Character ~= character then cache = { Character = character } end if cache.HumanoidRootPart == nil or cache.HumanoidRootPart.Parent ~= character then cache.HumanoidRootPart = character:FindFirstChild('HumanoidRootPart') end if cache.Humanoid == nil or cache.Humanoid.Parent ~= character then cache.Humanoid = character:FindFirstChild('Humanoid') end if cache.HitboxBody == nil or cache.HitboxBody.Parent ~= character then local bodyHitbox = character:FindFirstChild('HitboxBody') if cache.HitboxBody ~= bodyHitbox then cache.HitboxBody = bodyHitbox cache.HitboxBodyBoundsCache = {} end end if cache.HitboxHead == nil or cache.HitboxHead.Parent ~= character then local headHitbox = character:FindFirstChild('HitboxHead') if cache.HitboxHead ~= headHitbox then cache.HitboxHead = headHitbox cache.HitboxHeadBoundsCache = {} end end cache.HitboxBodyBoundsCache = cache.HitboxBodyBoundsCache or {} cache.HitboxHeadBoundsCache = cache.HitboxHeadBoundsCache or {} return cache end local function GetHitboxScreenBounds(characterParts, character, camera) if not characterParts or not character or not camera then return nil end local bodyHitbox = characterParts.HitboxBody local headHitbox = characterParts.HitboxHead local bodyBounds = GetPartScreenBounds(bodyHitbox, camera, characterParts.HitboxBodyBoundsCache) local headBounds = GetPartScreenBounds(headHitbox, camera, characterParts.HitboxHeadBoundsCache) characterParts.LastBodyScreenBounds = bodyBounds if bodyBounds and headBounds then local minX = math.min(bodyBounds.MinX, headBounds.MinX) local maxX = math.max(bodyBounds.MaxX, headBounds.MaxX) local minY = math.min(bodyBounds.MinY, headBounds.MinY) local maxY = math.max(bodyBounds.MaxY, headBounds.MaxY) local width = maxX - minX local height = maxY - minY return { MinX = minX, MaxX = maxX, MinY = minY, MaxY = maxY, Width = width, Height = height, CenterX = minX + (width * 0.5), CenterY = minY + (height * 0.5), } end if bodyBounds then return bodyBounds end if headBounds then return headBounds end return nil end local EspRenderSettings = {} local EspRenderSettingWatchersConnected = false local function ReadEspToggleValue(toggleId, fallback) local toggle = Toggles[toggleId] if toggle ~= nil then return toggle.Value == true end return fallback end local function ReadEspOptionValue(optionId, fallback) local option = Options[optionId] if option ~= nil and option.Value ~= nil then return option.Value end return fallback end local function RefreshEspRenderSettings() EspRenderSettings = { Enabled = ReadEspToggleValue('P1S1T1', true), ShowHighlight = ReadEspToggleValue('P1S1T2', true), ShowName = ReadEspToggleValue('P1S1T3', true), ShowHealth = ReadEspToggleValue('P1S1T4', true), ShowArrows = ReadEspToggleValue('P1S1T5', true), ShowDistance = ReadEspToggleValue('P1S1T6', true), UseVerticalHealth = ReadEspToggleValue('P1S1T7', false), ShowLookDir = ReadEspToggleValue('P1S1T8', true), ShowWeapon = ReadEspToggleValue('P1S1T9', true), ShowTeammates = ReadEspToggleValue('P1S1T10', true), ShowThrowable = ReadEspToggleValue('P1S1T11', true), ShowBox = ReadEspToggleValue('P1S2T1', true), ShowAmmoText = ReadEspToggleValue('P1S2T2', true), ShowRank = ReadEspToggleValue('P1S2T4', true), ShowWinStreak = ReadEspToggleValue('P1S2T5', true), ShowDeflecting = ReadEspToggleValue('P1S2T6', true), VisibleColor = ReadEspOptionValue('P1S1C1', Color3.fromRGB(0, 255, 0)), HiddenColor = ReadEspOptionValue('P1S1C2', Color3.fromRGB(255, 0, 0)), LookDirColor = ReadEspOptionValue('P1S1C4', Color3.fromRGB(255, 255, 0)), EmptyAmmoColor = ReadEspOptionValue('P1S2C1', Color3.fromRGB(255, 70, 70)), FillAlpha = ReadEspOptionValue('P1S1S3', 1), OutlineAlpha = ReadEspOptionValue('P1S1S4', 0), } if RivalsRuntimeBridge.UpdateEspPreview then pcall(RivalsRuntimeBridge.UpdateEspPreview) end return EspRenderSettings end local function RegisterEspRenderSettingWatcher(setting) if setting and type(setting.OnChanged) == 'function' then setting:OnChanged(RefreshEspRenderSettings) end end local function EnsureEspRenderSettingsTracking() if EspRenderSettingWatchersConnected then return end for _, toggleId in ipairs({ 'P1S1T1', 'P1S1T2', 'P1S1T3', 'P1S1T4', 'P1S1T5', 'P1S1T6', 'P1S1T7', 'P1S1T8', 'P1S1T9', 'P1S1T10', 'P1S1T11', 'P1S2T1', 'P1S2T2', 'P1S2T4', 'P1S2T5', 'P1S2T6', }) do RegisterEspRenderSettingWatcher(Toggles[toggleId]) end for _, optionId in ipairs({ 'P1S1C1', 'P1S1C2', 'P1S1C4', 'P1S2C1', 'P1S1S3', 'P1S1S4', }) do RegisterEspRenderSettingWatcher(Options[optionId]) end EspRenderSettingWatchersConnected = true RefreshEspRenderSettings() end RefreshEspRenderSettings() local HighlightContainer = Workspace local HazardAdornmentContainer = Workspace local ESPObjects = {} local DedicatedAimbotHitboxPartNames = { HitboxHead = true, HitboxBody = true, } local ResolveAimbotVisibilityRaycastParams do local AimbotVisibilityRaycastState = { Filter = {}, Params = RaycastParams.new(), } AimbotVisibilityRaycastState.Params.FilterType = Enum.RaycastFilterType.Exclude AimbotVisibilityRaycastState.Params.FilterDescendantsInstances = AimbotVisibilityRaycastState.Filter ResolveAimbotVisibilityRaycastParams = function(character) local filter = AimbotVisibilityRaycastState.Filter filter[1] = Char filter[2] = character filter[3] = nil AimbotVisibilityRaycastState.Params.FilterDescendantsInstances = filter return AimbotVisibilityRaycastState.Params end end local function IsPointVisible(character, worldPoint) local cam = Workspace.CurrentCamera if not cam or typeof(worldPoint) ~= 'Vector3' or not Char then return false end local origin = cam.CFrame.Position local direction = worldPoint - origin if direction.Magnitude <= 0 then return false end local rayParams = ResolveAimbotVisibilityRaycastParams(character) local result = Workspace:Raycast(origin, direction, rayParams) return result == nil end local function IsVisible(character, targetPart, targetPoint) if not targetPart then return false end local point = targetPoint or targetPart.Position return IsPointVisible(character, point) end -- ====================================================================== -- ESP (classic): lines and corner boxes -- ====================================================================== do local EspClassic = RivalsRuntimeBridge.ESPClassic local function CreateEspLine() local line = Drawing.new('Line') line.Visible = false return line end local function EnsureEspCornerBox(entry) if entry.cornerBox then return entry.cornerBox end local cornerBox = table.create(8) for segmentIndex = 1, 8 do cornerBox[segmentIndex] = { outline = CreateEspLine(), color = CreateEspLine(), } end entry.cornerBox = cornerBox return cornerBox end local function HideEspBox(entry) for _, segment in ipairs(entry.cornerBox or {}) do SetDrawingVisible(segment.outline, false) SetDrawingVisible(segment.color, false) end end local function RemoveEspBox(entry) for _, segment in ipairs(entry.cornerBox or {}) do pcall(function() segment.outline:Remove() end) pcall(function() segment.color:Remove() end) end entry.cornerBox = nil end local function SetEspCornerSegment(segment, fromPoint, toPoint, color, scale) local outline = segment.outline outline.From = fromPoint outline.To = toPoint outline.Color = PLAYER_CARD_THEME.Classic.BoxOutlineColor outline.Transparency = PLAYER_CARD_THEME.Classic.BoxOutlineTransparency outline.Thickness = math.max(PLAYER_CARD_THEME.Classic.BoxOutlineThickness * scale, 2) SetDrawingVisible(outline, true) local colorLine = segment.color colorLine.From = fromPoint colorLine.To = toPoint colorLine.Color = color colorLine.Transparency = 1 colorLine.Thickness = math.max(PLAYER_CARD_THEME.Classic.BoxThickness * scale, 1) SetDrawingVisible(colorLine, true) end local function DrawEspBox(entry, bounds, color, scale) local minX, minY = bounds.MinX, bounds.MinY local maxX, maxY = bounds.MaxX, bounds.MaxY local width = math.max(maxX - minX, 1) local height = math.max(maxY - minY, 1) local cornerBox = EnsureEspCornerBox(entry) local segmentLength = math.max(math.min(width, height) * 0.24, 5 * scale) segmentLength = math.min(segmentLength, width * 0.5, height * 0.5) SetEspCornerSegment(cornerBox[1], Vector2.new(minX, minY), Vector2.new(minX + segmentLength, minY), color, scale) SetEspCornerSegment(cornerBox[2], Vector2.new(maxX - segmentLength, minY), Vector2.new(maxX, minY), color, scale) SetEspCornerSegment(cornerBox[3], Vector2.new(minX, maxY), Vector2.new(minX + segmentLength, maxY), color, scale) SetEspCornerSegment(cornerBox[4], Vector2.new(maxX - segmentLength, maxY), Vector2.new(maxX, maxY), color, scale) SetEspCornerSegment(cornerBox[5], Vector2.new(minX, minY), Vector2.new(minX, minY + segmentLength), color, scale) SetEspCornerSegment(cornerBox[6], Vector2.new(minX, maxY - segmentLength), Vector2.new(minX, maxY), color, scale) SetEspCornerSegment(cornerBox[7], Vector2.new(maxX, minY), Vector2.new(maxX, minY + segmentLength), color, scale) SetEspCornerSegment(cornerBox[8], Vector2.new(maxX, maxY - segmentLength), Vector2.new(maxX, maxY), color, scale) end local function EnsureEspStatusText(entry) if entry.statusText then return entry.statusText end local statusText = Drawing.new('Text') statusText.Center = false statusText.Outline = true statusText.Font = 3 statusText.Color = PLAYER_CARD_THEME.BarTextColor statusText.Visible = false entry.statusText = statusText return statusText end EspClassic.HideBox = HideEspBox EspClassic.RemoveBox = RemoveEspBox EspClassic.DrawBox = DrawEspBox EspClassic.EnsureStatusText = EnsureEspStatusText end local function CreateESP(player) if ESPObjects[player] then return end local highlight = Instance.new('Highlight') highlight.DepthMode = Enum.HighlightDepthMode.AlwaysOnTop highlight.FillTransparency = 1 highlight.OutlineTransparency = 0 highlight.Enabled = false highlight.Parent = HighlightContainer local nameText = Drawing.new('Text') nameText.Center = false nameText.Outline = true nameText.Font = 3 nameText.Color = PLAYER_CARD_THEME.BarTextColor nameText.Visible = false local healthBarBg = CreateRoundedRect() local healthBarFill = CreateRoundedRect() local healthText = Drawing.new('Text') healthText.Center = true healthText.Outline = true healthText.Font = 3 healthText.Color = PLAYER_CARD_THEME.BarTextColor healthText.Visible = false local healthSuffixText = Drawing.new('Text') healthSuffixText.Center = false healthSuffixText.Outline = true healthSuffixText.Font = 3 healthSuffixText.Color = PLAYER_CARD_THEME.BarTextColor healthSuffixText.Visible = false local arrow = CreateArrowIndicator() local metaText = Drawing.new('Text') metaText.Center = false metaText.Outline = true metaText.Font = 3 metaText.Color = PLAYER_CARD_THEME.BarTextColor metaText.Visible = false local ammoText = Drawing.new('Text') ammoText.Center = true ammoText.Outline = true ammoText.Font = 3 ammoText.Color = PLAYER_CARD_THEME.BarTextColor ammoText.Visible = false local distanceText = Drawing.new('Text') distanceText.Center = false distanceText.Outline = true distanceText.Font = 3 distanceText.Color = PLAYER_CARD_THEME.BarTextColor distanceText.Visible = false local lookLine = Drawing.new('Line') lookLine.Thickness = 1 lookLine.Transparency = PLAYER_CARD_THEME.LookLineTransparency lookLine.Visible = false ESPObjects[player] = { highlight = highlight, characterParts = nil, nameText = nameText, healthBarBg = healthBarBg, healthBarFill = healthBarFill, healthText = healthText, healthSuffixText = healthSuffixText, arrow = arrow, metaText = metaText, ammoText = ammoText, distanceText = distanceText, lookLine = lookLine, } AttachWeaponInfoObservers(player) RivalsRuntimeBridge.ESPClassic.AttachStatusObservers(player, ESPObjects[player]) end local function DestroyESP(player) local entry = ESPObjects[player] if not entry then return end pcall(function() entry.highlight:Destroy() end) pcall(function() entry.nameText:Remove() end) RemoveRoundedRect(entry.healthBarBg) RemoveRoundedRect(entry.healthBarFill) pcall(function() entry.healthText:Remove() end) pcall(function() entry.healthSuffixText:Remove() end) RemoveArrowIndicator(entry.arrow) pcall(function() entry.metaText:Remove() end) pcall(function() entry.ammoText:Remove() end) pcall(function() entry.distanceText:Remove() end) pcall(function() entry.lookLine:Remove() end) if entry.statusText then pcall(function() entry.statusText:Remove() end) end RivalsRuntimeBridge.ESPClassic.RemoveBox(entry) if RivalsRuntimeBridge.PlayerChams then RivalsRuntimeBridge.PlayerChams.Clear(player) end RivalsRuntimeBridge.ESPClassic.DisconnectStatusObservers(entry) DisconnectWeaponInfoConnections(player) WeaponInfoCache[player] = nil RivalsRuntimeBridge.PlayerStatusInfoCache[player] = nil ESPObjects[player] = nil end local function DestroyAllESP() for player in pairs(ESPObjects) do DestroyESP(player) end end local function HideESP(entry) entry.highlight.Enabled = false SetDrawingVisible(entry.nameText, false) HideRoundedRect(entry.healthBarBg) HideRoundedRect(entry.healthBarFill) SetDrawingVisible(entry.healthText, false) SetDrawingVisible(entry.healthSuffixText, false) HideArrowIndicator(entry.arrow) SetDrawingVisible(entry.metaText, false) SetDrawingVisible(entry.ammoText, false) SetDrawingVisible(entry.distanceText, false) SetDrawingVisible(entry.lookLine, false) if entry.statusText then SetDrawingVisible(entry.statusText, false) end RivalsRuntimeBridge.ESPClassic.HideBox(entry) end local function ShouldShow(player, showTeammates) if player == LP then return false end if showTeammates then return true end local myTeam = LP:GetAttribute('TeamID') local theirTeam = player:GetAttribute('TeamID') if myTeam and theirTeam then return myTeam ~= theirTeam end if player.Team == nil or LP.Team == nil then return true end return player.Team ~= LP.Team end local function IsPlayerInLocalEnvironment(player) if not player or player == LP or player.Parent ~= Players then return false end local localCharacter = LP.Character local localEnvironmentId = localCharacter and localCharacter:GetAttribute('EnvironmentID') or nil if localEnvironmentId == nil then return false end local character = player.Character return character ~= nil and character.Parent ~= nil and character:GetAttribute('EnvironmentID') == localEnvironmentId end local function ReconcileESPPlayers() local showTeammates = EspRenderSettings.ShowTeammates for player in pairs(ESPObjects) do if not IsPlayerInLocalEnvironment(player) or not ShouldShow(player, showTeammates) then DestroyESP(player) end end for _, player in ipairs(Players:GetPlayers()) do if IsPlayerInLocalEnvironment(player) and ShouldShow(player, showTeammates) then CreateESP(player) end end end local function IsRivalsPickupPlace() return type(CurrentRivalsPlace) == 'number' and ( CurrentRivalsPlace == RIVALS_SHARED_MATCH_PLACE or CurrentRivalsPlace == 71874690745115 or CurrentRivalsPlace == 133215910299950 ) end local function ResolveRivalsDataRemote(remoteName, expectedClass) expectedClass = expectedClass or 'RemoteEvent' local remotesFolder = ReplicatedStorage:FindFirstChild('Remotes') local dataFolder = remotesFolder and remotesFolder:FindFirstChild('Data') local remote = dataFolder and dataFolder:FindFirstChild(remoteName) if remote and remote:IsA(expectedClass) then return remote end return nil end local function GetRivalsPickupTouchPart() if not InitCharacter() or not Char or not HumanoidRootPart then return nil end return Char:FindFirstChild('HitboxBody') or HumanoidRootPart or Char:FindFirstChildWhichIsA('BasePart') end local function GetRivalsPickupKind(drop) if not drop or not drop:IsA('BasePart') or drop.Name ~= '_drop' then return nil end if drop:FindFirstChild('Health') then return 'Health' end if drop:FindFirstChild('AmmoBalanced') or drop:FindFirstChild('Ammo') then return 'Ammo' end return nil end local function IsRivalsSpawnShieldActive(character) if not character or not character.Parent then return false end local player = Players:GetPlayerFromCharacter(character) local fighter = player and TrackedFightersByPlayer[player] if not fighter then local fighterController = ResolveFighterController() if fighterController and type(fighterController.GetFighter) == 'function' then fighter = player and fighterController:GetFighter(player) or nil if not fighter then fighter = fighterController:GetFighter(character) end end end if not fighter then return false end return FighterDataCache.ReadEntity(fighter.Entity) end local function TouchRivalsPickupDrop(drop, touchPart) if not drop or not drop.Parent or not touchPart or not touchPart.Parent then return false end firetouchinterest(touchPart, drop, 0) task.defer(function() if drop.Parent and touchPart.Parent then firetouchinterest(touchPart, drop, 1) end end) return true end local LocalPickupObserver = { RetryActive = false, RetryDelay = 0.05, RetryNonce = 0, } -- ====================================================================== -- Pickup drops (health / ammo) tracking -- ====================================================================== do local pickupDropsByKind = { Health = {}, Ammo = {}, } local pickupDropObservers = {} local function ClearPickupDropKinds(drop) if not drop then return end pickupDropsByKind.Health[drop] = nil pickupDropsByKind.Ammo[drop] = nil end local function DisconnectPickupDropObserver(drop) local observer = pickupDropObservers[drop] if not observer then return end local childAddedConnection = observer.ChildAddedConnection if childAddedConnection then pcall(function() childAddedConnection:Disconnect() end) end local childRemovedConnection = observer.ChildRemovedConnection if childRemovedConnection then pcall(function() childRemovedConnection:Disconnect() end) end pickupDropObservers[drop] = nil end local function RefreshPickupDrop(drop) ClearPickupDropKinds(drop) if not drop or drop.Parent ~= Workspace or not drop:IsA('BasePart') or drop.Name ~= '_drop' then return end local dropKind = GetRivalsPickupKind(drop) local hasTouchInterest = drop:FindFirstChildOfClass('TouchTransmitter') ~= nil if dropKind and hasTouchInterest then pickupDropsByKind[dropKind][drop] = true if UpdateRivalsPickupFeatures then UpdateRivalsPickupFeatures() end end end function LocalPickupObserver.ObserveDrop(drop) if not drop or not drop:IsA('BasePart') or drop.Name ~= '_drop' then return end if pickupDropObservers[drop] then RefreshPickupDrop(drop) return end local observer = {} pickupDropObservers[drop] = observer observer.ChildAddedConnection = drop.ChildAdded:Connect(GuardRivalsCallback('PickupDrop_ChildAdded', function() RefreshPickupDrop(drop) end)) observer.ChildRemovedConnection = drop.ChildRemoved:Connect(GuardRivalsCallback('PickupDrop_ChildRemoved', function() RefreshPickupDrop(drop) end)) RefreshPickupDrop(drop) end function LocalPickupObserver.UnobserveDrop(drop) ClearPickupDropKinds(drop) DisconnectPickupDropObserver(drop) end function LocalPickupObserver.ResetDropTracking() for drop in pairs(pickupDropObservers) do DisconnectPickupDropObserver(drop) end for drop in pairs(pickupDropsByKind.Health) do pickupDropsByKind.Health[drop] = nil end for drop in pairs(pickupDropsByKind.Ammo) do pickupDropsByKind.Ammo[drop] = nil end end function LocalPickupObserver.FindNearestDrop(pickupKind, touchPart) if not IsRivalsPickupPlace() or not touchPart or not touchPart.Parent then return nil end if pickupKind == 'Health' and (not Humanoid or Humanoid.Health >= Humanoid.MaxHealth) then return nil end local nearestDrop = nil local nearestDistance = nil local candidateDrops = pickupDropsByKind[pickupKind] if not candidateDrops then return nil end for drop in pairs(candidateDrops) do if drop.Parent ~= Workspace then ClearPickupDropKinds(drop) DisconnectPickupDropObserver(drop) else local dropKind = GetRivalsPickupKind(drop) local hasTouchInterest = drop:FindFirstChildOfClass('TouchTransmitter') ~= nil if dropKind == pickupKind and drop.CanTouch == true and hasTouchInterest then local distance = (drop.Position - touchPart.Position).Magnitude if not nearestDistance or distance < nearestDistance then nearestDrop = drop nearestDistance = distance end end end end return nearestDrop end end function LocalPickupObserver.CancelRetry() LocalPickupObserver.RetryActive = false LocalPickupObserver.RetryNonce = LocalPickupObserver.RetryNonce + 1 end function LocalPickupObserver.SyncRetry(shouldRetry) if not shouldRetry then LocalPickupObserver.CancelRetry() return end if LocalPickupObserver.RetryActive then return end LocalPickupObserver.RetryActive = true LocalPickupObserver.RetryNonce = LocalPickupObserver.RetryNonce + 1 local retryNonce = LocalPickupObserver.RetryNonce task.spawn(GuardRivalsCallback('Pickup_Retry', function() while LocalPickupObserver.RetryActive and LocalPickupObserver.RetryNonce == retryNonce do task.wait(LocalPickupObserver.RetryDelay) if LocalPickupObserver.RetryNonce ~= retryNonce or not LocalPickupObserver.RetryActive then return end if not UpdateRivalsPickupFeatures or not UpdateRivalsPickupFeatures() then LocalPickupObserver.RetryActive = false return end end end)) end UpdateRivalsPickupFeatures = function() if not __ZkhHub_genv.ZkxHubCaps.gate('Auto Pickup', 'firetouchinterest') then LocalPickupObserver.CancelRetry() return false end if not IsRivalsPickupPlace() or not InitCharacter() or not Humanoid or Humanoid.Health <= 0 then LocalPickupObserver.CancelRetry() return false end local touchPart = GetRivalsPickupTouchPart() if not touchPart then LocalPickupObserver.CancelRetry() return false end local touchedDrop = false local shouldRetry = false local missingHealth = math.max(Humanoid.MaxHealth - Humanoid.Health, 0) if Toggles.P8S1T1 and Toggles.P8S1T1.Value and missingHealth >= 25 then shouldRetry = true local healthDrop = LocalPickupObserver.FindNearestDrop('Health', touchPart) if healthDrop then touchedDrop = TouchRivalsPickupDrop(healthDrop, touchPart) end end local fighter = ResolveLocalFighter() local item = fighter and fighter.EquippedItem or nil local ammo = ReadItemValue(item, 'Ammo') if not touchedDrop and Toggles.P8S1T2 and Toggles.P8S1T2.Value and type(ammo) == 'number' and ammo <= 0 then shouldRetry = true local ammoDrop = LocalPickupObserver.FindNearestDrop('Ammo', touchPart) if ammoDrop then TouchRivalsPickupDrop(ammoDrop, touchPart) end end LocalPickupObserver.SyncRetry(shouldRetry) return shouldRetry end local function RefreshLocalPickupHealthObserver() if not IsRivalsPickupPlace() or not InitCharacter() or not Humanoid then DisconnectWeaponInfoConnection(LocalPickupObserver, 'HealthChanged') return end ConnectWeaponInfoSignal(Humanoid.HealthChanged, LocalPickupObserver, 'HealthChanged', function() local missingHealth = math.max(Humanoid.MaxHealth - Humanoid.Health, 0) if missingHealth >= 25 then UpdateRivalsPickupFeatures() end end) end -- ====================================================================== -- Weapon mods: hooks, input prototypes, knife extender -- ====================================================================== do local function IsRivalsModToggleEnabled(toggleId) local toggle = Toggles[toggleId] return toggle ~= nil and toggle.Value == true end local function ReadRivalsModNumber(optionId, fallback) local option = Options[optionId] if option ~= nil and type(option.Value) == 'number' then return option.Value end return fallback end RivalsModsState.ZkhHubInputBinding = {} RivalsModsState.ZkhHubInputBinding.__index = RivalsModsState.ZkhHubInputBinding function RivalsModsState.ZkhHubInputBinding.new(handleSetEnabled, handleDestroy) return setmetatable({ _handleSetEnabled = handleSetEnabled, _handleDestroy = handleDestroy, enabled = false, }, RivalsModsState.ZkhHubInputBinding) end function RivalsModsState.ZkhHubInputBinding:SetHandler(handler) self.handler = handler end function RivalsModsState.ZkhHubInputBinding:SetEnabled(enabled) self.enabled = enabled self._handleSetEnabled(enabled) end function RivalsModsState.ZkhHubInputBinding:Destroy() self._handleDestroy() end RivalsModsState.ZkhHubInputHookErrorReporter = {} RivalsModsState.ZkhHubInputHookErrorReporter.__index = RivalsModsState.ZkhHubInputHookErrorReporter function RivalsModsState.ZkhHubInputHookErrorReporter.new() return setmetatable({ _errors = {}, }, RivalsModsState.ZkhHubInputHookErrorReporter) end function RivalsModsState.ZkhHubInputHookErrorReporter:ReportResult(result) if not result.ok then table.insert(self._errors, result.error) ReportRivalsRuntimeIssue('mods_no_spread_input_hook', result.error) end return result end function RivalsModsState.ZkhHubInputHookErrorReporter:Destroy() table.clear(self._errors) end function RivalsModsState.CreateZkhHubInputHookPrototype(clientItem, useItemRemote, decodeInput) local cleanRemote = Instance.new('RemoteEvent') local fireServerNative = clonefunction(cleanRemote.FireServer) local voidOk = { ok = true, } local function inputHookError(stage, message) return { ok = false, error = 'InputHook/' .. stage .. ': ' .. message, } end local inputHookPrototype = {} inputHookPrototype.__index = inputHookPrototype function inputHookPrototype.new() local inputHook = setmetatable({ _errorReporter = RivalsModsState.ZkhHubInputHookErrorReporter.new(), _inputBindings = {}, _dummy = Color3.new(), }, inputHookPrototype) inputHook:_Initialize() return inputHook end function inputHookPrototype:_Initialize() local function fireServer(_, objectId, encodedType, args) local packet = { block = false, objectId = objectId, type = decodeInput(encodedType), args = args, } self:_Dispatch(packet) if packet.block then return end return fireServerNative(useItemRemote, objectId, encodedType, args, nil) end local remoteTree = { Replication = { Fighter = { UseItem = { FireServer = fireServer, }, }, }, } setrawmetatable(self._dummy, { __index = function() return remoteTree end, }) end function inputHookPrototype:Reserve() local binding binding = RivalsModsState.ZkhHubInputBinding.new(function(enabled) if enabled then self._errorReporter:ReportResult(self:_Load()) end end, function() for index, candidate in ipairs(self._inputBindings) do if candidate == binding then table.remove(self._inputBindings, index) break end end end) table.insert(self._inputBindings, binding) return binding end function inputHookPrototype:_Dispatch(packet) for _, binding in ipairs(self._inputBindings) do if binding.enabled and binding.handler ~= nil then binding.handler(packet) if packet.block then break end end end end function inputHookPrototype:_Load() if self._restore ~= nil then return voidOk end local inputMethod = rawget(clientItem, 'Input') if inputMethod == nil then return inputHookError('function_lookup', 'Failed to find Input in prototype') end for index, value in pairs(debug.getupvalues(inputMethod)) do if typeof(value) == 'Instance' then local matches = false if compareinstances ~= nil then matches = compareinstances(value, ReplicatedStorage) end if not matches then matches = value == ReplicatedStorage end if matches then debug.setupvalue(inputMethod, index, self._dummy) self._restore = { method = inputMethod, index = index, original = value, } end end end if self._restore == nil then for _, bundle in pairs(debug.getupvalues(inputMethod)) do if type(bundle) == 'table' then for key, value in pairs(bundle) do local matches = false if typeof(value) == 'Instance' then if compareinstances ~= nil then matches = compareinstances(value, ReplicatedStorage) end if not matches then matches = value == ReplicatedStorage end end if matches then self._restore = { bundle = bundle, key = key, original = value, } bundle[key] = self._dummy break end end if self._restore ~= nil then break end end end end if self._restore ~= nil then return voidOk end return inputHookError('upvalue_scan', 'Failed to find ReplicatedStorage upvalue') end function inputHookPrototype:_Revert() local restore = self._restore if restore == nil then return end self._restore = nil if restore.bundle ~= nil then restore.bundle[restore.key] = restore.original else debug.setupvalue(restore.method, restore.index, restore.original) end end function inputHookPrototype:Destroy() self:_Revert() self._errorReporter:Destroy() end return inputHookPrototype end RivalsModsState.ZkhHubNoSpreadPrototype = {} RivalsModsState.ZkhHubNoSpreadPrototype.__index = RivalsModsState.ZkhHubNoSpreadPrototype function RivalsModsState.ZkhHubNoSpreadPrototype.new(inputBinding, playerContext) local noSpread = setmetatable({ _inputBinding = inputBinding, _playerContext = playerContext, }, RivalsModsState.ZkhHubNoSpreadPrototype) inputBinding:SetHandler(function(packet) noSpread:_HandleInput(packet) end) return noSpread end function RivalsModsState.ZkhHubNoSpreadPrototype:SetEnabled(enabled) self._inputBinding:SetEnabled(enabled) end function RivalsModsState.ZkhHubNoSpreadPrototype:_HandleInput(packet) if packet.type ~= 'StartShooting' then return end local inner = self._playerContext.inner if inner == nil then return end local item = inner.fighterState:GetItemById(packet.objectId) if item == nil then return end if rawget(rawget(item, 'Info'), 'Type') == 'Gun' then packet.args['\2'] = true end end function RivalsModsState.ZkhHubNoSpreadPrototype:Destroy() self._inputBinding:Destroy() end -- Knife Extender: moves the swing forward before it is sent, so targets past the knife's normal reach still land. -- The melee packet is { ["\1"] = { ["\0"] = origin CFrame, ["\1"] = aim CFrame, ["\2"] = hit part, ["\3"] = hit offset }, ["\2"] = animation } -- (CFrames are encoded as { ["\0"] = x, ["\1"] = y, ["\2"] = z, ... rotation }). A swing is only moved when the part under the -- crosshair is out of the knife's real reach, by just enough to bring it back inside, and never by more than the Extra Range. function RivalsModsState.ComputeKnifeExtenderShift(origin, point, reach, range) local offset = point - origin local distance = offset.Magnitude if distance < 0.001 then return nil end local needed = distance - reach if needed <= 0.05 then return nil end if needed > range + 0.5 then return nil end return offset.Unit * math.min(needed + 0.3, range) end RivalsModsState.ZkhHubKnifeExtenderPrototype = {} RivalsModsState.ZkhHubKnifeExtenderPrototype.__index = RivalsModsState.ZkhHubKnifeExtenderPrototype function RivalsModsState.ZkhHubKnifeExtenderPrototype.new(inputBinding, playerContext) local extender = setmetatable({ _inputBinding = inputBinding, _playerContext = playerContext, _range = 3, _scope = 'Knife', _heavy = true, }, RivalsModsState.ZkhHubKnifeExtenderPrototype) inputBinding:SetHandler(function(packet) extender:_HandleInput(packet) end) return extender end function RivalsModsState.ZkhHubKnifeExtenderPrototype:SetEnabled(enabled) self._inputBinding:SetEnabled(enabled) end function RivalsModsState.ZkhHubKnifeExtenderPrototype:SetOptions(range, scope, heavy) self._range = range self._scope = scope self._heavy = heavy end function RivalsModsState.ZkhHubKnifeExtenderPrototype:_HandleInput(packet) local isHeavy = packet.type == 'StartAiming' if not (packet.type == 'StartShooting' or (isHeavy and self._heavy)) then return end local inner = self._playerContext.inner if inner == nil then return end local item = inner.fighterState:GetItemById(packet.objectId) if item == nil then return end local info = rawget(item, 'Info') if type(info) ~= 'table' or rawget(info, 'Type') ~= 'Melee' then return end if isHeavy and type(rawget(info, 'HeavyAttackReach')) ~= 'number' then return end if self._scope ~= 'All melee' then local okName, itemName = pcall(function() return item.Name end) if not (okName and itemName == 'Knife') then return end end local args = packet.args local data = type(args) == 'table' and args['\1'] or nil if type(data) ~= 'table' then return end local part = data['\2'] local okPart, isPart = pcall(function() return part ~= nil and part:IsA('BasePart') end) if not (okPart and isPart) then return end local originEncoded, aimEncoded = data['\0'], data['\1'] if type(originEncoded) ~= 'table' or type(originEncoded['\0']) ~= 'number' or type(originEncoded['\1']) ~= 'number' or type(originEncoded['\2']) ~= 'number' then return end local origin = Vector3.new(originEncoded['\0'], originEncoded['\1'], originEncoded['\2']) -- the game's own reach, not the value the Extend setting may have raised on the item local original = RivalsModsState.OriginalItemInfo[item] local reach if isHeavy then reach = original and original.HeavyAttackReach or rawget(info, 'HeavyAttackReach') else reach = original and original.AttackReach or rawget(info, 'AttackReach') end if type(reach) ~= 'number' then return end local point = part.Position pcall(function() point = part:GetClosestPointOnSurface(origin) end) local shift = RivalsModsState.ComputeKnifeExtenderShift(origin, point, reach, self._range) if shift == nil then return end local function moved(encoded) if type(encoded) ~= 'table' or type(encoded['\0']) ~= 'number' or type(encoded['\1']) ~= 'number' or type(encoded['\2']) ~= 'number' then return encoded end local copy = table.clone(encoded) copy['\0'] = encoded['\0'] + shift.X copy['\1'] = encoded['\1'] + shift.Y copy['\2'] = encoded['\2'] + shift.Z return copy end local newData = table.clone(data) newData['\0'] = moved(originEncoded) newData['\1'] = moved(aimEncoded) args['\1'] = newData end function RivalsModsState.ZkhHubKnifeExtenderPrototype:Destroy() self._inputBinding:Destroy() end function RivalsModsState.SyncKnifeExtender() local controller = RivalsModsState.KnifeExtenderController if not controller then return false end local scopeOption = Options and Options.KE_Scope controller:SetOptions( math.clamp(ReadRivalsModNumber('KE_Range', 3), 1, 40), (scopeOption and scopeOption.Value == 'All melee') and 'All melee' or 'Knife', IsRivalsModToggleEnabled('KE_Heavy') ) local enabled = IsRivalsModToggleEnabled('KE_Enabled') if controller._inputBinding.enabled ~= enabled then controller:SetEnabled(enabled) end return true end RivalsModsState.ZkhHubGrenadeFusePrototype = {} RivalsModsState.ZkhHubGrenadeFusePrototype.__index = RivalsModsState.ZkhHubGrenadeFusePrototype function RivalsModsState.ZkhHubGrenadeFusePrototype.new(inputBinding, playerContext) local grenadeFuse = setmetatable({ _inputBinding = inputBinding, _playerContext = playerContext, _explodeOn = 'Impact', _removeFuse = false, _trajectoryVisual = nil, _startThrowReplacerInstalled = false, _throwablePrototype = nil, _originalStartThrow = nil, }, RivalsModsState.ZkhHubGrenadeFusePrototype) inputBinding:SetHandler(function(packet) grenadeFuse:_HandleInput(packet) end) return grenadeFuse end function RivalsModsState.ZkhHubGrenadeFusePrototype:SetEnabled(enabled) self._inputBinding:SetEnabled(enabled) end function RivalsModsState.ZkhHubGrenadeFusePrototype:SetExplodeOn(explodeOn) self._explodeOn = explodeOn end function RivalsModsState.ZkhHubGrenadeFusePrototype:SetRemoveFuse(removeFuse) self._removeFuse = removeFuse if removeFuse then self:_EnsureStartThrowReplacer() else self:_RestoreStartThrowReplacer() end end function RivalsModsState.ZkhHubGrenadeFusePrototype:_EnsureStartThrowReplacer() if self._startThrowReplacerInstalled then return end local playerScripts = LP:FindFirstChild('PlayerScripts') local modules = playerScripts and playerScripts:FindFirstChild('Modules') local itemTypes = modules and modules:FindFirstChild('ItemTypes') local throwableModule = itemTypes and itemTypes:FindFirstChild('Throwable') if not throwableModule then return end local ok, throwable = pcall(require, throwableModule) if not ok or type(throwable) ~= 'table' then return end local original = rawget(throwable, '_StartThrow') if type(original) ~= 'function' then return end self._throwablePrototype = throwable self._originalStartThrow = original local controller = self rawset(throwable, '_StartThrow', function(item, ...) if controller._removeFuse then rawset(item, '_cook_detonate_delay', nil) end return original(item, ...) end) self._startThrowReplacerInstalled = true end function RivalsModsState.ZkhHubGrenadeFusePrototype:_RestoreStartThrowReplacer() if not self._startThrowReplacerInstalled then return end if self._throwablePrototype and self._originalStartThrow then rawset(self._throwablePrototype, '_StartThrow', self._originalStartThrow) end self._startThrowReplacerInstalled = false self._throwablePrototype = nil self._originalStartThrow = nil end function RivalsModsState.ZkhHubGrenadeFusePrototype:_ComputeImpactTime(trajectoryVisual) local lastArgs = rawget(trajectoryVisual, '_last_args') local step = lastArgs[4] if not step then step = 0.05 end local cap = lastArgs[6] if not cap then cap = math.huge end local impactCFrame = rawget(trajectoryVisual, '_impact_sphere').CFrame local segments = rawget(trajectoryVisual, '_segments') local key while true do local segment key, segment = next(segments, key) if key == nil then break end if segment.CFrame == impactCFrame then return math.min(cap, (key - 2) * step) end end return nil end function RivalsModsState.ZkhHubGrenadeFusePrototype:_HandleInput(packet) local packetType = packet.type local isStart = packetType == 'StartShooting' or packetType == 'StartAiming' local isFinish = packetType == 'FinishShooting' or packetType == 'FinishAiming' if not (isStart or isFinish) then return end local inner = self._playerContext.inner if inner == nil then return end local item = inner.fighterState:GetItemById(packet.objectId) if item == nil then return end if not rawget(rawget(item, 'Info'), 'CanCook') then return end if isStart then self._trajectoryVisual = rawget(item, '_trajectory_visual') if self._removeFuse then rawset(item, '_cook_detonate_delay', nil) end return end if self._explodeOn == 'Throw' then packet.args['\3'] = 0 else local trajectoryVisual = self._trajectoryVisual if trajectoryVisual then trajectoryVisual = self:_ComputeImpactTime(trajectoryVisual) end packet.args['\3'] = trajectoryVisual end self._trajectoryVisual = nil end function RivalsModsState.ZkhHubGrenadeFusePrototype:Destroy() self:_RestoreStartThrowReplacer() self._inputBinding:Destroy() end RivalsModsState.ZkhHubLocalFighterState = {} RivalsModsState.ZkhHubLocalFighterState.__index = RivalsModsState.ZkhHubLocalFighterState function RivalsModsState.ZkhHubLocalFighterState.new() return setmetatable({}, RivalsModsState.ZkhHubLocalFighterState) end function RivalsModsState.ZkhHubLocalFighterState:GetItems() local fighter = ResolveLocalFighter() local items = type(fighter) == 'table' and rawget(fighter, 'Items') or nil if type(items) == 'table' then return items end return {} end function RivalsModsState.ZkhHubLocalFighterState:GetItemById(objectId) for _, item in next, self:GetItems() do if objectId == rawget(rawget(item, 'Data'), 'ObjectID') then return item end end return nil end function RivalsModsState.ResolveNoSpreadClientItem() local state = RivalsModsState if state.NoSpreadClientItem then return state.NoSpreadClientItem end local playerScripts = LP:FindFirstChild('PlayerScripts') local modules = playerScripts and playerScripts:FindFirstChild('Modules') local replicated = modules and modules:FindFirstChild('ClientReplicatedClasses') local fighter = replicated and replicated:FindFirstChild('ClientFighter') local clientItemModule = fighter and fighter:FindFirstChild('ClientItem') if not clientItemModule then return nil end local ok, clientItem = pcall(require, clientItemModule) if not ok or type(clientItem) ~= 'table' then return nil end state.NoSpreadClientItem = clientItem return clientItem end function RivalsModsState.ResolveNoSpreadRemote() local state = RivalsModsState if state.NoSpreadRemote then return state.NoSpreadRemote end local remotes = ReplicatedStorage:FindFirstChild('Remotes') local replication = remotes and remotes:FindFirstChild('Replication') local fighter = replication and replication:FindFirstChild('Fighter') local remote = fighter and fighter:FindFirstChild('UseItem') if not remote or not remote:IsA('RemoteEvent') then return nil end state.NoSpreadRemote = cloneref(remote) return state.NoSpreadRemote end function RivalsModsState.EnsureZkhHubInputHook() local state = RivalsModsState if state.ZkhHubInputHook and state.NoSpreadController and state.GrenadeFuseController and state.KnifeExtenderController and (RivalsRuntimeBridge.Extras.CoreLoaded ~= true or state.ExtrasController ~= nil) then return true end if type(setrawmetatable) ~= 'function' or type(clonefunction) ~= 'function' or type(debug.getupvalues) ~= 'function' or type(debug.setupvalue) ~= 'function' then return false end local clientItem = RivalsModsState.ResolveNoSpreadClientItem() local remote = RivalsModsState.ResolveNoSpreadRemote() local enumLibrary = ResolveEnumLibrary() local fromEnum = type(enumLibrary) == 'table' and rawget(enumLibrary, '_from_enum') or nil if type(clientItem) ~= 'table' or not remote or type(fromEnum) ~= 'table' then return false end local playerContext = { inner = { fighterState = RivalsModsState.ZkhHubLocalFighterState.new(), }, } local ok, inputHookOrError = pcall(function() local inputHookPrototype = state.ZkhHubInputHookPrototype if inputHookPrototype == nil then inputHookPrototype = RivalsModsState.CreateZkhHubInputHookPrototype( clientItem, remote, function(encodedType) return rawget(fromEnum, encodedType) end ) state.ZkhHubInputHookPrototype = inputHookPrototype end local inputHook = inputHookPrototype.new() state.ZkhHubInputHook = inputHook state.NoSpreadPlayerContext = playerContext state.NoSpreadController = RivalsModsState.ZkhHubNoSpreadPrototype.new(inputHook:Reserve(), playerContext) state.GrenadeFuseController = RivalsModsState.ZkhHubGrenadeFusePrototype.new(inputHook:Reserve(), playerContext) state.KnifeExtenderController = RivalsModsState.ZkhHubKnifeExtenderPrototype.new(inputHook:Reserve(), playerContext) state.ExtrasController = RivalsRuntimeBridge.Extras.NewController(inputHook:Reserve(), playerContext) or false return inputHook end) if not ok then ReportRivalsRuntimeIssue('mods_no_spread_input_hook_initialize', inputHookOrError) return false end return inputHookOrError ~= nil end function RivalsModsState.SyncNoSpreadEnabled() local state = RivalsModsState local controller = state.NoSpreadController if not controller then return false end local enabled = IsRivalsModToggleEnabled('P4S1T3') if controller._inputBinding.enabled ~= enabled then controller:SetEnabled(enabled) end return true end function RivalsModsState.SyncGrenadeFuse() local state = RivalsModsState local controller = state.GrenadeFuseController if not controller then return false end local enabled = IsRivalsModToggleEnabled('P4S1T6') local explodeOn = Options and Options.P4S1D1 and Options.P4S1D1.Value controller:SetExplodeOn(type(explodeOn) == 'string' and explodeOn or 'Impact') controller:SetRemoveFuse(IsRivalsModToggleEnabled('P4S1T7')) if controller._inputBinding.enabled ~= enabled then controller:SetEnabled(enabled) end return true end function RivalsModsState.RestoreZkhHubInputHook() local state = RivalsModsState if state.NoSpreadController then state.NoSpreadController:Destroy() end if state.GrenadeFuseController then state.GrenadeFuseController:Destroy() end if state.KnifeExtenderController then state.KnifeExtenderController:Destroy() end if state.ZkhHubInputHook then state.ZkhHubInputHook:Destroy() end state.NoSpreadController = nil state.GrenadeFuseController = nil state.KnifeExtenderController = nil RivalsRuntimeBridge.Extras.DestroyController(state.ExtrasController) state.ExtrasController = nil state.NoSpreadPlayerContext = nil state.ZkhHubInputHook = nil state.NoSpreadClientItem = nil state.NoSpreadRemote = nil end function RivalsModsState.StoreOriginalItemInfo(item) local state = RivalsModsState if type(item) ~= 'table' or state.OriginalItemInfo[item] ~= nil then return end local info = item.Info if type(info) ~= 'table' then return end local inputSpammingEnabled = rawget(info, 'InputSpammingEnabled') state.OriginalItemInfo[item] = { Info = info, HasDashCooldown = rawget(info, 'DashCooldown') ~= nil, DashCooldown = rawget(info, 'DashCooldown'), HasMaxDoubleJumps = rawget(info, 'MaxDoubleJumps') ~= nil, MaxDoubleJumps = rawget(info, 'MaxDoubleJumps'), AttackReach = rawget(info, 'AttackReach'), HeavyAttackReach = rawget(info, 'HeavyAttackReach'), HasInputSpammingEnabled = inputSpammingEnabled ~= nil, InputSpammingEnabled = type(inputSpammingEnabled) == 'table' and table.clone(inputSpammingEnabled) or inputSpammingEnabled, } end function RivalsModsState.ApplyPersistentItemModifiers(item) RivalsModsState.StoreOriginalItemInfo(item) local original = RivalsModsState.OriginalItemInfo[item] local info = original and original.Info or nil if type(info) ~= 'table' then return end if IsRivalsModToggleEnabled('P4S2T4') then local inputSpammingEnabled = original.InputSpammingEnabled if type(inputSpammingEnabled) == 'table' then local automaticInputSpamming = table.clone(inputSpammingEnabled) automaticInputSpamming.StartShooting = 0 automaticInputSpamming.StartReloading = 0 rawset(info, 'InputSpammingEnabled', automaticInputSpamming) end elseif original.HasInputSpammingEnabled then rawset( info, 'InputSpammingEnabled', type(original.InputSpammingEnabled) == 'table' and table.clone(original.InputSpammingEnabled) or original.InputSpammingEnabled ) else rawset(info, 'InputSpammingEnabled', nil) end if IsRivalsModToggleEnabled('P4S2T5') then rawset(info, 'MaxDoubleJumps', 676767) elseif original.HasMaxDoubleJumps then rawset(info, 'MaxDoubleJumps', original.MaxDoubleJumps) else rawset(info, 'MaxDoubleJumps', nil) end if IsRivalsModToggleEnabled('P4S1T5') and type(original.DashCooldown) == 'number' then local reduction = math.clamp(ReadRivalsModNumber('P4S1S5', 75), 0, 100) / 100 rawset(info, 'DashCooldown', original.DashCooldown * (1 - reduction)) elseif original.HasDashCooldown then rawset(info, 'DashCooldown', original.DashCooldown) else rawset(info, 'DashCooldown', nil) end if type(original.AttackReach) == 'number' then local extra = 0 if IsRivalsModToggleEnabled('KE_Enabled') then local scopeOption = Options and Options.KE_Scope local okName, itemName = pcall(function() return item.Name end) if (scopeOption and scopeOption.Value == 'All melee') or (okName and itemName == 'Knife') then extra = math.clamp(ReadRivalsModNumber('KE_Range', 3), 1, 40) end end rawset(info, 'AttackReach', original.AttackReach + extra) if type(original.HeavyAttackReach) == 'number' then rawset( info, 'HeavyAttackReach', original.HeavyAttackReach + (IsRivalsModToggleEnabled('KE_Heavy') and extra or 0) ) end end end function RivalsModsState.RestorePersistentItem(item) local state = RivalsModsState local original = state.OriginalItemInfo[item] local info = original and original.Info or nil if type(info) == 'table' then if original.HasInputSpammingEnabled then rawset( info, 'InputSpammingEnabled', type(original.InputSpammingEnabled) == 'table' and table.clone(original.InputSpammingEnabled) or original.InputSpammingEnabled ) else rawset(info, 'InputSpammingEnabled', nil) end if original.HasMaxDoubleJumps then rawset(info, 'MaxDoubleJumps', original.MaxDoubleJumps) else rawset(info, 'MaxDoubleJumps', nil) end if original.HasDashCooldown then rawset(info, 'DashCooldown', original.DashCooldown) else rawset(info, 'DashCooldown', nil) end if type(original.AttackReach) == 'number' then rawset(info, 'AttackReach', original.AttackReach) end if type(original.HeavyAttackReach) == 'number' then rawset(info, 'HeavyAttackReach', original.HeavyAttackReach) end end state.OriginalItemInfo[item] = nil end function RivalsModsState.DisconnectPersistentItemSignals() local state = RivalsModsState for _, connectionName in ipairs({ 'InfoModifierItemAdded', 'InfoModifierItemRemoved' }) do local connection = state[connectionName] if connection then pcall(function() connection:Disconnect() end) end state[connectionName] = nil end state.InfoModifierFighter = nil end function RivalsModsState.RestorePersistentItemModifiers(disconnectSignals) local state = RivalsModsState local trackedItems = {} for item in pairs(state.OriginalItemInfo) do trackedItems[#trackedItems + 1] = item end for _, item in ipairs(trackedItems) do RivalsModsState.RestorePersistentItem(item) end if disconnectSignals then RivalsModsState.DisconnectPersistentItemSignals() end end function RivalsModsState.RefreshPersistentItemModifiers() local state = RivalsModsState local fighter = ResolveLocalFighter() if fighter ~= state.InfoModifierFighter then RivalsModsState.RestorePersistentItemModifiers(true) state.InfoModifierFighter = fighter if fighter then if fighter.ItemAdded and type(fighter.ItemAdded.Connect) == 'function' then state.InfoModifierItemAdded = fighter.ItemAdded:Connect( GuardRivalsCallback( 'Mods_ItemAdded', function(item) RivalsModsState.ApplyPersistentItemModifiers(item) end ) ) end if fighter.ItemRemoved and type(fighter.ItemRemoved.Connect) == 'function' then state.InfoModifierItemRemoved = fighter.ItemRemoved:Connect( GuardRivalsCallback('Mods_ItemRemoved', function(item) RivalsModsState.RestorePersistentItem(item) end) ) end end end if not fighter or type(fighter.Items) ~= 'table' then return false end for _, item in pairs(fighter.Items) do RivalsModsState.ApplyPersistentItemModifiers(item) end return true end function RivalsModsState.ResolveCameraController() local state = RivalsModsState if state.CameraController then return state.CameraController end if not IsReplicatedStateReady() then return nil end local playerScripts = LP:FindFirstChild('PlayerScripts') local controllers = playerScripts and playerScripts:FindFirstChild('Controllers') local cameraControllerModule = controllers and controllers:FindFirstChild('CameraController') if not cameraControllerModule then return nil end local cameraController = RivalsRuntimeBridge.RequireGameModule(cameraControllerModule, 'CameraController') if not cameraController then return nil end state.CameraController = cameraController return cameraController end function RivalsModsState.UpdateCameraModifiers() local state = RivalsModsState local cameraController = RivalsModsState.ResolveCameraController() if cameraController then if IsRivalsModToggleEnabled('P4S2T6') then if state.CameraShakeOriginalEnabled == nil then state.CameraShakeOriginalEnabled = cameraController._shake_enabled end cameraController._shake_enabled = false elseif state.CameraShakeOriginalEnabled ~= nil then cameraController._shake_enabled = state.CameraShakeOriginalEnabled state.CameraShakeOriginalEnabled = nil end if IsRivalsModToggleEnabled('P4S2T8') then if not state.CameraThirdPersonCaptured then state.CameraThirdPersonCaptured = true state.CameraThirdPersonOriginal = cameraController._third_person_override end if cameraController._third_person_override ~= true and type(cameraController.SetThirdPersonOverride) == 'function' then cameraController:SetThirdPersonOverride(true) end -- the override only grants access to third person; step in once so the toggle shows without pressing the POV key if not state.CameraThirdPersonEntered then local cameraState = rawget(cameraController, 'CameraState') if type(cameraState) == 'table' and type(cameraState.GetPublicState) == 'function' and type(cameraState.TogglePOV) == 'function' and type(cameraState.States) == 'table' then local okState, current = pcall(cameraState.GetPublicState, cameraState) if okState and current ~= nil then if current == cameraState.States.FirstPerson then pcall(cameraState.TogglePOV, cameraState) end state.CameraThirdPersonEntered = true end end end elseif state.CameraThirdPersonCaptured then if type(cameraController.SetThirdPersonOverride) == 'function' then cameraController:SetThirdPersonOverride(state.CameraThirdPersonOriginal) end state.CameraThirdPersonCaptured = false state.CameraThirdPersonOriginal = nil state.CameraThirdPersonEntered = false end if type(cameraController.SetExternalFOVOffset) == 'function' then local targetFov = ReadRivalsModNumber('P4S2S4', 80) local baseFov = type(cameraController._base_fov) == 'number' and cameraController._base_fov or 80 local fovOffset = IsRivalsModToggleEnabled('P4S2T9') and (targetFov - baseFov) or 0 cameraController:SetExternalFOVOffset('ZkxHub', fovOffset) end end -- viewmodel effects only need the local fighter, so they no longer stop when the camera controller is unavailable if type(RivalsModsState.RefreshViewmodelEffects) == 'function' then RivalsModsState.RefreshViewmodelEffects() end end -- The game only writes the third-person camera offset when the camera state changes, -- so overriding the spring target every frame is enough to hold a custom offset. RivalsModsState.ThirdPersonDefaults = { ThirdPerson = Vector3.new(3, 1.5, 7.5), ThirdPersonMirrored = Vector3.new(-3, 1.5, 7.5), } function RivalsModsState.RestoreThirdPersonOffset() local state = RivalsModsState if not state.ThirdPersonOffsetApplied then return end state.ThirdPersonOffsetApplied = false local controller = state.CameraController local spring = controller and rawget(controller, '_pov_spring') local cameraState = controller and rawget(controller, 'CameraState') if type(spring) ~= 'table' or type(cameraState) ~= 'table' or type(cameraState.GetPublicState) ~= 'function' then return end local ok, current = pcall(cameraState.GetPublicState, cameraState) if ok and current ~= nil then local default = state.ThirdPersonDefaults[current] if default then spring.Target = default end end end function RivalsModsState.UpdateThirdPersonOffset() local state = RivalsModsState local enabled = IsRivalsModToggleEnabled('PX11T1') if not enabled and not state.ThirdPersonOffsetApplied then return end local controller = state.CameraController if enabled and not controller then controller = state.ResolveCameraController() end if not controller then return end local spring = rawget(controller, '_pov_spring') local cameraState = rawget(controller, 'CameraState') if type(spring) ~= 'table' or type(cameraState) ~= 'table' or type(cameraState.GetPublicState) ~= 'function' then return end local ok, current = pcall(cameraState.GetPublicState, cameraState) if not ok then return end local states = cameraState.States local isThirdPerson = type(states) == 'table' and (current == states.ThirdPerson or current == states.ThirdPersonMirrored) if enabled and isThirdPerson then local x = ReadRivalsModNumber('PX11S1', 3) if current == states.ThirdPersonMirrored then x = -x end spring.Target = Vector3.new(x, ReadRivalsModNumber('PX11S2', 1.5), ReadRivalsModNumber('PX11S3', 7.5)) state.ThirdPersonOffsetApplied = true elseif state.ThirdPersonOffsetApplied then state.RestoreThirdPersonOffset() end end function RivalsModsState.ShouldSuppressViewmodelAnimation(animator, animationKey) if type(animationKey) ~= 'string' then return false end if IsRivalsModToggleEnabled('P1S31T2') and (string.find(animationKey, 'Shoot', 1, true) ~= nil or animationKey == 'QuickShot') then return true end if IsRivalsModToggleEnabled('P1S31T3') and animationKey == rawget(animator, '_sprint_animation') then return true end if IsRivalsModToggleEnabled('P1S31T4') and animationKey == rawget(animator, '_equip_animation') then return true end if IsRivalsModToggleEnabled('P1S31T5') and string.find(animationKey, 'Reload', 1, true) ~= nil then return true end return false end function RivalsModsState.ApplyViewmodelMotionSuppression(items) if not IsRivalsModToggleEnabled('P1S31T1') then return end for _, item in pairs(items) do local viewModel = type(item) == 'table' and rawget(item, 'ViewModel') or nil if type(viewModel) == 'table' then for springName, value in pairs(RivalsModsState.MotionSpringValues) do local spring = rawget(viewModel, springName) if type(spring) == 'table' then pcall(function() spring.Value = value end) end end end end end -- Without the sprint animation the game falls back to its procedural sprint pose (arms swing and tilt), so No Sprint Animation -- also switches that pose off for the weapons in hand. The flag lives on the shared item info and is put back afterwards. RivalsModsState.OriginalProceduralSprint = setmetatable({}, { __mode = 'k' }) function RivalsModsState.RestoreSprintPose() local saved = RivalsModsState.OriginalProceduralSprint for info, record in pairs(saved) do pcall(rawset, info, 'DisableProceduralSprinting', record.value) saved[info] = nil end end function RivalsModsState.ApplySprintPoseSuppression(items) if not IsRivalsModToggleEnabled('P1S31T3') then RivalsModsState.RestoreSprintPose() return end local saved = RivalsModsState.OriginalProceduralSprint for _, item in pairs(items) do local viewModel = type(item) == 'table' and rawget(item, 'ViewModel') or nil local info = type(viewModel) == 'table' and rawget(viewModel, 'Info') or nil if type(info) == 'table' then if saved[info] == nil then saved[info] = { value = rawget(info, 'DisableProceduralSprinting') } end pcall(rawset, info, 'DisableProceduralSprinting', true) end end end -- The viewmodel springs are time based, so zeroing them just before the game's update leaves almost nothing behind; the -- RenderStepped pass alone can land after the update and leave a trace of the motion. function RivalsModsState.SyncMotionStep() local state = RivalsModsState local wanted = IsRivalsModToggleEnabled('P1S31T1') if wanted and not state.MotionStepBound then state.MotionStepBound = true RunService:BindToRenderStep( 'ZkxHubViewmodelCalm', Enum.RenderPriority.First.Value, GuardRivalsCallback('ViewmodelCalm_Step', function() local fighter = ResolveLocalFighter() if fighter and type(fighter.Items) == 'table' then RivalsModsState.ApplyViewmodelMotionSuppression(fighter.Items) end end) ) elseif not wanted and state.MotionStepBound then state.MotionStepBound = false pcall(function() RunService:UnbindFromRenderStep('ZkxHubViewmodelCalm') end) end end function RivalsModsState.RefreshViewmodelEffects() local state = RivalsModsState local fighter = ResolveLocalFighter() local items = fighter and type(fighter.Items) == 'table' and fighter.Items or {} state.ApplyViewmodelMotionSuppression(items) state.ApplySprintPoseSuppression(items) state.SyncMotionStep() RivalsRuntimeBridge.NativeRemovals.SyncAnimationHook() RivalsRuntimeBridge.NativeRemovals.SyncViewmodelOffsetHook() end function RivalsModsState.RestoreClientModifiers() local state = RivalsModsState state.RestoreThirdPersonOffset() if state.CameraController and state.CameraShakeOriginalEnabled ~= nil then state.CameraController._shake_enabled = state.CameraShakeOriginalEnabled end if state.CameraController and state.CameraThirdPersonCaptured and type(state.CameraController.SetThirdPersonOverride) == 'function' then state.CameraController:SetThirdPersonOverride(state.CameraThirdPersonOriginal) end if state.CameraController and type(state.CameraController.SetExternalFOVOffset) == 'function' then state.CameraController:SetExternalFOVOffset('ZkxHub', 0) end state.CameraShakeOriginalEnabled = nil state.CameraThirdPersonCaptured = false state.CameraThirdPersonOriginal = nil state.CameraThirdPersonEntered = false state.CameraViewModelOriginal = nil state.CameraController = nil state.RestoreSprintPose() if state.MotionStepBound then state.MotionStepBound = false pcall(function() RunService:UnbindFromRenderStep('ZkxHubViewmodelCalm') end) end RivalsRuntimeBridge.NativeRemovals.RevertViewmodelOffsetHook() RivalsRuntimeBridge.NativeRemovals.RevertAnimationHook() RivalsModsState.RestorePersistentItemModifiers(true) end function RivalsModsState.ResolveGunModule() local state = RivalsModsState if state.GunModule then return state.GunModule end if not IsReplicatedStateReady() then return nil end local playerScripts = LP:FindFirstChild('PlayerScripts') local modules = playerScripts and playerScripts:FindFirstChild('Modules') local itemTypes = modules and modules:FindFirstChild('ItemTypes') local gunModuleScript = itemTypes and itemTypes:FindFirstChild('Gun') if not gunModuleScript then return nil end local ok, gunModule = pcall(require, gunModuleScript) if not ok then return nil end state.GunModule = gunModule return gunModule end function RivalsModsState.ResolveMeleeModule() local state = RivalsModsState if state.MeleeModule then return state.MeleeModule end if not IsReplicatedStateReady() then return nil end local playerScripts = LP:FindFirstChild('PlayerScripts') local modules = playerScripts and playerScripts:FindFirstChild('Modules') local itemTypes = modules and modules:FindFirstChild('ItemTypes') local meleeModuleScript = itemTypes and itemTypes:FindFirstChild('Melee') if not meleeModuleScript then return nil end local ok, meleeModule = pcall(require, meleeModuleScript) if not ok then return nil end state.MeleeModule = meleeModule return meleeModule end function RivalsModsState.ResolveGunbladeModule() local state = RivalsModsState if state.GunbladeModule then return state.GunbladeModule end if not IsReplicatedStateReady() then return nil end local playerScripts = LP:FindFirstChild('PlayerScripts') local modules = playerScripts and playerScripts:FindFirstChild('Modules') local items = modules and modules:FindFirstChild('Items') local gunbladeModuleScript = items and items:FindFirstChild('Gunblade') if not gunbladeModuleScript then return nil end local ok, gunbladeModule = pcall(require, gunbladeModuleScript) if not ok then return nil end state.GunbladeModule = gunbladeModule return gunbladeModule end function RivalsModsState.ResolveGunStartShootingBase(fallback) local gunModule = RivalsModsState.ResolveGunModule() if gunModule and type(gunModule.StartShooting) == 'function' then local originalGunStartShooting = RivalsModsState.OriginalGunStartShooting if fallback == originalGunStartShooting or fallback == gunModule.StartShooting then return gunModule.StartShooting end end local meleeModule = RivalsModsState.ResolveMeleeModule() if meleeModule and type(meleeModule.StartShooting) == 'function' then local originalMeleeStartShooting = RivalsModsState.OriginalMeleeStartShooting if fallback == originalMeleeStartShooting or fallback == meleeModule.StartShooting then return meleeModule.StartShooting end end local gunbladeModule = RivalsModsState.ResolveGunbladeModule() if gunbladeModule and type(gunbladeModule.StartShooting) == 'function' then local originalGunbladeStartShooting = RivalsModsState.OriginalGunbladeStartShooting if fallback == originalGunbladeStartShooting or fallback == gunbladeModule.StartShooting then return gunbladeModule.StartShooting end end return fallback end local function ResolveRivalsModsStartShootingItem(item) if type(item) == 'table' and type(item.Name) == 'string' and item.Name ~= '' then return item end local fighter = ResolveLocalFighter() local equippedItem = fighter and fighter.EquippedItem or nil if type(equippedItem) == 'table' and type(equippedItem.Name) == 'string' and equippedItem.Name ~= '' then return equippedItem end return nil end function ResolveRivalsWeaponSpeedScale() if not IsRivalsModToggleEnabled('P4S1T1') then return 1 end local weaponSpeedBoost = math.clamp(ReadRivalsModNumber('P4S1S4', 85), 0, 85) return 1 / (1 + (weaponSpeedBoost / 100)) end local function ResolveRivalsReloadKey(item, reloadEnum) local info = item and item.Info or nil if not info then return nil end if reloadEnum ~= nil and type(item.FromEnum) == 'function' then local ok, resolvedReloadKey = pcall(function() return item:FromEnum(reloadEnum) end) if ok and type(resolvedReloadKey) == 'string' then return resolvedReloadKey end end local currentAmmo = ReadItemValue(item, 'Ammo') if type(currentAmmo) == 'number' and currentAmmo <= 0 and info.HasEmptyReload then return 'EmptyReload' end return 'Reload' end local function WithScaledRivalsReloadTimings(info, reloadKey, reloadDurationScale, callback) if type(callback) ~= 'function' then return nil end if not info or type(reloadKey) ~= 'string' or type(reloadDurationScale) ~= 'number' or reloadDurationScale >= 0.999 then return callback() end local timingFields = nil if info.ReloadType == 'Regular' then timingFields = { reloadKey .. 'Length', reloadKey .. 'ActionTimestamp', } elseif info.ReloadType == 'Segmented' then timingFields = { reloadKey .. 'StartLength', reloadKey .. 'StartActionTimestamp', reloadKey .. 'SegmentLength', reloadKey .. 'SegmentActionTimestamp', reloadKey .. 'FinishLength', reloadKey .. 'FinishActionTimestamp', } else return callback() end local originalTimings = {} for _, fieldName in ipairs(timingFields) do local fieldValue = info[fieldName] if type(fieldValue) == 'number' then originalTimings[fieldName] = fieldValue info[fieldName] = math.max(0.05, fieldValue * reloadDurationScale) end end local results = table.pack(pcall(callback)) for fieldName, fieldValue in pairs(originalTimings) do info[fieldName] = fieldValue end if not results[1] then ReportRivalsRuntimeIssue('mods_reload_timings', results[2]) return nil end return table.unpack(results, 2, results.n) end local function StopRivalsEquipAnimations(item) local viewModel = item and item.ViewModel if not viewModel then return end if type(viewModel.StopAnimation) == 'function' then pcall(function() viewModel:StopAnimation('Equip') end) pcall(function() viewModel:StopAnimation('EquipEmpty') end) return end local animator = viewModel.Animator if animator and type(animator.StopAnimation) == 'function' then pcall(function() animator:StopAnimation('Equip') end) pcall(function() animator:StopAnimation('EquipEmpty') end) end end -- The class the live guns use is the table in their metatable chain that owns the gun methods (ClientItem has _Recoil but no -- StartShooting, Melee has no _Recoil). On most executors require() returns that same table; where it does not, hooks on the required -- module never run, so the live class wins. function RivalsModsState.ResolveLiveGunClass() local fighter = ResolveLocalFighter() local items = fighter and type(fighter.Items) == 'table' and fighter.Items or nil if not items then return nil end for _, item in pairs(items) do local class = type(item) == 'table' and getmetatable(item) or nil local depth = 0 while type(class) == 'table' and depth < 8 do if type(rawget(class, '_Recoil')) == 'function' and type(rawget(class, 'StartShooting')) == 'function' and type(rawget(class, 'StartReloading')) == 'function' then return class end class = getmetatable(class) depth = depth + 1 end end return nil end function RivalsModsState.RestoreGunHooks() local state = RivalsModsState local gunModule = state.GunModule if gunModule then if state.OriginalGunStartShooting then gunModule.StartShooting = state.OriginalGunStartShooting end if state.OriginalGunStartReloading then gunModule.StartReloading = state.OriginalGunStartReloading end if state.OriginalGunRecoil then gunModule._Recoil = state.OriginalGunRecoil end if state.OriginalGunGetAimSpeed then gunModule.GetAimSpeed = state.OriginalGunGetAimSpeed end if state.OriginalGunEquip then gunModule.Equip = state.OriginalGunEquip end end state.OriginalGunStartShooting = nil state.OriginalGunStartReloading = nil state.OriginalGunRecoil = nil state.OriginalGunGetAimSpeed = nil state.OriginalGunEquip = nil end function RivalsModsState.EnsureGunHooks() local state = RivalsModsState local liveGun = RivalsModsState.ResolveLiveGunClass() if liveGun and not rawequal(liveGun, state.GunModule) then RivalsModsState.RestoreGunHooks() state.GunModule = liveGun end local gunModule = RivalsModsState.ResolveGunModule() if not gunModule then return false end if type(gunModule.StartShooting) == 'function' and not state.OriginalGunStartShooting then local originalStartShooting = gunModule.StartShooting state.OriginalGunStartShooting = originalStartShooting gunModule.StartShooting = function(self, ...) local item = ResolveRivalsModsStartShootingItem(self) if not item then return nil end local info = item.Info local originalShootCooldown = nil local originalBurstCooldown = nil local originalQuickShotCooldown = nil if IsRivalsModToggleEnabled('P4S1T1') and info then local weaponSpeedScale = ResolveRivalsWeaponSpeedScale() if type(info.ShootCooldown) == 'number' then originalShootCooldown = info.ShootCooldown info.ShootCooldown = math.max(0.001, originalShootCooldown * weaponSpeedScale) end if type(info.ShootBurstCooldown) == 'number' then originalBurstCooldown = info.ShootBurstCooldown info.ShootBurstCooldown = math.max(0.001, originalBurstCooldown * weaponSpeedScale) end if type(info.QuickShotCooldown) == 'number' then originalQuickShotCooldown = info.QuickShotCooldown info.QuickShotCooldown = math.max(0.001, originalQuickShotCooldown * weaponSpeedScale) end end local didPrimeSilent, silentCamera, silentOriginalCFrame = false, nil, nil if type(state.BeginAimbotSilentShot) == 'function' then didPrimeSilent, silentCamera, silentOriginalCFrame = state.BeginAimbotSilentShot(item) end local results = CallGameAtIdentity2(originalStartShooting, item, ...) if type(state.FinishAimbotSilentShot) == 'function' then state.FinishAimbotSilentShot(didPrimeSilent, silentCamera, silentOriginalCFrame) end if originalShootCooldown ~= nil then info.ShootCooldown = originalShootCooldown end if originalBurstCooldown ~= nil then info.ShootBurstCooldown = originalBurstCooldown end if originalQuickShotCooldown ~= nil then info.QuickShotCooldown = originalQuickShotCooldown end if not results[1] then ReportRivalsRuntimeIssue('mods_start_shooting', results[2]) return nil end return table.unpack(results, 2, results.n) end end if type(gunModule.StartReloading) == 'function' and not state.OriginalGunStartReloading then local originalStartReloading = gunModule.StartReloading state.OriginalGunStartReloading = originalStartReloading gunModule.StartReloading = function(self, forceReload, reloadEnum, animationEnum, segmentedCount) if not IsRivalsModToggleEnabled('P4S1T2') then return CallGameOrError(originalStartReloading, self, forceReload, reloadEnum, animationEnum, segmentedCount) end local reloadSpeedBoost = math.clamp(ReadRivalsModNumber('P4S1S3', 10), 0, 20) if reloadSpeedBoost <= 0 then return CallGameOrError(originalStartReloading, self, forceReload, reloadEnum, animationEnum, segmentedCount) end local reloadDurationScale = 1 - (reloadSpeedBoost / 100) local resolvedReloadKey = ResolveRivalsReloadKey(self, reloadEnum) return WithScaledRivalsReloadTimings( self and self.Info or nil, resolvedReloadKey, reloadDurationScale, function() return CallGameOrError( originalStartReloading, self, forceReload, reloadEnum, animationEnum, segmentedCount ) end ) end end if type(gunModule._Recoil) == 'function' and not state.OriginalGunRecoil then local originalRecoil = gunModule._Recoil state.OriginalGunRecoil = originalRecoil gunModule._Recoil = function(self, multiplier) if IsRivalsModToggleEnabled('P4S1T4') and type(multiplier) == 'number' then local reduction = math.clamp(ReadRivalsModNumber('P4S1S2', 100), 0, 100) local reducedMultiplier = multiplier * (1 - (reduction / 100)) if reducedMultiplier <= 0.001 then return end return CallGameOrError(originalRecoil, self, reducedMultiplier) end return CallGameOrError(originalRecoil, self, multiplier) end end if type(gunModule.GetAimSpeed) == 'function' and not state.OriginalGunGetAimSpeed then local originalGetAimSpeed = gunModule.GetAimSpeed state.OriginalGunGetAimSpeed = originalGetAimSpeed gunModule.GetAimSpeed = function(self) local originalAimSpeed = CallGameOrError(originalGetAimSpeed, self) if IsRivalsModToggleEnabled('P4S2T2') and type(originalAimSpeed) == 'number' then local adsSpeedBoost = math.clamp(ReadRivalsModNumber('P4S2S2', 85), 0, 85) return math.max(1, originalAimSpeed * (1 + (adsSpeedBoost / 100))) end return originalAimSpeed end end if type(gunModule.Equip) == 'function' and not state.OriginalGunEquip then local originalEquip = gunModule.Equip state.OriginalGunEquip = originalEquip gunModule.Equip = function(self, ...) local results = table.pack(CallGameOrError(originalEquip, self, ...)) if IsRivalsModToggleEnabled('P4S2T3') then local equipSpeedBoost = math.clamp(ReadRivalsModNumber('P4S2S3', 100), 0, 80) local equipSpeedScale = 1 / (1 + (equipSpeedBoost / 100)) local now = tick() local remainingEquipCooldown = type(self._equip_cooldown) == 'number' and math.max(self._equip_cooldown - now, 0) or 0 self._equip_cooldown = now + (remainingEquipCooldown * equipSpeedScale) local equipHash = self._equip_hash local scaledEquipDuration = remainingEquipCooldown * equipSpeedScale task.delay(scaledEquipDuration, function() if self and self._equip_hash == equipHash then StopRivalsEquipAnimations(self) end end) end return table.unpack(results, 1, results.n) end end return true end function RivalsModsState.EnsureMeleeHooks() local meleeModule = RivalsModsState.ResolveMeleeModule() if not meleeModule then return false end local state = RivalsModsState if type(meleeModule.StartShooting) == 'function' and not state.OriginalMeleeStartShooting then local originalStartShooting = meleeModule.StartShooting state.OriginalMeleeStartShooting = originalStartShooting meleeModule.StartShooting = function(self, ...) local item = ResolveRivalsModsStartShootingItem(self) if not item then return nil end local info = item.Info local originalAttackCooldown = nil if IsRivalsModToggleEnabled('P4S1T1') and info then local weaponSpeedScale = ResolveRivalsWeaponSpeedScale() if type(info.AttackCooldown) == 'number' then originalAttackCooldown = info.AttackCooldown info.AttackCooldown = math.max(0.001, originalAttackCooldown * weaponSpeedScale) end end local results = CallGameAtIdentity2(originalStartShooting, item, ...) if originalAttackCooldown ~= nil then info.AttackCooldown = originalAttackCooldown end if not results[1] then ReportRivalsRuntimeIssue('mods_melee_start_shooting', results[2]) return nil end return table.unpack(results, 2, results.n) end end return true end function RivalsModsState.EnsureGunbladeHooks() local gunbladeModule = RivalsModsState.ResolveGunbladeModule() if not gunbladeModule then return false end local state = RivalsModsState if type(gunbladeModule.StartShooting) == 'function' and not state.OriginalGunbladeStartShooting then local originalStartShooting = gunbladeModule.StartShooting state.OriginalGunbladeStartShooting = originalStartShooting gunbladeModule.StartShooting = function(self, ...) local item = ResolveRivalsModsStartShootingItem(self) if not item then return nil end local info = item.Info local originalBladeCooldown = nil if IsRivalsModToggleEnabled('P4S1T1') and info and type(item and item.Get) == 'function' and item:Get('Mode') == 'Blade' then local weaponSpeedScale = ResolveRivalsWeaponSpeedScale() if type(info.BladeCooldown) == 'number' then originalBladeCooldown = info.BladeCooldown info.BladeCooldown = math.max(0.001, originalBladeCooldown * weaponSpeedScale) end end local results = CallGameAtIdentity2(originalStartShooting, item, ...) if originalBladeCooldown ~= nil then info.BladeCooldown = originalBladeCooldown end if not results[1] then ReportRivalsRuntimeIssue('mods_gunblade_start_shooting', results[2]) return nil end return table.unpack(results, 2, results.n) end end return true end function RivalsModsState.EnsureHooks() local state = RivalsModsState -- every step runs on its own: a feature that cannot start on this executor must not stop the gun hooks below local function Step(label, fn) local ok, err = pcall(fn) if not ok then ReportRivalsRuntimeIssue('mods_ensure_' .. label, err) end return ok end Step('persistent_items', RivalsModsState.RefreshPersistentItemModifiers) Step('camera', RivalsModsState.UpdateCameraModifiers) if state.NoSpreadController then Step('no_spread_sync', RivalsModsState.SyncNoSpreadEnabled) end if state.GrenadeFuseController then Step('grenade_fuse_sync', RivalsModsState.SyncGrenadeFuse) end if state.KnifeExtenderController then Step('knife_sync', RivalsModsState.SyncKnifeExtender) end Step('extras_sync', RivalsRuntimeBridge.Extras.SyncControllers) if not ( IsRivalsModToggleEnabled('P4S1T1') or IsRivalsModToggleEnabled('P4S1T2') or IsRivalsModToggleEnabled('P4S1T3') or IsRivalsModToggleEnabled('P4S1T4') or IsRivalsModToggleEnabled('P4S1T5') or IsRivalsModToggleEnabled('P4S1T6') or IsRivalsModToggleEnabled('P4S1T7') or IsRivalsModToggleEnabled('P4S2T2') or IsRivalsModToggleEnabled('P4S2T3') or IsRivalsModToggleEnabled('P4S2T4') or IsRivalsModToggleEnabled('P4S2T5') or IsRivalsModToggleEnabled('P4S2T6') or IsRivalsModToggleEnabled('P4S2T8') or IsRivalsModToggleEnabled('P4S2T9') or IsRivalsModToggleEnabled('P4S2T10') or IsRivalsModToggleEnabled('KE_Enabled') or RivalsRuntimeBridge.Extras.NeedsInputHook() ) then state.PendingEnsureHooks = false return false end if not IsReplicatedStateReady() then state.PendingEnsureHooks = true return false end state.PendingEnsureHooks = false if IsRivalsModToggleEnabled('P4S1T3') or IsRivalsModToggleEnabled('P4S1T6') or IsRivalsModToggleEnabled('P4S1T7') then __ZkhHub_genv.ZkxHubCaps.gate('No Spread / Grenade Fuse', 'debug.getupvalues', 'debug.setupvalue') end local inputOk, inputReady = pcall(RivalsModsState.EnsureZkhHubInputHook) if not inputOk then ReportRivalsRuntimeIssue('mods_ensure_input_hook', inputReady) elseif inputReady then Step('no_spread', RivalsModsState.SyncNoSpreadEnabled) Step('grenade_fuse', RivalsModsState.SyncGrenadeFuse) Step('knife', RivalsModsState.SyncKnifeExtender) Step('extras', RivalsRuntimeBridge.Extras.SyncControllers) end Step('gun_hooks', RivalsModsState.EnsureGunHooks) Step('melee_hooks', RivalsModsState.EnsureMeleeHooks) Step('gunblade_hooks', RivalsModsState.EnsureGunbladeHooks) return true end function RivalsModsState.RestoreHooks() local state = RivalsModsState RivalsModsState.RestoreClientModifiers() RivalsModsState.RestoreZkhHubInputHook() local gunModule = state.GunModule if gunModule then if state.OriginalGunStartShooting then gunModule.StartShooting = state.OriginalGunStartShooting end if state.OriginalGunStartReloading then gunModule.StartReloading = state.OriginalGunStartReloading end if state.OriginalGunRecoil then gunModule._Recoil = state.OriginalGunRecoil end if state.OriginalGunGetAimSpeed then gunModule.GetAimSpeed = state.OriginalGunGetAimSpeed end if state.OriginalGunEquip then gunModule.Equip = state.OriginalGunEquip end end local meleeModule = state.MeleeModule if meleeModule then if state.OriginalMeleeStartShooting then meleeModule.StartShooting = state.OriginalMeleeStartShooting end end local gunbladeModule = state.GunbladeModule if gunbladeModule then if state.OriginalGunbladeStartShooting then gunbladeModule.StartShooting = state.OriginalGunbladeStartShooting end end state.GunModule = nil state.MeleeModule = nil state.GunbladeModule = nil state.OriginalGunStartShooting = nil state.OriginalMeleeStartShooting = nil state.OriginalGunbladeStartShooting = nil state.OriginalGunStartReloading = nil state.OriginalGunRecoil = nil state.OriginalGunGetAimSpeed = nil state.OriginalGunEquip = nil end end -- Aimbot local RefreshAimbotEnabledToggleKeypickerState = nil -- ====================================================================== -- Aimbot bridge: pointer, targeting helpers -- ====================================================================== local AimbotBridge = {} do do local function IsTouchAimbotDevice() return UserInputService.TouchEnabled and not UserInputService.MouseEnabled end local function GetAimbotViewportCenter() local camera = Workspace.CurrentCamera if camera then return Vector2.new(camera.ViewportSize.X * 0.5, camera.ViewportSize.Y * 0.5) end return Vector2.zero end AimbotBridge.GetAimbotPointerPosition = function() if IsTouchAimbotDevice() or IsAimbotGameReady() then return GetAimbotViewportCenter() end return UserInputService:GetMouseLocation() end AimbotBridge.ResolveAimbotEquippedItem = function() local fighter = ResolveLocalFighter() return fighter and fighter.EquippedItem or nil end local AIMBOT_HARD_BLOCK_ITEMS = { ['Warper'] = true, ['Medkit'] = true, ['Subspace Tripmine'] = true, ['Warpstone'] = true, ['Flashbang'] = true, ['Grenade'] = true, ['Jump Pad'] = true, ['Molotov'] = true, ['Satchel'] = true, ['Smoke Grenade'] = true, ['War Horn'] = true, ['Grappler'] = true, ['Flamethrower'] = true, } local AIMBOT_HOLD_FIRE_ITEMS = { ['Flamethrower'] = true, } AimbotBridge.CanUseAimbotWithItem = function(item) if not item then return false end if AIMBOT_HARD_BLOCK_ITEMS[item.Name] == true then return false end local selection = Options.P2S1D4 and Options.P2S1D4.Value if type(selection) == 'table' then if selection[item.Name] == true then return false end for _, selectedName in ipairs(selection) do if selectedName == item.Name then return false end end end local info = item.Info if info and type(item.StartShooting) == 'function' then local hasTimedShotCooldown = type(info.ShootCooldown) == 'number' or type(info.ShootBurstCooldown) == 'number' or type(info.ShootBurst) == 'number' or type(info.QuickShotCooldown) == 'number' or type(info.ChargeReleaseCooldown) == 'number' or type(info.BladeCooldown) == 'number' local hasMeleeAttackCooldown = type(info.AttackCooldown) == 'number' and ((info.Class == 'Melee') or (info.Type == 'Melee')) if hasTimedShotCooldown or hasMeleeAttackCooldown then return true end end local name = item.Name if name == 'Daggers' or name == 'Bow' or name == 'Slingshot' then return true end if AIMBOT_HOLD_FIRE_ITEMS[name] == true then return true end return false end AimbotBridge.CanUseTriggerbotWithItem = function(item) return AimbotBridge.CanUseAimbotWithItem(item) end AimbotBridge.SyncAimbotToggleKeypickerToUi = function(toggle, keypicker) if not toggle or not keypicker then return function() end end local applyingToggleState = false local applyingKeypickerState = false local lastMode = keypicker.Mode local function syncKeypickerFromToggle(value) if keypicker.Mode ~= 'Toggle' then lastMode = keypicker.Mode return end local nextState = value == true if keypicker.Toggled == nextState then lastMode = keypicker.Mode return end applyingToggleState = true keypicker.Toggled = nextState if type(keypicker.Update) == 'function' then keypicker:Update() end applyingToggleState = false lastMode = keypicker.Mode end if type(toggle.OnChanged) == 'function' then toggle:OnChanged(function(value) if applyingKeypickerState then return end syncKeypickerFromToggle(value) end) end if type(keypicker.OnClick) == 'function' then keypicker:OnClick(function(state) if applyingToggleState or keypicker.Mode ~= 'Toggle' then lastMode = keypicker.Mode return end local nextValue = state == true if toggle.Value == nextValue then lastMode = keypicker.Mode return end applyingKeypickerState = true toggle:SetValue(nextValue) applyingKeypickerState = false lastMode = keypicker.Mode end) end syncKeypickerFromToggle(toggle.Value) return function() if keypicker.Mode == 'Toggle' and lastMode ~= 'Toggle' then syncKeypickerFromToggle(toggle.Value) else lastMode = keypicker.Mode end end end AimbotBridge.IsRagebotForcingCombat = function() local bot = RivalsRuntimeBridge.CombatBot return bot ~= nil and bot.IsActive() end AimbotBridge.ResolveAimbotMode = function() local mode = Options.P2S1D1 and Options.P2S1D1.Value or 'Silent' if mode ~= 'Camera' then return 'Silent' end return mode end AimbotBridge.IsAimbotSelected = function() return AimbotBridge.IsRagebotForcingCombat() or (AimbotBridge.ResolveAimbotMode() == 'Silent' and Toggles.P2S1T1 and Toggles.P2S1T1.Value == true) end AimbotBridge.IsExplicitAimbotEnabled = function() if RefreshAimbotEnabledToggleKeypickerState then RefreshAimbotEnabledToggleKeypickerState() end if not AimbotBridge.CanUseAimbotWithItem(AimbotBridge.ResolveAimbotEquippedItem()) then return false end if not (Toggles.P2S1T1 and Toggles.P2S1T1.Value) then return false end if Options.P2S1T1K and type(Options.P2S1T1K.GetState) == 'function' then return Options.P2S1T1K:GetState() end return true end AimbotBridge.IsAimbotEnabled = function() if AimbotBridge.IsRagebotForcingCombat() then return AimbotBridge.CanUseAimbotWithItem(AimbotBridge.ResolveAimbotEquippedItem()) end return AimbotBridge.ResolveAimbotMode() == 'Silent' and AimbotBridge.IsExplicitAimbotEnabled() end AimbotBridge.IsCameraAimEnabled = function() if AimbotBridge.ResolveAimbotMode() ~= 'Camera' or not AimbotBridge.IsExplicitAimbotEnabled() then return false end if AimbotInputState.SyntheticInputBlocked() then return false end if not IsTouchAimbotDevice() and not AimbotInputState.RightMouse and not (Toggles.PX19T1 and Toggles.PX19T1.Value == false) then return false end return true end AimbotBridge.IsTriggerbotEnabled = function() if not AimbotBridge.CanUseTriggerbotWithItem(AimbotBridge.ResolveAimbotEquippedItem()) then return false end if AimbotBridge.IsRagebotForcingCombat() then return true end if not (Toggles.P2S1T7 and Toggles.P2S1T7.Value) then return false end if Options.P2S1T7K and type(Options.P2S1T7K.GetState) == 'function' then return Options.P2S1T7K:GetState() end return true end AimbotBridge.IsAimbotIgnoreFovEnabled = function() return Toggles.P2S1T3 and Toggles.P2S1T3.Value == true end AimbotBridge.UpdateAimbotFovCircle = function(active, gameReady, usesFov) local showFov = Toggles.P2S1T5 and Toggles.P2S1T5.Value if not active or not gameReady or not usesFov or not showFov then AimbotFovCircle.Visible = false return end AimbotFovCircle.Position = AimbotBridge.GetAimbotPointerPosition() AimbotFovCircle.Radius = Options.P2S1S2 and Options.P2S1S2.Value or 170 AimbotFovCircle.Visible = true end AimbotBridge.IsAimbotShootInputHeld = function() if not AimbotBridge.CanUseAimbotWithItem(AimbotBridge.ResolveAimbotEquippedItem()) then return false end local touchActive = AimbotInputState.TouchCount > 0 return AimbotInputState.LeftMouse or AimbotInputState.RightMouse or touchActive end end do AimbotBridge.ReadAimbotShootRemaining = function(item) local shootCooldown = item and rawget(item, '_shoot_cooldown') if type(shootCooldown) == 'number' then return math.max(shootCooldown - tick(), 0) end return 0 end AimbotBridge.ReadAimbotReloadRemaining = function(item) local reloadCooldown = item and rawget(item, '_reload_cooldown') if type(reloadCooldown) == 'number' then return math.max(reloadCooldown - tick(), 0) end return 0 end AimbotBridge.ReadAimbotLastShot = function(item) local lastShot = item and rawget(item, '_last_shot') if type(lastShot) == 'number' then return lastShot end return nil end AimbotBridge.ReadAimbotIsCharging = function(item) return item and rawget(item, '_is_charging') == true end local function IsAimbotKatanaDeflectCheckEnabled() return Toggles.P2S1T8 and Toggles.P2S1T8.Value == true end local AIMBOT_KATANA_DEFLECT_BYPASS_ITEMS = { ['Slingshot'] = true, ['Bow'] = true, ['Exogun'] = true, ['Spray'] = true, ['Flamethrower'] = true, ['Grenade Launcher'] = true, ['RPG'] = true, ['Crossbow'] = true, ['Daggers'] = true, } local function IsAimbotKatanaDeflectBypassItem(item) if not item then return false end return AIMBOT_KATANA_DEFLECT_BYPASS_ITEMS[item.Name] == true end RivalsRuntimeBridge.IsRivalsKatanaDeflectActiveItem = function(item) if not item or item.Name ~= 'Katana' then return false end local now = tick() local attackCooldown = rawget(item, '_attack_cooldown') local deflectCooldown = rawget(item, '_deflect_cooldown') local deflectHash = rawget(item, '_deflect_hash') return type(attackCooldown) == 'number' and attackCooldown > now and type(deflectCooldown) == 'number' and deflectCooldown > now and type(deflectHash) == 'number' and deflectHash % 2 == 1 end AimbotBridge.ShouldIgnoreAimbotKatanaDeflectTarget = function(player, localItem) if not player or not IsAimbotKatanaDeflectCheckEnabled() or IsAimbotKatanaDeflectBypassItem(localItem) then return false end local fighter = TrackedFightersByPlayer[player] return RivalsRuntimeBridge.IsRivalsKatanaDeflectActiveItem(fighter and fighter.EquippedItem or nil) end AimbotBridge.IsAimbotSilentShotHookItem = function(item) if not AimbotBridge.CanUseTriggerbotWithItem(item) then return false end local info = item and item.Info or nil local hasTimedShotCooldown = type(info and info.ShootCooldown) == 'number' or type(info and info.ShootBurstCooldown) == 'number' or type(info and info.ShootBurst) == 'number' or type(info and info.QuickShotCooldown) == 'number' or type(info and info.ChargeReleaseCooldown) == 'number' or type(info and info.BladeCooldown) == 'number' local hasMeleeAttackCooldown = type(info and info.AttackCooldown) == 'number' and ((info.Class == 'Melee') or (info.Type == 'Melee')) return item ~= nil and type(item.StartShooting) == 'function' and (hasTimedShotCooldown or hasMeleeAttackCooldown) end AimbotBridge.ShouldUseAimbotSilentContinuousAim = function(item) if item and item.Name == 'Flamethrower' then return rawget(item, '_is_using') == true end if not AimbotBridge.IsAimbotSilentShotHookItem(item) then return true end return AimbotBridge.ReadAimbotIsCharging(item) end end end -- ====================================================================== -- Silent aim and triggerbot runtime -- ====================================================================== do local function ResolveAimbotSilentDebugEnabled() return Toggles.P9S2T1 and Toggles.P9S2T1.Value == true end local function ReadAimbotSilentToggle(toggleId, fallback) local toggle = Toggles[toggleId] if toggle ~= nil then return toggle.Value == true end return fallback == true end local function ReleaseTriggerbotMouseHold() if not AimbotSilentConnections.TriggerbotHoldingMouse then return end local holdX = AimbotSilentConnections.TriggerbotHoldX or 0 local holdY = AimbotSilentConnections.TriggerbotHoldY or 0 local virtualInputManager = Instance.new('VirtualInputManager') virtualInputManager:SendMouseButtonEvent(holdX, holdY, 0, false, game, 1) AimbotSilentConnections.TriggerbotHoldingMouse = false AimbotSilentConnections.TriggerbotHoldX = nil AimbotSilentConnections.TriggerbotHoldY = nil end AimbotBridge.ResetTriggerbotReactionState = function() AimbotSilentConnections.TriggerbotTarget = nil AimbotSilentConnections.TriggerbotTargetAcquiredAt = 0 end AimbotBridge.ResetCameraAimRandomState = function() AimbotSilentConnections.CameraAimRandomTarget = nil AimbotSilentConnections.CameraAimRandomPart = nil end local function DisconnectAimbotSilentConnections() ReleaseTriggerbotMouseHold() for _, connectionName in ipairs({ 'Shot', 'ProjectileShot' }) do local connection = AimbotSilentConnections[connectionName] if connection then pcall(function() connection:Disconnect() end) end end AimbotSilentConnections.Shot = nil AimbotSilentConnections.ProjectileShot = nil local hookedItem = AimbotSilentConnections.HookedItem local originalStartShooting = AimbotSilentConnections.OriginalStartShooting if hookedItem and originalStartShooting and hookedItem.StartShooting == AimbotSilentConnections.WrappedStartShooting then hookedItem.StartShooting = RivalsModsState.ResolveGunStartShootingBase(originalStartShooting) end AimbotSilentConnections.HookedItem = nil AimbotSilentConnections.OriginalStartShooting = nil AimbotSilentConnections.WrappedStartShooting = nil end local function ResetAimbotRuntimeState() DisconnectAimbotSilentConnections() AimbotSilentState.Item = nil AimbotSilentState.HookTarget = nil AimbotSilentState.HookTargetCapturedAt = 0 AimbotSilentState.ShotPrimeDepth = 0 end local function EnsureAimbotSilentConnections(item) local state = AimbotSilentState if state.Item ~= item then DisconnectAimbotSilentConnections() state.Item = item state.LastShot = AimbotBridge.ReadAimbotLastShot(item) state.LastShotChangedAt = 0 state.ShotSignalAt = 0 state.ProjectileShotAt = 0 state.HookTarget = nil state.HookTargetCapturedAt = 0 end if not item then return end if not AimbotSilentConnections.Shot and type(item.Shot) == 'table' and type(item.Shot.Connect) == 'function' then AimbotSilentConnections.Shot = item.Shot:Connect(GuardRivalsCallback('Aimbot_Shot', function() AimbotSilentState.ShotSignalAt = tick() end)) end if not AimbotSilentConnections.ProjectileShot and type(item.ProjectileShot) == 'table' and type(item.ProjectileShot.Connect) == 'function' then AimbotSilentConnections.ProjectileShot = item.ProjectileShot:Connect( GuardRivalsCallback('Aimbot_ProjectileShot', function() AimbotSilentState.ProjectileShotAt = tick() end) ) end end local function ResolveAimbotSilentHoldWindow(item) local holdWindow = 0 local info = item and item.Info or nil if ReadAimbotSilentToggle('P9S2T8', true) and type(info and info.ShootCooldown) == 'number' then holdWindow = math.max(holdWindow, info.ShootCooldown) end if ReadAimbotSilentToggle('P9S2T10', true) and type(info and info.ShootBurstCooldown) == 'number' then holdWindow = math.max(holdWindow, info.ShootBurstCooldown) end if ReadAimbotSilentToggle('P9S2T11', true) and type(info and info.QuickShotCooldown) == 'number' then holdWindow = math.max(holdWindow, info.QuickShotCooldown) end if ReadAimbotSilentToggle('P9S2T18', true) and type(info and info.ChargeReleaseCooldown) == 'number' then holdWindow = math.max(holdWindow, info.ChargeReleaseCooldown) end return holdWindow end local function ResolveAimbotSilentHookTargetCacheWindow(item) local info = item and item.Info or nil local cacheWindow = math.huge local function consider(window) if type(window) == 'number' and window > 0 then cacheWindow = math.min(cacheWindow, window) end end consider(info and info.ShootBurstCooldown) consider(info and info.QuickShotCooldown) consider(info and info.ShootCooldown) consider(info and info.ChargeReleaseCooldown) if cacheWindow == math.huge then return 1 / 30 end return math.clamp(cacheWindow, 1 / 60, 0.08) end AimbotBridge.ResolveAimbotSilentDebugEnabled = ResolveAimbotSilentDebugEnabled AimbotBridge.ReadAimbotSilentToggle = ReadAimbotSilentToggle AimbotBridge.DisconnectAimbotSilentConnections = DisconnectAimbotSilentConnections AimbotBridge.ResetAimbotRuntimeState = ResetAimbotRuntimeState AimbotBridge.EnsureAimbotSilentConnections = EnsureAimbotSilentConnections AimbotBridge.ResolveAimbotSilentHoldWindow = ResolveAimbotSilentHoldWindow AimbotBridge.ResolveAimbotSilentHookTargetCacheWindow = ResolveAimbotSilentHookTargetCacheWindow AimbotBridge.ReleaseTriggerbotMouseHold = ReleaseTriggerbotMouseHold end -- ====================================================================== -- Flickbot -- ====================================================================== RivalsRuntimeBridge.Flickbot = { State = 'Idle', Trajectory = nil, TrajectoryIndex = 1, ElapsedMs = 0, TotalMs = 0, BakedEndDirection = Vector3.zAxis, Selected = nil, Timer = 0, WasHeld = false, Rng = Random.new(), OuTheta = 3.5, TremorFrequencyMin = 8, TremorFrequencyMax = 12, SampleDeltaMean = 7.8, GammaShape = 3.5, } function RivalsRuntimeBridge.ResetFlickbot() local flickbot = RivalsRuntimeBridge.Flickbot flickbot.State = 'Idle' flickbot.Trajectory = nil flickbot.TrajectoryIndex = 1 flickbot.ElapsedMs = 0 flickbot.TotalMs = 0 flickbot.BakedEndDirection = Vector3.zAxis flickbot.Selected = nil flickbot.Timer = 0 flickbot.WasHeld = false end -- ====================================================================== -- World visuals: lighting, sky, weather, lightning, post effects -- ====================================================================== RivalsRuntimeBridge.WorldVisuals = { Initialized = false, CustomSkyboxesLoaded = false, Lighting = game:GetService('Lighting'), SoundService = game:GetService('SoundService'), Connections = {}, LightingGuard = nil, OwnedEffects = {}, NativeEffectGuards = {}, OwnedAmbience = nil, ActiveAmbienceSource = nil, OwnedWeatherPart = nil, WeatherEmitters = {}, ActiveWeatherPreset = nil, WeatherGeneration = 0, NextLightningAt = 0, StretchedResolutionBound = false, StretchedLastCamera = nil, StretchedLastBase = nil, StretchedLastResult = nil, MotionBlurEffect = nil, MotionBlurLastCFrame = nil, MotionBlurSize = 0, OwnedSky = nil, ActiveSkyName = nil, NativeSkyGuards = setmetatable({}, { __mode = 'k' }), OwnedAtmosphere = nil, NativeAtmosphereGuards = setmetatable({}, { __mode = 'k' }), CustomSkyboxes = {}, CustomSkyPath = 'ZkxHub/RIVALS Skyboxes.json', SkyProperties = { 'SkyboxBk', 'SkyboxDn', 'SkyboxFt', 'SkyboxLf', 'SkyboxRt', 'SkyboxUp', 'StarCount', 'CelestialBodiesShown', 'SunAngularSize', 'MoonAngularSize', 'SunTextureId', 'MoonTextureId', }, AtmosphereProperties = { 'Color', 'Decay', 'Density', 'Offset', 'Glare', 'Haze' }, WeatherPresets = { Snow = { { Texture = 'rbxassetid://119455261341623', BaseRate = 18, SpeedMin = 8, SpeedMax = 12, SizeStart = 0.025, SizeEnd = 0.05, TransparencyMax = 0.4, LifetimeMin = 4, LifetimeMax = 6, RotationMin = 0, RotationMax = 360, RotationSpeedMin = -25, RotationSpeedMax = 25, BaseSpread = 0.4, BaseAccelerationY = -3, }, { Texture = 'rbxassetid://135953828575052', BaseRate = 12, SpeedMin = 12, SpeedMax = 18, SizeStart = 0.03, SizeEnd = 0.08, TransparencyMax = 0.5, LifetimeMin = 3, LifetimeMax = 5, RotationMin = 0, RotationMax = 360, RotationSpeedMin = -35, RotationSpeedMax = 35, BaseSpread = 0.45, BaseAccelerationY = -4, }, { Texture = 'rbxassetid://119888390708774', BaseRate = 48, SpeedMin = 10, SpeedMax = 16, SizeStart = 0.06, SizeEnd = 0.13, TransparencyMax = 0.15, LifetimeMin = 4, LifetimeMax = 6, RotationMin = 0, RotationMax = 360, RotationSpeedMin = -90, RotationSpeedMax = 90, BaseSpread = 0.5, BaseAccelerationY = -2, }, }, Rain = { { Texture = 'rbxassetid://1822883048', BaseRate = 180, SpeedMin = 80, SpeedMax = 105, SizeStart = 0.18, SizeEnd = 0.35, TransparencyMax = 0.1, LifetimeMin = 1.4, LifetimeMax = 1.8, RotationMin = -5, RotationMax = 5, RotationSpeedMin = 0, RotationSpeedMax = 0, BaseSpread = 0.4, BaseAccelerationY = -55, Orientation = Enum.ParticleOrientation.FacingCamera, }, { Texture = 'rbxassetid://113640658844067', BaseRate = 75, SpeedMin = 100, SpeedMax = 135, SizeStart = 0.22, SizeEnd = 0.42, TransparencyMax = 0.25, LifetimeMin = 1.1, LifetimeMax = 1.6, RotationMin = -4, RotationMax = 4, RotationSpeedMin = 0, RotationSpeedMax = 0, BaseSpread = 0.3, BaseAccelerationY = -70, Orientation = Enum.ParticleOrientation.FacingCamera, }, }, Blizzard = { { Texture = 'rbxassetid://119455261341623', BaseRate = 25, SpeedMin = 25, SpeedMax = 40, SizeStart = 0.025, SizeEnd = 0.06, TransparencyMax = 0.35, LifetimeMin = 2.5, LifetimeMax = 4, RotationMin = 0, RotationMax = 360, RotationSpeedMin = -45, RotationSpeedMax = 45, BaseSpread = 0.55, BaseAccelerationY = -8, }, { Texture = 'rbxassetid://135953828575052', BaseRate = 20, SpeedMin = 28, SpeedMax = 45, SizeStart = 0.035, SizeEnd = 0.09, TransparencyMax = 0.45, LifetimeMin = 2.2, LifetimeMax = 3.8, RotationMin = 0, RotationMax = 360, RotationSpeedMin = -55, RotationSpeedMax = 55, BaseSpread = 0.6, BaseAccelerationY = -10, }, { Texture = 'rbxassetid://119888390708774', BaseRate = 90, SpeedMin = 22, SpeedMax = 38, SizeStart = 0.07, SizeEnd = 0.15, TransparencyMax = 0.1, LifetimeMin = 2.8, LifetimeMax = 4.5, RotationMin = 0, RotationMax = 360, RotationSpeedMin = -120, RotationSpeedMax = 120, BaseSpread = 0.7, BaseAccelerationY = -7, }, { Texture = 'rbxassetid://1822883048', BaseRate = 50, SpeedMin = 55, SpeedMax = 80, SizeStart = 0.14, SizeEnd = 0.28, TransparencyMax = 0.2, LifetimeMin = 1.8, LifetimeMax = 2.6, RotationMin = -12, RotationMax = 12, RotationSpeedMin = 0, RotationSpeedMax = 0, BaseSpread = 0.5, BaseAccelerationY = -35, }, }, }, LightingProperties = { 'Ambient', 'OutdoorAmbient', 'ClockTime', 'Brightness', 'ColorShift_Top', 'ColorShift_Bottom', 'EnvironmentDiffuseScale', 'EnvironmentSpecularScale', 'ExposureCompensation', 'TonemapperPreset', 'FogColor', 'FogStart', 'FogEnd', 'GeographicLatitude', 'GlobalShadows', 'ShadowSoftness', 'LightingStyle', 'ShadowColor', 'PrioritizeLightingQuality', }, SkyFaceInputs = { SkyboxBk = 'P1S12I2', SkyboxDn = 'P1S12I6', SkyboxFt = 'P1S12I1', SkyboxLf = 'P1S12I3', SkyboxRt = 'P1S12I4', SkyboxUp = 'P1S12I5', }, PostEffects = { BloomEffect = { Toggle = 'P1S6T1', Properties = { Intensity = { 'P1S6S1', 0.4 }, Size = { 'P1S6S2', 24 }, Threshold = { 'P1S6S3', 0.95 }, }, }, ColorCorrectionEffect = { Toggle = 'P1S7T1', Properties = { TintColor = { 'P1S7C1', Color3.new(1, 1, 1) }, Saturation = { 'P1S7S1', 0 }, Brightness = { 'P1S7S2', 0 }, Contrast = { 'P1S7S3', 0 }, }, }, SunRaysEffect = { Toggle = 'P1S10T1', Properties = { Intensity = { 'P1S10S1', 0.25 }, Spread = { 'P1S10S2', 1 }, }, }, DepthOfFieldEffect = { Toggle = 'PX1T1', Properties = { FarIntensity = { 'PX1S1', 0.75 }, FocusDistance = { 'PX1S2', 25 }, InFocusRadius = { 'PX1S3', 30 }, NearIntensity = { 'PX1S4', 0.5 }, }, }, }, SkyPresetNames = { 'GreenHaze', 'Space2', 'PinkMountains', 'Valentines', 'PinkArt', 'PurplePlanet', 'Blizzard', 'Winter', 'FlamingSunset', 'Mountains', 'FPSBoost', 'AestheticMountains', 'BetterNight3', 'Galaxy2', 'Aesthetic3', 'PurpleSpace', 'Pink', 'PurpleNight', 'Flame', 'Galaxy3', 'Storm', 'Milkyway', 'PurpleClouds', 'BlueClouds', 'BetterNight', 'Cloudy', 'NetherWorld', 'NewYork', 'Purple2', 'FunnyStorm', 'Galaxy', 'BlueSpace', 'Purple3', 'Nebula2', 'Nebula', 'LunarNight', 'Purple', 'Hell', 'Nebula6', 'DarkishPink', 'RedNight', 'Space', 'OverPlanet', 'Orange', 'Pastel', 'Beach', 'FakeClouds', 'Aesthetic2', 'Nebula4', 'BluePlanet', 'Nebula5', 'LunarNight2', 'Pinkie', 'PurpleMountains', 'DarkClouds', 'BetterNight2', 'DarkMountains', 'Nebula3', 'BetterSky', 'MagentaOrange', 'Aesthetic', 'Alien', 'Lunar', 'Retro', 'Realistic', 'Midnight', 'Antartica', 'Moon', 'NightMountains', 'Art', 'Ame', 'Dawn', }, SkyPresets = { GreenHaze = { SkyboxBk = 'rbxassetid://160193404', SkyboxDn = 'rbxassetid://160193466', SkyboxFt = 'rbxassetid://160193461', SkyboxLf = 'rbxassetid://160193469', SkyboxRt = 'rbxassetid://160193463', SkyboxUp = 'rbxassetid://160193458', SunAngularSize = 0, }, Space2 = { SkyboxBk = 'rbxassetid://11844076072', SkyboxDn = 'rbxassetid://11844069700', SkyboxFt = 'rbxassetid://11844067209', SkyboxLf = 'rbxassetid://11844063543', SkyboxRt = 'rbxassetid://11844058446', SkyboxUp = 'rbxassetid://11844053742', StarCount = 3000, SunAngularSize = 11, MoonAngularSize = 20, MoonTextureId = 'rbxassetid://11844121592', }, PinkMountains = { SkyboxBk = 'rbxassetid://160188495', SkyboxDn = 'rbxassetid://160188614', SkyboxFt = 'rbxassetid://160188609', SkyboxLf = 'rbxassetid://160188589', SkyboxRt = 'rbxassetid://160188597', SkyboxUp = 'rbxassetid://160188588', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 0, }, Valentines = { SkyboxBk = 'rbxassetid://11427769401', SkyboxDn = 'rbxassetid://11427770685', SkyboxFt = 'rbxassetid://11427769401', SkyboxLf = 'rbxassetid://11427769401', SkyboxRt = 'rbxassetid://11427769401', SkyboxUp = 'rbxassetid://11427771954', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 0, }, PinkArt = { SkyboxBk = 'rbxassetid://71607054149497', SkyboxDn = 'rbxassetid://78865378050055', SkyboxFt = 'rbxassetid://104560113223878', SkyboxLf = 'rbxassetid://80395333901607', SkyboxRt = 'rbxassetid://87570388049514', SkyboxUp = 'rbxassetid://79190209626172', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 0, }, PurplePlanet = { SkyboxBk = 'rbxassetid://16262356578', SkyboxDn = 'rbxassetid://16262358026', SkyboxFt = 'rbxassetid://16262360469', SkyboxLf = 'rbxassetid://16262362003', SkyboxRt = 'rbxassetid://16262363873', SkyboxUp = 'rbxassetid://16262366016', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Blizzard = { SkyboxBk = 'rbxassetid://16653221738', SkyboxDn = 'rbxassetid://16653222701', SkyboxFt = 'rbxassetid://16653224051', SkyboxLf = 'rbxassetid://16653225849', SkyboxRt = 'rbxassetid://16653227200', SkyboxUp = 'rbxassetid://16653228333', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 0, SunTextureId = 'rbxassetid://6196665106', MoonTextureId = 'rbxassetid://6444320592', }, Winter = { SkyboxBk = 'rbxassetid://155657655', SkyboxDn = 'rbxassetid://155674246', SkyboxFt = 'rbxassetid://155657609', SkyboxLf = 'rbxassetid://155657671', SkyboxRt = 'rbxassetid://155657619', SkyboxUp = 'rbxassetid://155674931', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 11, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, FlamingSunset = { SkyboxBk = 'rbxassetid://415688378', SkyboxDn = 'rbxassetid://415688193', SkyboxFt = 'rbxassetid://415688242', SkyboxLf = 'rbxassetid://415688310', SkyboxRt = 'rbxassetid://415688274', SkyboxUp = 'rbxassetid://415688354', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://6196665106', MoonTextureId = 'rbxassetid://6444320592', }, Mountains = { SkyboxBk = 'rbxassetid://15359410490', SkyboxDn = 'rbxassetid://15359411132', SkyboxFt = 'rbxassetid://15359412131', SkyboxLf = 'rbxassetid://15359411633', SkyboxRt = 'rbxassetid://15359417656', SkyboxUp = 'rbxassetid://15359412677', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, FPSBoost = { SkyboxBk = 'rbxassetid://11457548274', SkyboxDn = 'rbxassetid://11457548274', SkyboxFt = 'rbxassetid://11457548274', SkyboxLf = 'rbxassetid://11457548274', SkyboxRt = 'rbxassetid://11457548274', SkyboxUp = 'rbxassetid://11457548274', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, AestheticMountains = { SkyboxBk = 'rbxassetid://15470198023', SkyboxDn = 'rbxassetid://15470151245', SkyboxFt = 'rbxassetid://15470200128', SkyboxLf = 'rbxassetid://15470202648', SkyboxRt = 'rbxassetid://15470204862', SkyboxUp = 'rbxassetid://15470207755', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 11, SunTextureId = 'rbxassetid://6196665106', MoonTextureId = 'rbxassetid://6444320592', }, BetterNight3 = { SkyboxBk = 'rbxassetid://2670643994', SkyboxDn = 'rbxassetid://2670643365', SkyboxFt = 'rbxassetid://2670643214', SkyboxLf = 'rbxassetid://2670643070', SkyboxRt = 'rbxassetid://2670644173', SkyboxUp = 'rbxassetid://2670644331', StarCount = 500, SunAngularSize = 21, MoonAngularSize = 1.5, SunTextureId = 'rbxassetid://6196665106', MoonTextureId = 'rbxassetid://1075087760', }, Galaxy2 = { SkyboxBk = 'rbxassetid://14164368678', SkyboxDn = 'rbxassetid://14164386126', SkyboxFt = 'rbxassetid://14164389230', SkyboxLf = 'rbxassetid://14164398493', SkyboxRt = 'rbxassetid://14164402782', SkyboxUp = 'rbxassetid://14164405298', StarCount = 500, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Aesthetic3 = { SkyboxBk = 'rbxassetid://151165214', SkyboxDn = 'rbxassetid://151165197', SkyboxFt = 'rbxassetid://151165224', SkyboxLf = 'rbxassetid://151165191', SkyboxRt = 'rbxassetid://151165206', SkyboxUp = 'rbxassetid://151165227', StarCount = 500, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, PurpleSpace = { SkyboxBk = 'rbxassetid://15983968922', SkyboxDn = 'rbxassetid://15983966825', SkyboxFt = 'rbxassetid://5260817288', SkyboxLf = 'rbxassetid://15983967420', SkyboxRt = 'rbxassetid://15983966246', SkyboxUp = 'rbxassetid://15983964246', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Pink = { SkyboxBk = 'rbxassetid://271042516', SkyboxDn = 'rbxassetid://271077243', SkyboxFt = 'rbxassetid://271042556', SkyboxLf = 'rbxassetid://271042310', SkyboxRt = 'rbxassetid://271042467', SkyboxUp = 'rbxassetid://271077958', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, PurpleNight = { SkyboxBk = 'rbxassetid://5260808177', SkyboxDn = 'rbxassetid://5260653793', SkyboxFt = 'rbxassetid://5260817288', SkyboxLf = 'rbxassetid://5260800833', SkyboxRt = 'rbxassetid://5260800833', SkyboxUp = 'rbxassetid://5084576400', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Flame = { SkyboxBk = 'rbxassetid://6286780109', SkyboxDn = 'rbxassetid://6286782353', SkyboxFt = 'rbxassetid://6286784186', SkyboxLf = 'rbxassetid://6286785801', SkyboxRt = 'rbxassetid://6286788245', SkyboxUp = 'rbxassetid://6286790025', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Galaxy3 = { SkyboxBk = 'rbxassetid://14543264135', SkyboxDn = 'rbxassetid://14543358958', SkyboxFt = 'rbxassetid://14543257810', SkyboxLf = 'rbxassetid://14543275895', SkyboxRt = 'rbxassetid://14543280890', SkyboxUp = 'rbxassetid://14543371676', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Storm = { SkyboxBk = 'rbxassetid://1618912481', SkyboxDn = 'rbxassetid://1618913943', SkyboxFt = 'rbxassetid://1618913244', SkyboxLf = 'rbxassetid://1618912849', SkyboxRt = 'rbxassetid://1618911568', SkyboxUp = 'rbxassetid://1618913654', SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://1084351190', MoonTextureId = 'rbxassetid://1075087760', }, Milkyway = { SkyboxBk = 'rbxassetid://129876530632297', SkyboxDn = 'rbxassetid://108406529909981', SkyboxFt = 'rbxassetid://104400530594543', SkyboxLf = 'rbxassetid://73372229972523', SkyboxRt = 'rbxassetid://87408857415924', SkyboxUp = 'rbxassetid://137817405681365', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, PurpleClouds = { SkyboxBk = 'rbxassetid://570557514', SkyboxDn = 'rbxassetid://570557775', SkyboxFt = 'rbxassetid://570557559', SkyboxLf = 'rbxassetid://570557620', SkyboxRt = 'rbxassetid://570557672', SkyboxUp = 'rbxassetid://570557727', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, BlueClouds = { SkyboxBk = 'rbxassetid://591058823', SkyboxDn = 'rbxassetid://591059876', SkyboxFt = 'rbxassetid://591058104', SkyboxLf = 'rbxassetid://591057861', SkyboxRt = 'rbxassetid://591057625', SkyboxUp = 'rbxassetid://591059642', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://6196665106', MoonTextureId = 'rbxassetid://6444320592', }, BetterNight = { SkyboxBk = 'rbxassetid://15470289121', SkyboxDn = 'rbxassetid://15470291746', SkyboxFt = 'rbxassetid://15470294431', SkyboxLf = 'rbxassetid://15470297162', SkyboxRt = 'rbxassetid://15470299887', SkyboxUp = 'rbxassetid://15470303050', StarCount = 3000, }, Cloudy = { SkyboxBk = 'rbxassetid://4495864450', SkyboxDn = 'rbxassetid://4495864887', SkyboxFt = 'rbxassetid://4495865458', SkyboxLf = 'rbxassetid://4495866035', SkyboxRt = 'rbxassetid://4495866584', SkyboxUp = 'rbxassetid://4495867486', StarCount = 3000, }, NetherWorld = { SkyboxBk = 'rbxassetid://14365019002', SkyboxDn = 'rbxassetid://14365023350', SkyboxFt = 'rbxassetid://14365018399', SkyboxLf = 'rbxassetid://14365018705', SkyboxRt = 'rbxassetid://14365018143', SkyboxUp = 'rbxassetid://14365019327', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, NewYork = { SkyboxBk = 'rbxassetid://11333973069', SkyboxDn = 'rbxassetid://11333969768', SkyboxFt = 'rbxassetid://11333964303', SkyboxLf = 'rbxassetid://11333971332', SkyboxRt = 'rbxassetid://11333982864', SkyboxUp = 'rbxassetid://11333967970', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://6196665106', MoonTextureId = 'rbxassetid://6444320592', }, Purple2 = { SkyboxBk = 'rbxassetid://8107841671', SkyboxDn = 'rbxassetid://6444884785', SkyboxFt = 'rbxassetid://8107841671', SkyboxLf = 'rbxassetid://8107841671', SkyboxRt = 'rbxassetid://8107841671', SkyboxUp = 'rbxassetid://8107849791', StarCount = 3000, SunAngularSize = 11, MoonAngularSize = 0, SunTextureId = 'rbxassetid://6196665106', MoonTextureId = 'rbxassetid://6444320592', }, FunnyStorm = { SkyboxBk = 'rbxassetid://6280934001', SkyboxDn = 'rbxassetid://6280935347', SkyboxFt = 'rbxassetid://6280936575', SkyboxLf = 'rbxassetid://6280938749', SkyboxRt = 'rbxassetid://6280940989', SkyboxUp = 'rbxassetid://6280942402', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Galaxy = { SkyboxBk = 'rbxassetid://159454299', SkyboxDn = 'rbxassetid://159454296', SkyboxFt = 'rbxassetid://159454293', SkyboxLf = 'rbxassetid://159454293', SkyboxRt = 'rbxassetid://159454293', SkyboxUp = 'rbxassetid://159454288', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, BlueSpace = { SkyboxBk = 'rbxassetid://16876541778', SkyboxDn = 'rbxassetid://16876543880', SkyboxFt = 'rbxassetid://16876546384', SkyboxLf = 'rbxassetid://16876548320', SkyboxRt = 'rbxassetid://16876550345', SkyboxUp = 'rbxassetid://16876552681', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Purple3 = { SkyboxBk = 'rbxassetid://433274085', SkyboxDn = 'rbxassetid://433274194', SkyboxFt = 'rbxassetid://433274131', SkyboxLf = 'rbxassetid://433274370', SkyboxRt = 'rbxassetid://433274429', SkyboxUp = 'rbxassetid://433274285', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Nebula2 = { SkyboxBk = 'rbxassetid://16932794531', SkyboxDn = 'rbxassetid://16932797813', SkyboxFt = 'rbxassetid://16932800523', SkyboxLf = 'rbxassetid://16932803722', SkyboxRt = 'rbxassetid://16932806825', SkyboxUp = 'rbxassetid://16932810138', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Nebula = { SkyboxBk = 'rbxassetid://5260808177', SkyboxDn = 'rbxassetid://5260653793', SkyboxFt = 'rbxassetid://5260817288', SkyboxLf = 'rbxassetid://5260800833', SkyboxRt = 'rbxassetid://5260811073', SkyboxUp = 'rbxassetid://5260824661', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, LunarNight = { SkyboxBk = 'rbxassetid://187713366', SkyboxDn = 'rbxassetid://187712428', SkyboxFt = 'rbxassetid://187712836', SkyboxLf = 'rbxassetid://187713755', SkyboxRt = 'rbxassetid://187714525', SkyboxUp = 'rbxassetid://187712111', StarCount = 0, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Purple = { SkyboxBk = 'rbxassetid://8539982183', SkyboxDn = 'rbxassetid://8539981943', SkyboxFt = 'rbxassetid://8539981721', SkyboxLf = 'rbxassetid://8539981424', SkyboxRt = 'rbxassetid://8539980766', SkyboxUp = 'rbxassetid://8539981085', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Hell = { SkyboxBk = 'rbxassetid://11730840088', SkyboxDn = 'rbxassetid://11730842997', SkyboxFt = 'rbxassetid://11730849615', SkyboxLf = 'rbxassetid://11730852920', SkyboxRt = 'rbxassetid://11730855491', SkyboxUp = 'rbxassetid://11730857150', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 11, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Nebula6 = { SkyboxBk = 'rbxassetid://16694187412', SkyboxDn = 'rbxassetid://16694190947', SkyboxFt = 'rbxassetid://16694194795', SkyboxLf = 'rbxassetid://16694197080', SkyboxRt = 'rbxassetid://16694200892', SkyboxUp = 'rbxassetid://16694204069', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 11, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, DarkishPink = { SkyboxBk = 'rbxassetid://570555736', SkyboxDn = 'rbxassetid://570555964', SkyboxFt = 'rbxassetid://570555800', SkyboxLf = 'rbxassetid://570555840', SkyboxRt = 'rbxassetid://570555882', SkyboxUp = 'rbxassetid://570555929', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 11, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, RedNight = { SkyboxBk = 'rbxassetid://401664839', SkyboxDn = 'rbxassetid://401664862', SkyboxFt = 'rbxassetid://401664960', SkyboxLf = 'rbxassetid://401664881', SkyboxRt = 'rbxassetid://401664901', SkyboxUp = 'rbxassetid://401664936', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Space = { SkyboxBk = 'rbxassetid://166509999', SkyboxDn = 'rbxassetid://166510057', SkyboxFt = 'rbxassetid://166510116', SkyboxLf = 'rbxassetid://166510092', SkyboxRt = 'rbxassetid://166510131', SkyboxUp = 'rbxassetid://166510114', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, OverPlanet = { SkyboxBk = 'rbxassetid://165052268', SkyboxDn = 'rbxassetid://165052286', SkyboxFt = 'rbxassetid://165052328', SkyboxLf = 'rbxassetid://165052365', SkyboxRt = 'rbxassetid://165052306', SkyboxUp = 'rbxassetid://165052345', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 11, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Orange = { SkyboxBk = 'rbxassetid://150939022', SkyboxDn = 'rbxassetid://150939038', SkyboxFt = 'rbxassetid://150939047', SkyboxLf = 'rbxassetid://150939056', SkyboxRt = 'rbxassetid://150939063', SkyboxUp = 'rbxassetid://150939082', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Pastel = { SkyboxBk = 'rbxassetid://2128458653', SkyboxDn = 'rbxassetid://2128462480', SkyboxFt = 'rbxassetid://2128458653', SkyboxLf = 'rbxassetid://2128462027', SkyboxRt = 'rbxassetid://2128462027', SkyboxUp = 'rbxassetid://2128462236', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Beach = { SkyboxBk = 'rbxassetid://173380597', SkyboxDn = 'rbxassetid://173380627', SkyboxFt = 'rbxassetid://173380642', SkyboxLf = 'rbxassetid://173380671', SkyboxRt = 'rbxassetid://173380774', SkyboxUp = 'rbxassetid://173380790', StarCount = 0, SunAngularSize = 21, MoonAngularSize = 11, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, FakeClouds = { SkyboxBk = 'rbxassetid://8496892810', SkyboxDn = 'rbxassetid://8496896250', SkyboxFt = 'rbxassetid://8496892810', SkyboxLf = 'rbxassetid://8496892810', SkyboxRt = 'rbxassetid://8496892810', SkyboxUp = 'rbxassetid://8496897504', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Aesthetic2 = { SkyboxBk = 'rbxassetid://600830446', SkyboxDn = 'rbxassetid://600831635', SkyboxFt = 'rbxassetid://600832720', SkyboxLf = 'rbxassetid://600886090', SkyboxRt = 'rbxassetid://600833862', SkyboxUp = 'rbxassetid://600835177', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Nebula4 = { SkyboxBk = 'rbxassetid://17103618635', SkyboxDn = 'rbxassetid://17103622190', SkyboxFt = 'rbxassetid://17103624898', SkyboxLf = 'rbxassetid://17103628153', SkyboxRt = 'rbxassetid://17103636666', SkyboxUp = 'rbxassetid://17103639457', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, BluePlanet = { SkyboxBk = 'rbxassetid://16888989874', SkyboxDn = 'rbxassetid://16888991855', SkyboxFt = 'rbxassetid://16888995219', SkyboxLf = 'rbxassetid://16888998994', SkyboxRt = 'rbxassetid://16889000916', SkyboxUp = 'rbxassetid://16889004122', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Nebula5 = { SkyboxBk = 'rbxassetid://17124357467', SkyboxDn = 'rbxassetid://17124359797', SkyboxFt = 'rbxassetid://17124362093', SkyboxLf = 'rbxassetid://17124365127', SkyboxRt = 'rbxassetid://17124367200', SkyboxUp = 'rbxassetid://17124369657', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, LunarNight2 = { SkyboxBk = 'rbxassetid://14365026085', SkyboxDn = 'rbxassetid://14365026242', SkyboxFt = 'rbxassetid://14365025735', SkyboxLf = 'rbxassetid://14365025904', SkyboxRt = 'rbxassetid://14365025444', SkyboxUp = 'rbxassetid://14365026442', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Pinkie = { SkyboxBk = 'rbxassetid://11555017034', SkyboxDn = 'rbxassetid://11555013415', SkyboxFt = 'rbxassetid://11555010145', SkyboxLf = 'rbxassetid://11555006545', SkyboxRt = 'rbxassetid://11555000712', SkyboxUp = 'rbxassetid://11554996247', StarCount = 0, SunAngularSize = 0, MoonAngularSize = 1.5, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, PurpleMountains = { SkyboxBk = 'rbxassetid://17901353811', SkyboxDn = 'rbxassetid://17901366771', SkyboxFt = 'rbxassetid://17901356262', SkyboxLf = 'rbxassetid://17901359687', SkyboxRt = 'rbxassetid://17901362326', SkyboxUp = 'rbxassetid://17901365106', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, DarkClouds = { SkyboxBk = 'rbxassetid://190477248', SkyboxDn = 'rbxassetid://190477222', SkyboxFt = 'rbxassetid://190477200', SkyboxLf = 'rbxassetid://190477185', SkyboxRt = 'rbxassetid://190477166', SkyboxUp = 'rbxassetid://190477146', StarCount = 0, SunAngularSize = 0, MoonAngularSize = 1.5, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, BetterNight2 = { SkyboxBk = 'rbxassetid://248431616', SkyboxDn = 'rbxassetid://248431677', SkyboxFt = 'rbxassetid://248431598', SkyboxLf = 'rbxassetid://248431686', SkyboxRt = 'rbxassetid://248431611', SkyboxUp = 'rbxassetid://248431605', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 1.5, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, DarkMountains = { SkyboxBk = 'rbxassetid://5098814730', SkyboxDn = 'rbxassetid://5098815227', SkyboxFt = 'rbxassetid://5098815653', SkyboxLf = 'rbxassetid://5098816155', SkyboxRt = 'rbxassetid://5098820352', SkyboxUp = 'rbxassetid://5098819127', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://6196665106', MoonTextureId = 'rbxassetid://6444320592', }, Nebula3 = { SkyboxBk = 'rbxassetid://17839210699', SkyboxDn = 'rbxassetid://17839215896', SkyboxFt = 'rbxassetid://17839218166', SkyboxLf = 'rbxassetid://17839220800', SkyboxRt = 'rbxassetid://17839223605', SkyboxUp = 'rbxassetid://17839226876', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, BetterSky = { SkyboxBk = 'rbxassetid://591058823', SkyboxDn = 'rbxassetid://591059876', SkyboxFt = 'rbxassetid://591058104', SkyboxLf = 'rbxassetid://591057861', SkyboxRt = 'rbxassetid://591057625', SkyboxUp = 'rbxassetid://591059642', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://6196665106', MoonTextureId = 'rbxassetid://6444320592', }, MagentaOrange = { SkyboxBk = 'rbxassetid://566616113', SkyboxDn = 'rbxassetid://566616232', SkyboxFt = 'rbxassetid://566616141', SkyboxLf = 'rbxassetid://566616044', SkyboxRt = 'rbxassetid://566616082', SkyboxUp = 'rbxassetid://566616187', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Aesthetic = { SkyboxBk = 'rbxassetid://1417494030', SkyboxDn = 'rbxassetid://1417494146', SkyboxFt = 'rbxassetid://1417494253', SkyboxLf = 'rbxassetid://1417494402', SkyboxRt = 'rbxassetid://1417494499', SkyboxUp = 'rbxassetid://1417494643', StarCount = 3000, SunAngularSize = 0, MoonAngularSize = 0, SunTextureId = 'rbxassetid://8281961896', MoonTextureId = 'rbxassetid://6444320592', }, Alien = { SkyboxBk = 'rbxassetid://159248188', SkyboxDn = 'rbxassetid://159248183', SkyboxFt = 'rbxassetid://159248187', SkyboxLf = 'rbxassetid://159248173', SkyboxRt = 'rbxassetid://159248192', SkyboxUp = 'rbxassetid://159248176', StarCount = 0, SunAngularSize = 21, MoonAngularSize = 0, SunTextureId = 'rbxasset://sky/sun.jpg', MoonTextureId = 'rbxassetid://6444320592', }, Lunar = { SkyboxBk = 'rbxassetid://179723991', SkyboxDn = 'rbxassetid://179724005', SkyboxFt = 'rbxassetid://179724018', SkyboxLf = 'rbxassetid://179724029', SkyboxRt = 'rbxassetid://179724037', SkyboxUp = 'rbxassetid://179724054', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 0, SunTextureId = 'rbxasset://sky/sun.jpg', MoonTextureId = 'rbxasset://sky/moon.jpg', }, Retro = { SkyboxBk = 'rbxassetid://16642396302', SkyboxDn = 'rbxassetid://16642397027', SkyboxFt = 'rbxassetid://16642398372', SkyboxLf = 'rbxassetid://16642399309', SkyboxRt = 'rbxassetid://16642400745', SkyboxUp = 'rbxassetid://16642401907', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 0, SunTextureId = 'rbxasset://sky/sun.jpg', MoonTextureId = 'rbxasset://sky/moon.jpg', }, Realistic = { SkyboxBk = 'rbxassetid://402559900', SkyboxDn = 'rbxassetid://402559927', SkyboxFt = 'rbxassetid://402559953', SkyboxLf = 'rbxassetid://402559983', SkyboxRt = 'rbxassetid://402560019', SkyboxUp = 'rbxassetid://402560052', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 0, SunTextureId = 'rbxasset://sky/sun.jpg', MoonTextureId = 'rbxasset://sky/moon.jpg', }, Midnight = { SkyboxBk = 'rbxassetid://154185004', SkyboxDn = 'rbxassetid://154184960', SkyboxFt = 'rbxassetid://154185021', SkyboxLf = 'rbxassetid://154184943', SkyboxRt = 'rbxassetid://154184972', SkyboxUp = 'rbxassetid://154185031', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 0, SunTextureId = 'rbxasset://sky/sun.jpg', MoonTextureId = 'rbxasset://sky/moon.jpg', }, Antartica = { SkyboxBk = 'rbxassetid://2118763079', SkyboxDn = 'rbxassetid://2118766919', SkyboxFt = 'rbxassetid://2118765204', SkyboxLf = 'rbxassetid://2118764070', SkyboxRt = 'rbxassetid://2118761853', SkyboxUp = 'rbxassetid://2118766003', StarCount = 3000, SunAngularSize = 21, SunTextureId = 'rbxasset://sky/sun.jpg', MoonTextureId = 'rbxasset://sky/moon.jpg', }, Moon = { SkyboxBk = 'rbxassetid://17489386489', SkyboxDn = 'rbxassetid://17489387417', SkyboxFt = 'rbxassetid://17489388310', SkyboxLf = 'rbxassetid://17489389632', SkyboxRt = 'rbxassetid://17489391101', SkyboxUp = 'rbxassetid://17489393055', StarCount = 3000, SunAngularSize = 21, SunTextureId = 'rbxasset://sky/sun.jpg', MoonTextureId = 'rbxasset://sky/moon.jpg', }, NightMountains = { SkyboxBk = 'rbxassetid://264908339', SkyboxDn = 'rbxassetid://264907909', SkyboxFt = 'rbxassetid://264909420', SkyboxLf = 'rbxassetid://264909758', SkyboxRt = 'rbxassetid://264908886', SkyboxUp = 'rbxassetid://264907379', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 11, SunTextureId = 'rbxasset://sky/sun.jpg', MoonTextureId = 'rbxasset://sky/moon.jpg', }, Art = { SkyboxBk = 'rbxassetid://9831762777', SkyboxDn = 'rbxassetid://9831764283', SkyboxFt = 'rbxassetid://9831766232', SkyboxLf = 'rbxassetid://9831768023', SkyboxRt = 'rbxassetid://9831768909', SkyboxUp = 'rbxassetid://9831769868', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 11, SunTextureId = 'rbxasset://sky/sun.jpg', MoonTextureId = 'rbxassetid://6444320592', }, Ame = { SkyboxBk = 'rbxassetid://160185927', SkyboxDn = 'rbxassetid://160185945', SkyboxFt = 'rbxassetid://160186027', SkyboxLf = 'rbxassetid://160186075', SkyboxRt = 'rbxassetid://160186126', SkyboxUp = 'rbxassetid://160186148', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 11, SunTextureId = 'rbxasset://sky/sun.jpg', MoonTextureId = 'rbxassetid://6444320592', }, Dawn = { SkyboxBk = 'rbxassetid://250381017', SkyboxDn = 'rbxassetid://254156892', SkyboxFt = 'rbxassetid://250380916', SkyboxLf = 'rbxassetid://250380953', SkyboxRt = 'rbxassetid://250380985', SkyboxUp = 'rbxassetid://250381047', StarCount = 3000, SunAngularSize = 21, MoonAngularSize = 11, SunTextureId = 'rbxasset://sky/sun.jpg', }, }, } function RivalsRuntimeBridge.WorldVisuals.ReadToggle(optionId, fallback) local toggle = Toggles[optionId] if toggle and toggle.Value ~= nil then return toggle.Value == true end return fallback == true end function RivalsRuntimeBridge.WorldVisuals.ReadOption(optionId, fallback) local option = Options[optionId] if option and option.Value ~= nil then return option.Value end return fallback end function RivalsRuntimeBridge.WorldVisuals.NewGuard(target) local guard = { Target = target, Originals = {}, HasOriginal = {}, Overrides = {}, Writing = false, Connection = nil, } guard.Connection = target.Changed:Connect(function(property) if guard.Writing or guard.Overrides[property] == nil then return end local ok, nativeValue = pcall(function() return guard.Target[property] end) if not ok then return end guard.Originals[property] = nativeValue guard.HasOriginal[property] = true guard.Writing = true pcall(function() guard.Target[property] = guard.Overrides[property] end) guard.Writing = false end) return guard end function RivalsRuntimeBridge.WorldVisuals.SetGuardOverride(guard, property, value) if not guard or not guard.Target then return false end local capturedNow = false if guard.HasOriginal[property] ~= true then local ok, nativeValue = pcall(function() return guard.Target[property] end) if not ok then return false end guard.Originals[property] = nativeValue guard.HasOriginal[property] = true capturedNow = true end guard.Overrides[property] = value guard.Writing = true local ok = pcall(function() guard.Target[property] = value end) guard.Writing = false if not ok then guard.Overrides[property] = nil if capturedNow then guard.Originals[property] = nil guard.HasOriginal[property] = nil end end return ok end function RivalsRuntimeBridge.WorldVisuals.ClearGuardOverride(guard, property) if not guard or guard.HasOriginal[property] ~= true then return false end local nativeValue = guard.Originals[property] guard.Overrides[property] = nil guard.Writing = true local ok = pcall(function() guard.Target[property] = nativeValue end) guard.Writing = false guard.Originals[property] = nil guard.HasOriginal[property] = nil return ok end function RivalsRuntimeBridge.WorldVisuals.ReconcileGuard(guard, properties, desired) for _, property in ipairs(properties) do if desired[property] ~= nil then RivalsRuntimeBridge.WorldVisuals.SetGuardOverride(guard, property, desired[property]) else RivalsRuntimeBridge.WorldVisuals.ClearGuardOverride(guard, property) end end end function RivalsRuntimeBridge.WorldVisuals.DestroyGuard(guard) if not guard then return end local properties = {} for property in pairs(guard.HasOriginal) do properties[#properties + 1] = property end for _, property in ipairs(properties) do RivalsRuntimeBridge.WorldVisuals.ClearGuardOverride(guard, property) end if guard.Connection then guard.Connection:Disconnect() guard.Connection = nil end end function RivalsRuntimeBridge.WorldVisuals.RefreshLighting() local world = RivalsRuntimeBridge.WorldVisuals local desired = {} if world.ReadToggle('P1S4T2', false) then desired.Ambient = world.ReadOption('P1S4C1', Color3.new(0.231373, 0.796078, 1)) desired.OutdoorAmbient = world.ReadOption('P1S4C2', Color3.new(0.003922, 0.4, 0.545098)) end if world.ReadToggle('P1S4T3', false) then desired.ClockTime = world.ReadOption('P1S4S1', 12) end if world.ReadToggle('P1S4T4', false) then desired.Brightness = world.ReadOption('P1S4S2', 2) end if world.ReadToggle('P1S4T5', false) then desired.ColorShift_Top = world.ReadOption('P1S4C3', Color3.fromRGB(100, 160, 255)) desired.ColorShift_Bottom = world.ReadOption('P1S4C4', Color3.fromRGB(20, 40, 90)) end if world.ReadToggle('P1S4T6', false) then desired.EnvironmentDiffuseScale = world.ReadOption('P1S4S3', 0) end if world.ReadToggle('P1S4T7', false) then desired.ExposureCompensation = world.ReadOption('P1S4S4', 0) end if world.ReadToggle('P1S4T8', false) then desired.EnvironmentSpecularScale = world.ReadOption('P1S4S5', 0) end if world.ReadToggle('P1S5T1', false) then desired.FogColor = world.ReadOption('P1S5C1', Color3.fromRGB(192, 192, 192)) desired.FogStart = world.ReadOption('P1S5S1', 0) desired.FogEnd = world.ReadOption('P1S5S2', 100000) end if world.ReadToggle('P1S5T2', false) then desired.GeographicLatitude = world.ReadOption('P1S5S3', 41.733) end if world.ReadToggle('P1S5T3', false) then desired.GlobalShadows = false end if world.ReadToggle('P1S5T4', false) then desired.ShadowSoftness = world.ReadOption('P1S5S4', 0.5) end if world.ReadToggle('P1S5T5', false) then desired.LightingStyle = Enum.LightingStyle[world.ReadOption('P1S5D1', 'Realistic')] end if world.ReadToggle('P1S5T6', false) then desired.ShadowColor = world.ReadOption('P1S5C2', Color3.new(0.7, 0.7, 0.72)) end if world.ReadToggle('P1S5T7', false) then desired.PrioritizeLightingQuality = true end if world.ReadToggle('P1S8T1', false) then local enumOk, tonemapperPreset = pcall( function() return Enum.TonemapperPreset[world.ReadOption('P1S8D1', 'Default')] end ) if enumOk and tonemapperPreset then desired.TonemapperPreset = tonemapperPreset end end world.ReconcileGuard(world.LightingGuard, world.LightingProperties, desired) end function RivalsRuntimeBridge.WorldVisuals.ApplyOwnedEffectProperties(className, effect) local definition = RivalsRuntimeBridge.WorldVisuals.PostEffects[className] if not definition or not effect then return end for property, optionInfo in pairs(definition.Properties) do pcall(function() effect[property] = RivalsRuntimeBridge.WorldVisuals.ReadOption(optionInfo[1], optionInfo[2]) end) end pcall(function() effect.Enabled = true end) end function RivalsRuntimeBridge.WorldVisuals.GuardNativeEffect(className, effect) local world = RivalsRuntimeBridge.WorldVisuals if not effect or effect == world.OwnedEffects[className] then return end local guards = world.NativeEffectGuards[className] if not guards then guards = setmetatable({}, { __mode = 'k' }) world.NativeEffectGuards[className] = guards end local guard = guards[effect] if not guard then guard = world.NewGuard(effect) guards[effect] = guard end world.SetGuardOverride(guard, 'Enabled', false) end function RivalsRuntimeBridge.WorldVisuals.RestorePostEffect(className) local world = RivalsRuntimeBridge.WorldVisuals local owned = world.OwnedEffects[className] world.OwnedEffects[className] = nil if owned then pcall(function() owned:Destroy() end) end local guards = world.NativeEffectGuards[className] if guards then for effect, guard in pairs(guards) do world.DestroyGuard(guard) guards[effect] = nil end end world.NativeEffectGuards[className] = nil end function RivalsRuntimeBridge.WorldVisuals.RefreshPostEffect(className) local world = RivalsRuntimeBridge.WorldVisuals local definition = world.PostEffects[className] if not definition or not world.ReadToggle(definition.Toggle, false) then world.RestorePostEffect(className) return end local owned = world.OwnedEffects[className] if not owned or owned.Parent == nil then owned = Instance.new(className) owned.Name = 'ZkxHubWorldVisuals_' .. className world.OwnedEffects[className] = owned world.ApplyOwnedEffectProperties(className, owned) owned.Parent = world.Lighting else world.ApplyOwnedEffectProperties(className, owned) end for _, descendant in ipairs(world.Lighting:GetDescendants()) do if descendant.ClassName == className and descendant ~= owned then world.GuardNativeEffect(className, descendant) end end end function RivalsRuntimeBridge.WorldVisuals.NormalizeAssetId(value) if type(value) == 'number' then return 'rbxassetid://' .. tostring(value) end if type(value) ~= 'string' then return nil end value = value:match('^%s*(.-)%s*$') or '' if value == '' then return nil end if value:match('^%d+$') then return 'rbxassetid://' .. value end return value end function RivalsRuntimeBridge.WorldVisuals.RefreshAmbience() local world = RivalsRuntimeBridge.WorldVisuals local source = world.ReadToggle('P1S14T1', false) and world.NormalizeAssetId(world.ReadOption('P1S14I1', '')) or nil if not source then local owned = world.OwnedAmbience world.OwnedAmbience = nil world.ActiveAmbienceSource = nil if owned then pcall(function() owned:Destroy() end) end return end local sound = world.OwnedAmbience if not sound or sound.Parent == nil then sound = Instance.new('Sound') sound.Name = 'ZkxHubWorldVisuals_Ambience' sound.Looped = true world.OwnedAmbience = sound sound.Parent = world.SoundService end sound.Volume = world.ReadOption('P1S14S1', 1) if world.ActiveAmbienceSource ~= source or sound.SoundId ~= source then world.ActiveAmbienceSource = source sound.SoundId = source sound:Play() elseif not sound.IsPlaying then sound:Play() end end function RivalsRuntimeBridge.WorldVisuals.ScaleNumberSequence(sequence, scale) local keypoints = table.create(#sequence.Keypoints) for _, keypoint in ipairs(sequence.Keypoints) do table.insert(keypoints, NumberSequenceKeypoint.new(keypoint.Time, keypoint.Value * scale, keypoint.Envelope * scale)) end return NumberSequence.new(keypoints) end function RivalsRuntimeBridge.WorldVisuals.ClearWeatherEmitters() local world = RivalsRuntimeBridge.WorldVisuals for _, record in ipairs(world.WeatherEmitters) do if record.Emitter then pcall(function() record.Emitter:Destroy() end) end end world.WeatherEmitters = {} world.ActiveWeatherPreset = nil end function RivalsRuntimeBridge.WorldVisuals.DestroyWeather() local world = RivalsRuntimeBridge.WorldVisuals world.WeatherGeneration = world.WeatherGeneration + 1 world.NextLightningAt = 0 world.ClearWeatherEmitters() local part = world.OwnedWeatherPart world.OwnedWeatherPart = nil if part then pcall(function() part:Destroy() end) end end function RivalsRuntimeBridge.WorldVisuals.ReconcileWeather() local world = RivalsRuntimeBridge.WorldVisuals local intensity = math.max(0, tonumber(world.ReadOption('P1S28S1', 1)) or 1) local rate = math.max(0, tonumber(world.ReadOption('P1S28S2', 1)) or 1) local speed = math.max(0, tonumber(world.ReadOption('P1S28S3', 1)) or 1) local size = math.max(0, tonumber(world.ReadOption('P1S28S4', 1)) or 1) local glow = math.clamp(tonumber(world.ReadOption('P1S28S5', 0.6)) or 0.6, 0, 1) local spread = math.max(0, tonumber(world.ReadOption('P1S28S6', 25)) or 25) local color = world.ReadOption('P1S28C1', Color3.new(1, 1, 1)) if typeof(color) ~= 'Color3' then color = Color3.new(1, 1, 1) end local angle = math.rad(tonumber(world.ReadOption('P1S28S9', 0)) or 0) local strength = tonumber(world.ReadOption('P1S28S8', 0)) or 0 local windX = math.sin(angle) * strength local windZ = math.cos(angle) * strength for _, record in ipairs(world.WeatherEmitters) do local emitter = record.Emitter local spec = record.Spec if emitter and emitter.Parent and spec then emitter.Rate = spec.BaseRate * intensity * rate emitter.Speed = NumberRange.new(spec.SpeedMin * speed, spec.SpeedMax * speed) emitter.Size = world.ScaleNumberSequence(NumberSequence.new(spec.SizeStart, spec.SizeEnd), size) emitter.LightEmission = glow local spreadAngle = spec.BaseSpread * spread emitter.SpreadAngle = Vector2.new(spreadAngle, spreadAngle) emitter.Color = ColorSequence.new(color) emitter.Acceleration = Vector3.new(windX, spec.BaseAccelerationY, windZ) end end end function RivalsRuntimeBridge.WorldVisuals.RebuildWeather(preset) local world = RivalsRuntimeBridge.WorldVisuals local specs = world.WeatherPresets[preset] world.ClearWeatherEmitters() if not specs or not world.OwnedWeatherPart then return end world.ActiveWeatherPreset = preset for _, spec in ipairs(specs) do local emitter = Instance.new('ParticleEmitter') emitter.Name = 'ZkxHubWeatherEmitter' emitter.Shape = Enum.ParticleEmitterShape.Box emitter.EmissionDirection = Enum.NormalId.Bottom emitter.Enabled = true emitter.Texture = spec.Texture emitter.Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 1), NumberSequenceKeypoint.new(0.15, spec.TransparencyMax), NumberSequenceKeypoint.new(0.85, spec.TransparencyMax), NumberSequenceKeypoint.new(1, 1), }) emitter.Lifetime = NumberRange.new(spec.LifetimeMin, spec.LifetimeMax) emitter.Rotation = NumberRange.new(spec.RotationMin, spec.RotationMax) emitter.RotSpeed = NumberRange.new(spec.RotationSpeedMin, spec.RotationSpeedMax) emitter.LightInfluence = 0 if spec.Orientation then emitter.Orientation = spec.Orientation end emitter.Parent = world.OwnedWeatherPart table.insert(world.WeatherEmitters, { Emitter = emitter, Spec = spec }) end world.ReconcileWeather() end function RivalsRuntimeBridge.WorldVisuals.RefreshWeather() local world = RivalsRuntimeBridge.WorldVisuals if not world.ReadToggle('P1S28T1', false) then world.DestroyWeather() return end local preset = world.ReadOption('P1S28D1', 'Snow') if not world.WeatherPresets[preset] then preset = 'Snow' end local part = world.OwnedWeatherPart if not part or part.Parent == nil then part = Instance.new('Part') part.Name = 'ZkxHubWeatherEmitter' part.Anchored = true part.CanCollide = false part.CanQuery = false part.CanTouch = false part.CastShadow = false part.Transparency = 1 part.Size = Vector3.new(140, 1, 140) part.Parent = Workspace world.OwnedWeatherPart = part world.ActiveWeatherPreset = nil world.NextLightningAt = tick() + math.max(0.2, tonumber(world.ReadOption('P1S29S1', 2)) or 2) end if world.ActiveWeatherPreset ~= preset then world.WeatherGeneration = world.WeatherGeneration + 1 world.NextLightningAt = tick() + math.max(0.2, tonumber(world.ReadOption('P1S29S1', 2)) or 2) world.RebuildWeather(preset) else world.ReconcileWeather() end end function RivalsRuntimeBridge.WorldVisuals.AddLightningSegment( instances, startPosition, endPosition, color, thickness, brightness ) local near = Instance.new('Attachment') near.Name = 'ZkxHubLightningAttachment' near.Position = startPosition near.Parent = Workspace.Terrain local far = Instance.new('Attachment') far.Name = 'ZkxHubLightningAttachment' far.Position = endPosition far.Parent = Workspace.Terrain local beam = Instance.new('Beam') beam.Name = 'ZkxHubLightningBeam' beam.Attachment0 = near beam.Attachment1 = far beam.Color = ColorSequence.new(color) beam.Width0 = thickness beam.Width1 = math.max(0.05, thickness * 0.72) beam.FaceCamera = true beam.LightEmission = 1 beam.LightInfluence = 0 beam.Brightness = math.max(1, brightness) beam.Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 0.02), NumberSequenceKeypoint.new(0.8, 0.12), NumberSequenceKeypoint.new(1, 1), }) beam.Parent = Workspace.Terrain table.insert(instances, beam) table.insert(instances, near) table.insert(instances, far) end function RivalsRuntimeBridge.WorldVisuals.BuildLightningPoints(startPosition, endPosition, jaggedness) local distance = (endPosition - startPosition).Magnitude local segments = math.clamp(math.floor(distance / 12), 8, 28) local points = table.create(segments + 1) for index = 0, segments do local alpha = index / segments local point = startPosition:Lerp(endPosition, alpha) if index > 0 and index < segments then local taper = math.sin(alpha * math.pi) point = point + Vector3.new( (math.random() - 0.5) * jaggedness * 2 * taper, (math.random() - 0.5) * jaggedness * taper, (math.random() - 0.5) * jaggedness * 2 * taper ) end points[index + 1] = point end return points end function RivalsRuntimeBridge.WorldVisuals.ResolveWeatherSound() local world = RivalsRuntimeBridge.WorldVisuals local source = world.ReadOption('P1S29I1', '') if type(source) ~= 'string' or source:match('^%s*$') then return 'rbxassetid://79197041664081' end source = world.NormalizeAssetId(source) if not source then return nil end if source:match('^rbxasset') then return source end local customAsset = type(getcustomasset) == 'function' and getcustomasset or (type(getsynasset) == 'function' and getsynasset or nil) if customAsset and type(isfile) == 'function' and isfile(source) then local ok, asset = pcall(customAsset, source) if ok and type(asset) == 'string' then return asset end end return nil end function RivalsRuntimeBridge.WorldVisuals.PlayWeatherThunder(distance) local world = RivalsRuntimeBridge.WorldVisuals if not world.ReadToggle('P1S29T4', true) then return end local source = world.ResolveWeatherSound() if not source then return end local generation = world.WeatherGeneration local delayTime = world.ReadToggle('P1S29T5', true) and distance / 1225 or 0 local volume = (tonumber(world.ReadOption('P1S29S15', 1)) or 1) * math.max(0.3, 1 - distance / 320) local playbackSpeed = 0.75 + math.random() * 0.25 task.delay(delayTime, function() if not world.Initialized or generation ~= world.WeatherGeneration or not world.ReadToggle('P1S28T1', false) then return end local sound = Instance.new('Sound') sound.Name = 'ZkxHubWeatherThunder' sound.SoundId = source sound.Volume = math.max(0, volume) sound.PlaybackSpeed = playbackSpeed sound.PlayOnRemove = true sound.Parent = world.SoundService sound:Destroy() end) end function RivalsRuntimeBridge.WorldVisuals.RenderWeatherLightning(startPosition, endPosition) local world = RivalsRuntimeBridge.WorldVisuals local color = world.ReadOption('P1S29C1', Color3.fromRGB(214, 230, 255)) if typeof(color) ~= 'Color3' then color = Color3.fromRGB(214, 230, 255) end local thickness = math.max(0.05, tonumber(world.ReadOption('P1S29S4', 4)) or 4) local jaggedness = math.max(0, tonumber(world.ReadOption('P1S29S5', 7)) or 7) local branchCount = math.clamp(math.floor(tonumber(world.ReadOption('P1S29S6', 12)) or 12), 0, 24) local flash = math.max(0, tonumber(world.ReadOption('P1S29S7', 8)) or 8) local instances = {} local points = world.BuildLightningPoints(startPosition, endPosition, jaggedness) for index = 1, #points - 1 do world.AddLightningSegment(instances, points[index], points[index + 1], color, thickness, flash) end for _ = 1, branchCount do local sourceIndex = math.random(2, math.max(2, #points - 2)) local source = points[sourceIndex] local branchLength = math.max(4, jaggedness * (1.5 + math.random())) local branchEnd = source + Vector3.new( (math.random() - 0.5) * branchLength * 2, -math.random() * branchLength, (math.random() - 0.5) * branchLength * 2 ) world.AddLightningSegment(instances, source, branchEnd, color, thickness * 0.45, flash * 0.7) end local impact = Instance.new('Attachment') impact.Name = 'ZkxHubLightningImpact' impact.Position = endPosition impact.Parent = Workspace.Terrain table.insert(instances, impact) if flash > 0 then local light = Instance.new('PointLight') light.Name = 'ZkxHubLightningFlash' light.Color = color light.Brightness = flash light.Range = math.max(18, flash * 5) light.Shadows = true light.Parent = impact table.insert(instances, light) end if world.ReadToggle('P1S29T2', true) then local sparkColor = world.ReadOption('P1S29C2', Color3.fromRGB(238, 246, 255)) if typeof(sparkColor) ~= 'Color3' then sparkColor = Color3.fromRGB(238, 246, 255) end local sparkCount = math.clamp(math.floor(tonumber(world.ReadOption('P1S29S8', 18)) or 18), 0, 40) local sparkThickness = math.max(0.2, tonumber(world.ReadOption('P1S29S9', 2.2)) or 2.2) local sparkDistance = math.max(2, tonumber(world.ReadOption('P1S29S10', 22)) or 22) local sparkSpeed = math.max(2, tonumber(world.ReadOption('P1S29S11', 22)) or 22) local sparks = Instance.new('ParticleEmitter') sparks.Name = 'ZkxHubLightningSparks' sparks.Texture = 'rbxassetid://119455261341623' sparks.Rate = 0 sparks.Color = ColorSequence.new(sparkColor) sparks.LightEmission = 1 sparks.Speed = NumberRange.new(sparkSpeed * 0.65, sparkSpeed) sparks.Lifetime = NumberRange.new(math.max(0.1, sparkDistance / sparkSpeed * 0.65), math.max(0.15, sparkDistance / sparkSpeed)) sparks.Size = NumberSequence.new({ NumberSequenceKeypoint.new(0, sparkThickness * 0.035), NumberSequenceKeypoint.new(1, 0), }) sparks.SpreadAngle = Vector2.new(180, 180) sparks.Acceleration = Vector3.new(0, -Workspace.Gravity * 0.2, 0) sparks.Parent = impact sparks:Emit(sparkCount) table.insert(instances, sparks) end if world.ReadToggle('P1S29T3', true) then local explosionColor = world.ReadOption('P1S29C3', Color3.fromRGB(238, 200, 255)) if typeof(explosionColor) ~= 'Color3' then explosionColor = Color3.fromRGB(238, 200, 255) end local explosionSize = math.max(0, tonumber(world.ReadOption('P1S29S13', 0.45)) or 0.45) local explosionBolts = math.clamp(math.floor(tonumber(world.ReadOption('P1S29S14', 14)) or 14), 0, 30) for _ = 1, explosionBolts do local angle = math.random() * math.pi * 2 local length = (4 + math.random() * 12) * explosionSize local arcEnd = endPosition + Vector3.new(math.cos(angle) * length, math.random() * length * 0.35, math.sin(angle) * length) world.AddLightningSegment(instances, endPosition, arcEnd, explosionColor, thickness * 0.22, flash * 0.5) end end task.delay(0.24, function() for _, instance in ipairs(instances) do if instance and instance.Parent then pcall(function() instance:Destroy() end) end end end) end function RivalsRuntimeBridge.WorldVisuals.FireWeatherLightning() local world = RivalsRuntimeBridge.WorldVisuals local camera = Workspace.CurrentCamera if not camera or not world.ReadToggle('P1S29T1', false) then return end local preset = world.ActiveWeatherPreset if preset ~= 'Rain' and preset ~= 'Blizzard' then return end local distanceLimit = math.max(10, tonumber(world.ReadOption('P1S29S2', 80)) or 80) local radius = math.min(18, distanceLimit) + math.random() * math.max(0, distanceLimit - math.min(18, distanceLimit)) local angle = math.random() * math.pi * 2 local cameraPosition = camera.CFrame.Position local strikeX = cameraPosition.X + math.cos(angle) * radius local strikeZ = cameraPosition.Z + math.sin(angle) * radius local height = math.max(50, tonumber(world.ReadOption('P1S29S3', 220)) or 220) local params = RaycastParams.new() params.FilterType = Enum.RaycastFilterType.Exclude if LP.Character then params.FilterDescendantsInstances = { LP.Character } end local groundY = cameraPosition.Y - 40 local result = Workspace:Raycast(Vector3.new(strikeX, cameraPosition.Y + height, strikeZ), Vector3.new(0, -2000, 0), params) if result then groundY = result.Position.Y end local finishPosition = Vector3.new(strikeX, groundY, strikeZ) local startPosition = Vector3.new(strikeX + (math.random() - 0.5) * 30, groundY + height, strikeZ + (math.random() - 0.5) * 30) world.RenderWeatherLightning(startPosition, finishPosition) world.PlayWeatherThunder(radius) end function RivalsRuntimeBridge.WorldVisuals.UpdateWeather() local world = RivalsRuntimeBridge.WorldVisuals local part = world.OwnedWeatherPart local camera = Workspace.CurrentCamera if part and part.Parent and camera then local height = tonumber(world.ReadOption('P1S28S7', 45)) or 45 part.Position = camera.CFrame.Position + Vector3.new(0, height, 0) end if not part or not world.ReadToggle('P1S29T1', false) or (world.ActiveWeatherPreset ~= 'Rain' and world.ActiveWeatherPreset ~= 'Blizzard') then return end local now = tick() if now >= world.NextLightningAt then local interval = math.max(0.2, tonumber(world.ReadOption('P1S29S1', 2)) or 2) world.NextLightningAt = now + interval world.FireWeatherLightning() end end function RivalsRuntimeBridge.WorldVisuals.RestoreMotionBlur() local world = RivalsRuntimeBridge.WorldVisuals local effect = world.MotionBlurEffect world.MotionBlurEffect = nil world.MotionBlurLastCFrame = nil world.MotionBlurSize = 0 if effect then pcall(function() effect:Destroy() end) end end function RivalsRuntimeBridge.WorldVisuals.UpdateMotionBlur(deltaTime) local world = RivalsRuntimeBridge.WorldVisuals if not world.ReadToggle('PX2T1', false) then if world.MotionBlurEffect or world.MotionBlurLastCFrame then world.RestoreMotionBlur() end return end local camera = Workspace.CurrentCamera if not camera then return end local effect = world.MotionBlurEffect if not effect or effect.Parent == nil then effect = Instance.new('BlurEffect') effect.Name = 'ZkxHubWorldVisuals_MotionBlur' effect.Size = 0 effect.Parent = world.Lighting world.MotionBlurEffect = effect end local current = camera.CFrame local last = world.MotionBlurLastCFrame world.MotionBlurLastCFrame = current local dt = math.max(tonumber(deltaTime) or 0, 1 / 240) local speed = 0 if last then local source = world.ReadOption('PX2D1', 'Camera + Movement') local turn = math.acos(math.clamp(current.LookVector:Dot(last.LookVector), -1, 1)) local travel = (current.Position - last.Position).Magnitude if travel < 60 then -- a hard flick is ~10 rad/s and a sprint ~20 studs/s, so turning counts far more than walking local turnSpeed = source == 'Movement' and 0 or turn / dt / 10 local moveSpeed = source == 'Camera Turning' and 0 or travel / dt / 80 speed = turnSpeed + moveSpeed end end local threshold = math.clamp(tonumber(world.ReadOption('PX2S3', 0)) or 0, 0, 0.95) if threshold > 0 then speed = math.max(speed - threshold, 0) / (1 - threshold) end local sensitivity = math.max(tonumber(world.ReadOption('PX2S2', 1)) or 1, 0) local ceiling = math.clamp(tonumber(world.ReadOption('PX2S1', 14)) or 14, 0, 56) local target = math.min(ceiling, speed * sensitivity * ceiling) local rate if target > world.MotionBlurSize then rate = math.clamp(tonumber(world.ReadOption('PX2S4', 14)) or 14, 1, 60) else rate = math.clamp(tonumber(world.ReadOption('PX2S5', 14)) or 14, 1, 60) end world.MotionBlurSize += (target - world.MotionBlurSize) * math.min(1, dt * rate) effect.Size = world.MotionBlurSize end function RivalsRuntimeBridge.WorldVisuals.UpdateFrame(deltaTime) RivalsRuntimeBridge.WorldVisuals.UpdateWeather() RivalsRuntimeBridge.WorldVisuals.UpdateMotionBlur(deltaTime) end function RivalsRuntimeBridge.WorldVisuals.UpdateStretchedResolution() local world = RivalsRuntimeBridge.WorldVisuals local camera = Workspace.CurrentCamera if not camera then return end local xRatio = math.clamp(tonumber(world.ReadOption('P1S30S1', 1)) or 1, 0.01, 1) local yRatio = math.clamp(tonumber(world.ReadOption('P1S30S2', 1)) or 1, 0.01, 1) local base = camera.CFrame local result = base * CFrame.new(0, 0, 0, xRatio, 0, 0, 0, yRatio, 0, 0, 0, 1) world.StretchedLastCamera = camera world.StretchedLastBase = base world.StretchedLastResult = result camera.CFrame = result end function RivalsRuntimeBridge.WorldVisuals.RestoreStretchedResolution() local world = RivalsRuntimeBridge.WorldVisuals if world.StretchedResolutionBound then RunService:UnbindFromRenderStep('ZkxHubStretchedResolution') world.StretchedResolutionBound = false end local camera = world.StretchedLastCamera if camera and camera.Parent and world.StretchedLastBase and world.StretchedLastResult and camera.CFrame == world.StretchedLastResult then camera.CFrame = world.StretchedLastBase end world.StretchedLastCamera = nil world.StretchedLastBase = nil world.StretchedLastResult = nil end function RivalsRuntimeBridge.WorldVisuals.RefreshStretchedResolution() local world = RivalsRuntimeBridge.WorldVisuals if not world.ReadToggle('P1S30T1', false) then world.RestoreStretchedResolution() return end if not world.StretchedResolutionBound then world.StretchedResolutionBound = true RunService:BindToRenderStep( 'ZkxHubStretchedResolution', Enum.RenderPriority.Camera.Value + 1, GuardRivalsCallback('WorldVisuals_StretchedResolution', world.UpdateStretchedResolution) ) end end function RivalsRuntimeBridge.WorldVisuals.LoadCustomSkyboxes() local world = RivalsRuntimeBridge.WorldVisuals if world.CustomSkyboxesLoaded then return end world.CustomSkyboxesLoaded = true world.CustomSkyboxes = {} if not isfile(world.CustomSkyPath) then return end local readOk, raw = pcall(readfile, world.CustomSkyPath) local decodeOk, decoded = pcall(function() return game:GetService('HttpService'):JSONDecode(raw) end) if not readOk or not decodeOk or type(decoded) ~= 'table' then return end for name, faces in pairs(decoded) do if type(name) == 'string' and name ~= '' and type(faces) == 'table' and world.SkyPresets[name] == nil then local normalized = {} local valid = true for property in pairs(world.SkyFaceInputs) do normalized[property] = world.NormalizeAssetId(faces[property]) if not normalized[property] then valid = false break end end if valid then world.CustomSkyboxes[name] = normalized end end end end function RivalsRuntimeBridge.WorldVisuals.SaveCustomSkyboxes() local world = RivalsRuntimeBridge.WorldVisuals return pcall(function() writefile(world.CustomSkyPath, game:GetService('HttpService'):JSONEncode(world.CustomSkyboxes)) end) end function RivalsRuntimeBridge.WorldVisuals.ResolveSkyPresetNames() local world = RivalsRuntimeBridge.WorldVisuals world.LoadCustomSkyboxes() local names = table.clone(world.SkyPresetNames) local customNames = {} for name in pairs(world.CustomSkyboxes) do customNames[#customNames + 1] = name end table.sort(customNames) for _, name in ipairs(customNames) do names[#names + 1] = name end return names end function RivalsRuntimeBridge.WorldVisuals.ResolveCustomSkyNames() RivalsRuntimeBridge.WorldVisuals.LoadCustomSkyboxes() local names = {} for name in pairs(RivalsRuntimeBridge.WorldVisuals.CustomSkyboxes) do names[#names + 1] = name end table.sort(names) return names end function RivalsRuntimeBridge.WorldVisuals.ResolveSkyPreset(name) local world = RivalsRuntimeBridge.WorldVisuals world.LoadCustomSkyboxes() return world.SkyPresets[name] or world.CustomSkyboxes[name] end RivalsRuntimeBridge.WorldVisuals.WorldPresetManagedToggles = { 'P1S4T2', 'P1S4T3', 'P1S4T4', 'P1S4T5', 'P1S4T6', 'P1S4T7', 'P1S4T8', 'P1S5T1', 'P1S5T2', 'P1S5T3', 'P1S5T4', 'P1S5T5', 'P1S5T6', 'P1S5T7', 'P1S6T1', 'P1S7T1', 'P1S8T1', 'P1S10T1', 'P1S12T1', 'P1S13T1', } RivalsRuntimeBridge.WorldVisuals.WorldPresetOrder = { 'Game Default', 'Competitive Clarity', 'Sunny Day', 'Golden Hour', 'Cinematic', 'Midnight', 'Neon Nights', 'Vaporwave', 'Crimson Dusk', 'Arctic', 'Toxic', 'Deep Space', 'Noir', 'Retro Aesthetic', 'Moody Dawn', } -- ====================================================================== -- World presets -- ====================================================================== RivalsRuntimeBridge.WorldVisuals.WorldPresets = { ['Game Default'] = { Toggles = {}, Options = {}, Colors = {} }, ['Competitive Clarity'] = { Toggles = { P1S4T3 = true, P1S4T4 = true, P1S4T7 = true, P1S4T2 = true, P1S5T1 = true, P1S5T3 = true, P1S7T1 = true, P1S13T1 = true, P1S12T1 = true, }, Options = { P1S4S1 = 12, P1S4S2 = 2.8, P1S4S4 = 0.1, P1S5S1 = 0, P1S5S2 = 10000, P1S7S1 = 0.05, P1S7S2 = 0.03, P1S7S3 = 0.05, P1S13S1 = 0, P1S13S2 = 0, P1S13S3 = 0, P1S13S4 = 0, P1S12D1 = 'FPSBoost', }, Colors = { P1S4C1 = { 170, 170, 170 }, P1S4C2 = { 170, 170, 170 }, P1S5C1 = { 200, 200, 200 }, P1S7C1 = { 255, 255, 255 }, }, }, ['Sunny Day'] = { Toggles = { P1S4T3 = true, P1S4T4 = true, P1S4T5 = true, P1S4T6 = true, P1S4T7 = true, P1S4T8 = true, P1S5T4 = true, P1S6T1 = true, P1S7T1 = true, P1S10T1 = true, }, Options = { P1S4S1 = 13, P1S4S2 = 2.4, P1S4S3 = 0.4, P1S4S4 = 0.05, P1S4S5 = 0.3, P1S5S4 = 0.4, P1S6S1 = 0.45, P1S6S2 = 32, P1S6S3 = 1.35, P1S7S1 = 0.15, P1S7S2 = 0.02, P1S7S3 = 0.06, P1S10S1 = 0.25, P1S10S2 = 0.15, }, Colors = { P1S4C3 = { 255, 240, 200 }, P1S4C4 = { 160, 170, 190 }, P1S7C1 = { 255, 250, 240 }, }, }, ['Golden Hour'] = { Toggles = { P1S4T2 = true, P1S4T3 = true, P1S4T4 = true, P1S4T5 = true, P1S4T6 = true, P1S4T7 = true, P1S4T8 = true, P1S5T4 = true, P1S6T1 = true, P1S7T1 = true, P1S10T1 = true, P1S13T1 = true, }, Options = { P1S4S1 = 17.5, P1S4S2 = 2.5, P1S4S3 = 0.4, P1S4S4 = 0.15, P1S4S5 = 0.3, P1S5S4 = 0.35, P1S6S1 = 0.55, P1S6S2 = 40, P1S6S3 = 1.2, P1S7S1 = 0.12, P1S7S2 = 0.01, P1S7S3 = 0.06, P1S10S1 = 0.28, P1S10S2 = 0.18, P1S13S1 = 0.35, P1S13S2 = 0.25, P1S13S3 = 0.25, P1S13S4 = 0.6, }, Colors = { P1S4C1 = { 140, 120, 105 }, P1S4C2 = { 150, 115, 85 }, P1S4C3 = { 255, 180, 110 }, P1S4C4 = { 120, 70, 40 }, P1S7C1 = { 255, 235, 215 }, P1S13C1 = { 255, 170, 110 }, P1S13C2 = { 120, 70, 50 }, }, }, ['Cinematic'] = { Toggles = { P1S4T3 = true, P1S4T4 = true, P1S4T5 = true, P1S4T6 = true, P1S4T7 = true, P1S4T8 = true, P1S5T4 = true, P1S6T1 = true, P1S7T1 = true, P1S10T1 = true, P1S13T1 = true, }, Options = { P1S4S1 = 15.5, P1S4S2 = 2.2, P1S4S3 = 0.4, P1S4S4 = -0.1, P1S4S5 = 0.4, P1S5S4 = 0.25, P1S6S1 = 0.45, P1S6S2 = 36, P1S6S3 = 1.3, P1S7S1 = 0.18, P1S7S2 = -0.03, P1S7S3 = 0.18, P1S10S1 = 0.18, P1S10S2 = 0.12, P1S13S1 = 0.3, P1S13S2 = 0.2, P1S13S3 = 0.1, P1S13S4 = 0.35, }, Colors = { P1S4C3 = { 255, 200, 150 }, P1S4C4 = { 40, 70, 80 }, P1S7C1 = { 225, 240, 245 }, P1S13C1 = { 170, 200, 210 }, P1S13C2 = { 90, 110, 120 }, }, }, ['Midnight'] = { Toggles = { P1S4T2 = true, P1S4T3 = true, P1S4T4 = true, P1S4T5 = true, P1S4T7 = true, P1S5T4 = true, P1S6T1 = true, P1S7T1 = true, P1S13T1 = true, P1S12T1 = true, }, Options = { P1S4S1 = 0, P1S4S2 = 1.2, P1S4S4 = 0.1, P1S5S4 = 0.5, P1S6S1 = 0.35, P1S6S2 = 28, P1S6S3 = 1.4, P1S7S1 = -0.08, P1S7S2 = 0, P1S7S3 = 0.05, P1S13S1 = 0.32, P1S13S2 = 0.2, P1S13S3 = 0, P1S13S4 = 0.55, P1S12D1 = 'BetterNight', }, Colors = { P1S4C1 = { 70, 80, 110 }, P1S4C2 = { 60, 70, 105 }, P1S4C3 = { 90, 110, 170 }, P1S4C4 = { 25, 30, 55 }, P1S7C1 = { 205, 215, 255 }, P1S13C1 = { 110, 130, 180 }, P1S13C2 = { 25, 35, 70 }, }, }, ['Neon Nights'] = { Toggles = { P1S4T2 = true, P1S4T3 = true, P1S4T4 = true, P1S4T5 = true, P1S6T1 = true, P1S7T1 = true, P1S13T1 = true, P1S12T1 = true, }, Options = { P1S4S1 = 0, P1S4S2 = 1.6, P1S6S1 = 0.9, P1S6S2 = 56, P1S6S3 = 0.8, P1S7S1 = 0.25, P1S7S2 = -0.02, P1S7S3 = 0.12, P1S13S1 = 0.28, P1S13S2 = 0.15, P1S13S3 = 0, P1S13S4 = 0.5, P1S12D1 = 'PurpleNight', }, Colors = { P1S4C1 = { 85, 70, 120 }, P1S4C2 = { 70, 55, 110 }, P1S4C3 = { 150, 80, 255 }, P1S4C4 = { 30, 15, 60 }, P1S7C1 = { 235, 220, 255 }, P1S13C1 = { 150, 90, 220 }, P1S13C2 = { 40, 20, 80 }, }, }, ['Vaporwave'] = { Toggles = { P1S4T2 = true, P1S4T3 = true, P1S4T4 = true, P1S4T5 = true, P1S6T1 = true, P1S7T1 = true, P1S13T1 = true, P1S12T1 = true, }, Options = { P1S4S1 = 18, P1S4S2 = 2.2, P1S6S1 = 0.7, P1S6S2 = 48, P1S6S3 = 1, P1S7S1 = 0.3, P1S7S2 = 0, P1S7S3 = 0.08, P1S13S1 = 0.3, P1S13S2 = 0.25, P1S13S3 = 0.15, P1S13S4 = 0.55, P1S12D1 = 'Aesthetic', }, Colors = { P1S4C1 = { 190, 140, 200 }, P1S4C2 = { 170, 120, 190 }, P1S4C3 = { 255, 120, 200 }, P1S4C4 = { 90, 60, 160 }, P1S7C1 = { 255, 210, 240 }, P1S13C1 = { 235, 130, 200 }, P1S13C2 = { 110, 70, 170 }, }, }, ['Crimson Dusk'] = { Toggles = { P1S4T2 = true, P1S4T3 = true, P1S4T4 = true, P1S4T5 = true, P1S6T1 = true, P1S7T1 = true, P1S10T1 = true, P1S13T1 = true, P1S12T1 = true, }, Options = { P1S4S1 = 18.2, P1S4S2 = 2, P1S6S1 = 0.6, P1S6S2 = 42, P1S6S3 = 1.1, P1S7S1 = 0.15, P1S7S2 = -0.02, P1S7S3 = 0.1, P1S10S1 = 0.3, P1S10S2 = 0.2, P1S13S1 = 0.42, P1S13S2 = 0.3, P1S13S3 = 0.3, P1S13S4 = 0.6, P1S12D1 = 'Flame', }, Colors = { P1S4C1 = { 150, 95, 90 }, P1S4C2 = { 140, 80, 75 }, P1S4C3 = { 255, 90, 70 }, P1S4C4 = { 70, 20, 25 }, P1S7C1 = { 255, 215, 205 }, P1S13C1 = { 225, 90, 70 }, P1S13C2 = { 80, 20, 25 }, }, }, ['Arctic'] = { Toggles = { P1S4T2 = true, P1S4T3 = true, P1S4T4 = true, P1S4T5 = true, P1S4T6 = true, P1S4T7 = true, P1S4T8 = true, P1S5T1 = true, P1S5T6 = true, P1S6T1 = true, P1S7T1 = true, P1S12T1 = true, }, Options = { P1S4S1 = 11, P1S4S2 = 2.6, P1S4S3 = 0.6, P1S4S4 = 0.05, P1S4S5 = 0.5, P1S5S1 = 60, P1S5S2 = 1500, P1S6S1 = 0.4, P1S6S2 = 30, P1S6S3 = 1.5, P1S7S1 = -0.12, P1S7S2 = 0.04, P1S7S3 = 0.05, P1S12D1 = 'Winter', }, Colors = { P1S4C1 = { 200, 215, 235 }, P1S4C2 = { 195, 210, 235 }, P1S4C3 = { 220, 235, 255 }, P1S4C4 = { 150, 170, 200 }, P1S5C1 = { 225, 235, 245 }, P1S5C2 = { 170, 185, 205 }, P1S7C1 = { 225, 240, 255 }, }, }, ['Toxic'] = { Toggles = { P1S4T2 = true, P1S4T3 = true, P1S4T4 = true, P1S4T5 = true, P1S5T1 = true, P1S6T1 = true, P1S7T1 = true, P1S12T1 = true, }, Options = { P1S4S1 = 14, P1S4S2 = 2.1, P1S5S1 = 40, P1S5S2 = 900, P1S6S1 = 0.65, P1S6S2 = 44, P1S6S3 = 1, P1S7S1 = 0.2, P1S7S2 = -0.02, P1S7S3 = 0.1, P1S12D1 = 'GreenHaze', }, Colors = { P1S4C1 = { 120, 180, 130 }, P1S4C2 = { 95, 160, 110 }, P1S4C3 = { 150, 255, 170 }, P1S4C4 = { 25, 70, 35 }, P1S5C1 = { 140, 200, 150 }, P1S7C1 = { 215, 255, 220 }, }, }, ['Deep Space'] = { Toggles = { P1S4T2 = true, P1S4T3 = true, P1S4T4 = true, P1S4T5 = true, P1S4T7 = true, P1S6T1 = true, P1S7T1 = true, P1S13T1 = true, P1S12T1 = true, }, Options = { P1S4S1 = 0, P1S4S2 = 1.8, P1S4S4 = 0.05, P1S6S1 = 0.6, P1S6S2 = 40, P1S6S3 = 1.1, P1S7S1 = 0.1, P1S7S2 = 0, P1S7S3 = 0.1, P1S13S1 = 0.15, P1S13S2 = 0.1, P1S13S3 = 0, P1S13S4 = 0.3, P1S12D1 = 'Space2', }, Colors = { P1S4C1 = { 90, 90, 130 }, P1S4C2 = { 80, 80, 120 }, P1S4C3 = { 130, 120, 220 }, P1S4C4 = { 20, 20, 50 }, P1S7C1 = { 225, 225, 255 }, P1S13C1 = { 90, 90, 140 }, P1S13C2 = { 20, 20, 50 }, }, }, ['Noir'] = { Toggles = { P1S4T3 = true, P1S4T4 = true, P1S4T7 = true, P1S5T4 = true, P1S6T1 = true, P1S7T1 = true, P1S13T1 = true, P1S12T1 = true, }, Options = { P1S4S1 = 16, P1S4S2 = 2.4, P1S4S4 = -0.15, P1S5S4 = 0.15, P1S6S1 = 0.3, P1S6S2 = 30, P1S6S3 = 1.6, P1S7S1 = -1, P1S7S2 = -0.04, P1S7S3 = 0.35, P1S13S1 = 0.38, P1S13S2 = 0.15, P1S13S3 = 0, P1S13S4 = 0.5, P1S12D1 = 'DarkClouds', }, Colors = { P1S7C1 = { 255, 255, 255 }, P1S13C1 = { 140, 140, 140 }, P1S13C2 = { 40, 40, 40 }, }, }, ['Retro Aesthetic'] = { Toggles = { P1S6T1 = true, P1S7T1 = true, P1S8T1 = true, P1S12T1 = true, P1S13T1 = true, }, Options = { P1S6S1 = 0.47, P1S6S2 = 24, P1S6S3 = 0.51, P1S7S1 = -0.07, P1S7S2 = -0.07, P1S7S3 = -0.05, P1S8D1 = 'Retro', P1S13S1 = 0, P1S13S2 = 1, P1S13S3 = 0, P1S13S4 = 0, P1S12D1 = 'Aesthetic2', }, Colors = { P1S7C1 = { 255, 255, 255 }, P1S13C1 = { 200, 170, 108 }, P1S13C2 = { 92, 60, 14 }, }, }, ['Moody Dawn'] = { Toggles = { P1S4T2 = true, P1S4T3 = true, P1S4T4 = true, P1S4T5 = true, P1S4T7 = true, P1S6T1 = true, P1S7T1 = true, P1S12T1 = true, }, Options = { P1S4S1 = 6.87, P1S4S2 = 2.72, P1S4S4 = -1.07, P1S6S1 = 0.32, P1S6S2 = 9, P1S6S3 = 0.22, P1S7S1 = 0.03, P1S7S2 = 0.03, P1S7S3 = 0.07, P1S12D1 = 'Nebula3', }, Colors = { P1S4C1 = { 57, 138, 167 }, P1S4C2 = { 211, 69, 255 }, P1S4C3 = { 135, 100, 255 }, P1S4C4 = { 210, 188, 100 }, P1S7C1 = { 255, 255, 255 }, }, }, } RivalsRuntimeBridge.WorldVisuals.AmbiencePresetOrder = { 'Heavy Rain', 'Rain On Window', 'Rain At 3 A.M.', 'Ocean Surf', 'Howling Wind', 'Night Crickets', 'Cozy Fire', 'Lofi Focus', } RivalsRuntimeBridge.WorldVisuals.AmbiencePresets = { ['Heavy Rain'] = 'rbxassetid://9112853286', ['Rain On Window'] = 'rbxassetid://9112856394', ['Rain At 3 A.M.'] = 'rbxassetid://130699135921413', ['Ocean Surf'] = 'rbxassetid://9117143192', ['Howling Wind'] = 'rbxassetid://5516579371', ['Night Crickets'] = 'rbxassetid://9112764573', ['Cozy Fire'] = 'rbxassetid://150367086', ['Lofi Focus'] = 'rbxassetid://136419664364400', } function RivalsRuntimeBridge.WorldVisuals.ApplyWorldPreset(name) local world = RivalsRuntimeBridge.WorldVisuals local preset = world.WorldPresets[name] if not preset then return end local wanted = preset.Toggles or {} for _, toggleId in ipairs(world.WorldPresetManagedToggles) do local toggle = Toggles and Toggles[toggleId] if toggle and wanted[toggleId] ~= true and toggle.Value then toggle:SetValue(false) end end for optionId, value in pairs(preset.Options or {}) do local option = Options and Options[optionId] if option and type(option.SetValue) == 'function' then option:SetValue(value) end end for optionId, rgb in pairs(preset.Colors or {}) do local option = Options and Options[optionId] if option and type(option.SetValueRGB) == 'function' then option:SetValueRGB(Color3.fromRGB(rgb[1], rgb[2], rgb[3])) end end for _, toggleId in ipairs(world.WorldPresetManagedToggles) do local toggle = Toggles and Toggles[toggleId] if toggle and wanted[toggleId] == true and not toggle.Value then toggle:SetValue(true) end end world.RefreshAll() end function RivalsRuntimeBridge.WorldVisuals.RefreshSkyDropdowns() local world = RivalsRuntimeBridge.WorldVisuals local presetOption = Options.P1S12D1 local savedOption = Options.P1S12D2 if presetOption and type(presetOption.SetValues) == 'function' then pcall(function() presetOption:SetValues(world.ResolveSkyPresetNames()) end) end if savedOption and type(savedOption.SetValues) == 'function' then pcall(function() savedOption:SetValues(world.ResolveCustomSkyNames()) end) end end function RivalsRuntimeBridge.WorldVisuals.AddCustomSkybox(name, faces) local world = RivalsRuntimeBridge.WorldVisuals world.LoadCustomSkyboxes() name = type(name) == 'string' and (name:match('^%s*(.-)%s*$') or '') or '' if name == '' or world.SkyPresets[name] ~= nil or world.CustomSkyboxes[name] ~= nil then return false end local normalized = {} for property in pairs(world.SkyFaceInputs) do normalized[property] = world.NormalizeAssetId(faces[property]) if not normalized[property] then return false end end world.CustomSkyboxes[name] = normalized if not world.SaveCustomSkyboxes() then world.CustomSkyboxes[name] = nil return false end world.RefreshSkyDropdowns() return true end function RivalsRuntimeBridge.WorldVisuals.AddCustomSkyboxFromOptions() local world = RivalsRuntimeBridge.WorldVisuals local faces = {} for property, optionId in pairs(world.SkyFaceInputs) do faces[property] = world.ReadOption(optionId, '') end local added = world.AddCustomSkybox(world.ReadOption('P1S12I7', ''), faces) if added then local nameOption = Options.P1S12I7 if nameOption and type(nameOption.SetValue) == 'function' then nameOption:SetValue('') end for _, optionId in pairs(world.SkyFaceInputs) do local option = Options[optionId] if option and type(option.SetValue) == 'function' then option:SetValue('') end end end return added end function RivalsRuntimeBridge.WorldVisuals.RemoveSelectedCustomSkybox() local world = RivalsRuntimeBridge.WorldVisuals local name = world.ReadOption('P1S12D2', nil) if type(name) ~= 'string' or world.CustomSkyboxes[name] == nil then return false end local removed = world.CustomSkyboxes[name] world.CustomSkyboxes[name] = nil if not world.SaveCustomSkyboxes() then world.CustomSkyboxes[name] = removed return false end world.RefreshSkyDropdowns() local presetOption = Options.P1S12D1 if presetOption and presetOption.Value == name and type(presetOption.SetValue) == 'function' then presetOption:SetValue(nil) end local savedOption = Options.P1S12D2 if savedOption and savedOption.Value == name and type(savedOption.SetValue) == 'function' then savedOption:SetValue(nil) end world.RefreshSky() return true end function RivalsRuntimeBridge.WorldVisuals.RestoreSky() local world = RivalsRuntimeBridge.WorldVisuals local owned = world.OwnedSky world.OwnedSky = nil world.ActiveSkyName = nil if owned then pcall(function() owned:Destroy() end) end for sky, guard in pairs(world.NativeSkyGuards) do world.DestroyGuard(guard) world.NativeSkyGuards[sky] = nil end end function RivalsRuntimeBridge.WorldVisuals.GuardNativeSky(sky, preset) local world = RivalsRuntimeBridge.WorldVisuals if not sky or sky == world.OwnedSky then return end local guard = world.NativeSkyGuards[sky] if not guard then guard = world.NewGuard(sky) world.NativeSkyGuards[sky] = guard end world.ReconcileGuard(guard, world.SkyProperties, preset or {}) end function RivalsRuntimeBridge.WorldVisuals.RefreshSky() local world = RivalsRuntimeBridge.WorldVisuals local presetName = world.ReadOption('P1S12D1', '') local preset = world.ReadToggle('P1S12T1', false) and world.ResolveSkyPreset(presetName) or nil if not preset then world.RestoreSky() return end local nativeSkies = {} for _, child in ipairs(world.Lighting:GetChildren()) do if child:IsA('Sky') and child ~= world.OwnedSky then nativeSkies[#nativeSkies + 1] = child end end if #nativeSkies > 0 then local owned = world.OwnedSky world.OwnedSky = nil world.ActiveSkyName = nil if owned then pcall(function() owned:Destroy() end) end for _, sky in ipairs(nativeSkies) do world.GuardNativeSky(sky, preset) end return end if not world.OwnedSky or world.OwnedSky.Parent == nil or world.ActiveSkyName ~= presetName then local previous = world.OwnedSky world.OwnedSky = nil if previous then pcall(function() previous:Destroy() end) end local sky = Instance.new('Sky') sky.Name = 'ZkxHubWorldVisuals_Sky' for property, value in pairs(preset) do pcall(function() sky[property] = value end) end world.OwnedSky = sky world.ActiveSkyName = presetName sky.Parent = world.Lighting end end function RivalsRuntimeBridge.WorldVisuals.ResolveAtmosphereValues() local world = RivalsRuntimeBridge.WorldVisuals return { Color = world.ReadOption('P1S13C1', Color3.new(0.7843, 0.6667, 0.4235)), Decay = world.ReadOption('P1S13C2', Color3.new(0.3608, 0.2353, 0.0549)), Density = world.ReadOption('P1S13S1', 0.395), Offset = world.ReadOption('P1S13S2', 0), Glare = world.ReadOption('P1S13S3', 0), Haze = world.ReadOption('P1S13S4', 0), } end function RivalsRuntimeBridge.WorldVisuals.RestoreAtmosphere() local world = RivalsRuntimeBridge.WorldVisuals local owned = world.OwnedAtmosphere world.OwnedAtmosphere = nil if owned then pcall(function() owned:Destroy() end) end for atmosphere, guard in pairs(world.NativeAtmosphereGuards) do world.DestroyGuard(guard) world.NativeAtmosphereGuards[atmosphere] = nil end end function RivalsRuntimeBridge.WorldVisuals.GuardNativeAtmosphere(atmosphere, desired) local world = RivalsRuntimeBridge.WorldVisuals if not atmosphere or atmosphere == world.OwnedAtmosphere then return end local guard = world.NativeAtmosphereGuards[atmosphere] if not guard then guard = world.NewGuard(atmosphere) world.NativeAtmosphereGuards[atmosphere] = guard end world.ReconcileGuard(guard, world.AtmosphereProperties, desired) end function RivalsRuntimeBridge.WorldVisuals.RefreshAtmosphere() local world = RivalsRuntimeBridge.WorldVisuals if not world.ReadToggle('P1S13T1', false) then world.RestoreAtmosphere() return end local desired = world.ResolveAtmosphereValues() local nativeAtmospheres = {} for _, child in ipairs(world.Lighting:GetChildren()) do if child:IsA('Atmosphere') and child ~= world.OwnedAtmosphere then nativeAtmospheres[#nativeAtmospheres + 1] = child end end if #nativeAtmospheres > 0 then local owned = world.OwnedAtmosphere world.OwnedAtmosphere = nil if owned then pcall(function() owned:Destroy() end) end for _, atmosphere in ipairs(nativeAtmospheres) do world.GuardNativeAtmosphere(atmosphere, desired) end return end local atmosphere = world.OwnedAtmosphere if not atmosphere or atmosphere.Parent == nil then atmosphere = Instance.new('Atmosphere') atmosphere.Name = 'ZkxHubWorldVisuals_Atmosphere' world.OwnedAtmosphere = atmosphere atmosphere.Parent = world.Lighting end for property, value in pairs(desired) do atmosphere[property] = value end end function RivalsRuntimeBridge.WorldVisuals.OnLightingDescendantAdded(descendant) local world = RivalsRuntimeBridge.WorldVisuals local className = descendant.ClassName local definition = world.PostEffects[className] if definition and world.ReadToggle(definition.Toggle, false) then world.GuardNativeEffect(className, descendant) elseif descendant:IsA('Sky') and world.ReadToggle('P1S12T1', false) then world.RefreshSky() elseif descendant:IsA('Atmosphere') and world.ReadToggle('P1S13T1', false) then world.RefreshAtmosphere() end end function RivalsRuntimeBridge.WorldVisuals.OnLightingDescendantRemoving(descendant) local world = RivalsRuntimeBridge.WorldVisuals local shouldRefresh = false local effectGuards = world.NativeEffectGuards[descendant.ClassName] local effectGuard = effectGuards and effectGuards[descendant] if effectGuard then world.DestroyGuard(effectGuard) effectGuards[descendant] = nil end local skyGuard = world.NativeSkyGuards[descendant] if skyGuard then world.DestroyGuard(skyGuard) world.NativeSkyGuards[descendant] = nil shouldRefresh = true end local atmosphereGuard = world.NativeAtmosphereGuards[descendant] if atmosphereGuard then world.DestroyGuard(atmosphereGuard) world.NativeAtmosphereGuards[descendant] = nil shouldRefresh = true end for className, owned in pairs(world.OwnedEffects) do if descendant == owned then world.OwnedEffects[className] = nil shouldRefresh = true end end if descendant == world.OwnedSky then world.OwnedSky = nil shouldRefresh = true elseif descendant == world.OwnedAtmosphere then world.OwnedAtmosphere = nil shouldRefresh = true end if shouldRefresh and world.Initialized then task.defer(function() if world.Initialized then world.RefreshAll() end end) end end function RivalsRuntimeBridge.WorldVisuals.EnsureInitialized() local world = RivalsRuntimeBridge.WorldVisuals if world.Initialized then return end world.Initialized = true world.LoadCustomSkyboxes() world.LightingGuard = world.NewGuard(world.Lighting) world.Connections.DescendantAdded = world.Lighting.DescendantAdded:Connect(world.OnLightingDescendantAdded) world.Connections.DescendantRemoving = world.Lighting.DescendantRemoving:Connect(world.OnLightingDescendantRemoving) local preRenderSignal = RunService.PreRender or RunService.RenderStepped world.Connections.PreRender = preRenderSignal:Connect(world.UpdateFrame) end function RivalsRuntimeBridge.WorldVisuals.RefreshAll() local world = RivalsRuntimeBridge.WorldVisuals world.EnsureInitialized() world.RefreshLighting() for className in pairs(world.PostEffects) do world.RefreshPostEffect(className) end world.RefreshAmbience() world.RefreshWeather() world.RefreshStretchedResolution() world.RefreshSky() world.RefreshAtmosphere() end function RivalsRuntimeBridge.WorldVisuals.Destroy() local world = RivalsRuntimeBridge.WorldVisuals if not world.Initialized then return end world.Initialized = false for _, connection in pairs(world.Connections) do connection:Disconnect() end world.Connections = {} world.DestroyGuard(world.LightingGuard) world.LightingGuard = nil for className in pairs(world.PostEffects) do world.RestorePostEffect(className) end world.RestoreMotionBlur() local ambience = world.OwnedAmbience world.OwnedAmbience = nil world.ActiveAmbienceSource = nil if ambience then pcall(function() ambience:Destroy() end) end world.DestroyWeather() world.RestoreStretchedResolution() world.RestoreSky() world.RestoreAtmosphere() end -- ===== camera tools :: camera tilt + freecam ===== RivalsRuntimeBridge.CameraTilt = { Roll = 0, Bound = false, Camera = nil, LastBase = nil, LastResult = nil, } function RivalsRuntimeBridge.CameraTilt.Restore() local tilt = RivalsRuntimeBridge.CameraTilt local camera = tilt.Camera if camera and camera.Parent and tilt.LastBase and tilt.LastResult and camera.CFrame == tilt.LastResult then camera.CFrame = tilt.LastBase end tilt.LastBase = nil tilt.LastResult = nil end function RivalsRuntimeBridge.CameraTilt.Step(deltaTime) local tilt = RivalsRuntimeBridge.CameraTilt local world = RivalsRuntimeBridge.WorldVisuals local camera = Workspace.CurrentCamera local dt = math.clamp(tonumber(deltaTime) or 0, 0, 0.1) if not camera or dt <= 0 then return end if tilt.Camera ~= camera then tilt.Restore() tilt.Camera = camera end if not world.ReadToggle('PX17T1', false) or RivalsRuntimeBridge.Freecam.Active then if tilt.Roll ~= 0 or tilt.LastResult then tilt.Roll = 0 tilt.Restore() end return end -- A rolls the camera one way and D the other; the roll eases back to level while the menu or a text box has the keys local target = 0 if not Library.Toggled and not UserInputService:GetFocusedTextBox() then local left = UserInputService:IsKeyDown(Enum.KeyCode.A) local right = UserInputService:IsKeyDown(Enum.KeyCode.D) local angle = math.clamp(tonumber(world.ReadOption('PX17S1', 5)) or 5, 0, 20) if world.ReadToggle('PX17T2', false) then angle = -angle end if left and not right then target = angle elseif right and not left then target = -angle end end local smoothness = math.clamp(tonumber(world.ReadOption('PX17S2', 10)) or 10, 1, 60) tilt.Roll += (target - tilt.Roll) * (1 - math.exp(-smoothness * dt)) if target == 0 and math.abs(tilt.Roll) < 0.005 then tilt.Roll = 0 tilt.Restore() return end -- the roll is applied on top of whatever the game and Stretched Resolution wrote this frame (shake and stretch survive), -- and a camera nobody rewrote since our last write is rebuilt from its own base so the roll never compounds local base = camera.CFrame if tilt.LastResult and base == tilt.LastResult then base = tilt.LastBase end local result = base * CFrame.Angles(0, 0, math.rad(tilt.Roll)) tilt.LastBase = base tilt.LastResult = result camera.CFrame = result end function RivalsRuntimeBridge.CameraTilt.Start() local tilt = RivalsRuntimeBridge.CameraTilt if tilt.Bound then return end tilt.Bound = true RunService:BindToRenderStep( 'ZkxHubCameraTilt', Enum.RenderPriority.Camera.Value + 2, GuardRivalsCallback('CameraTilt_Step', tilt.Step) ) end function RivalsRuntimeBridge.CameraTilt.Destroy() local tilt = RivalsRuntimeBridge.CameraTilt if tilt.Bound then tilt.Bound = false pcall(function() RunService:UnbindFromRenderStep('ZkxHubCameraTilt') end) end tilt.Restore() tilt.Roll = 0 tilt.Camera = nil end RivalsRuntimeBridge.Freecam = { Active = false, Camera = nil, Position = Vector3.zero, Yaw = 0, Pitch = 0, TargetYaw = 0, TargetPitch = 0, Velocity = Vector3.zero, SavedCameraType = nil, SavedFov = nil, SavedMouseBehavior = nil, SavedRotation = nil, } function RivalsRuntimeBridge.Freecam.ReadNumber(optionId, fallback, minimum, maximum) local value = tonumber(RivalsRuntimeBridge.WorldVisuals.ReadOption(optionId, fallback)) or fallback return math.clamp(value, minimum, maximum) end -- keeps WASD, Space, Shift, Ctrl and the mouse buttons away from the game while the camera is detached function RivalsRuntimeBridge.Freecam.Sink() if not RivalsRuntimeBridge.Freecam.Active or Library.Toggled or UserInputService:GetFocusedTextBox() then return Enum.ContextActionResult.Pass end return Enum.ContextActionResult.Sink end -- the game's own camera keeps reading the mouse, so its rotation is pinned to where you were looking function RivalsRuntimeBridge.Freecam.Hold() local freecam = RivalsRuntimeBridge.Freecam local rotation = freecam.SavedRotation if not freecam.Active or not rotation then return end local controller = RivalsModsState.ResolveCameraController() if controller and type(controller.SetRotation) == 'function' then controller:SetRotation(rotation) end end function RivalsRuntimeBridge.Freecam.Step(deltaTime) local freecam = RivalsRuntimeBridge.Freecam local camera = Workspace.CurrentCamera if not freecam.Active or not camera then return end freecam.Camera = camera local dt = math.clamp(tonumber(deltaTime) or 0, 0, 0.1) local world = RivalsRuntimeBridge.WorldVisuals local menuOpen = Library.Toggled == true local typing = UserInputService:GetFocusedTextBox() ~= nil local smoothing = freecam.ReadNumber('PX18S4', 14, 0, 60) local alpha = smoothing <= 0 and 1 or 1 - math.exp(-smoothing * dt) if not menuOpen then if UserInputService.MouseBehavior ~= Enum.MouseBehavior.LockCenter then UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter end local delta = UserInputService:GetMouseDelta() local sensitivity = freecam.ReadNumber('PX18S3', 1, 0.1, 5) * 0.0042 freecam.TargetYaw = freecam.TargetYaw - delta.X * sensitivity freecam.TargetPitch = math.clamp(freecam.TargetPitch - delta.Y * sensitivity, -1.55, 1.55) end freecam.Yaw += (freecam.TargetYaw - freecam.Yaw) * alpha freecam.Pitch += (freecam.TargetPitch - freecam.Pitch) * alpha local rotation = CFrame.Angles(0, freecam.Yaw, 0) * CFrame.Angles(freecam.Pitch, 0, 0) local direction = Vector3.zero if not menuOpen and not typing then local forward = (UserInputService:IsKeyDown(Enum.KeyCode.W) and 1 or 0) - (UserInputService:IsKeyDown(Enum.KeyCode.S) and 1 or 0) local strafe = (UserInputService:IsKeyDown(Enum.KeyCode.D) and 1 or 0) - (UserInputService:IsKeyDown(Enum.KeyCode.A) and 1 or 0) local rise = 0 if UserInputService:IsKeyDown(Enum.KeyCode.Space) or UserInputService:IsKeyDown(Enum.KeyCode.E) then rise += 1 end if UserInputService:IsKeyDown(Enum.KeyCode.LeftControl) or UserInputService:IsKeyDown(Enum.KeyCode.Q) then rise -= 1 end direction = rotation.LookVector * forward + rotation.RightVector * strafe + Vector3.new(0, rise, 0) if direction.Magnitude > 1 then direction = direction.Unit end end local speed = freecam.ReadNumber('PX18S1', 60, 1, 1000) if not menuOpen and not typing and UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) then speed = speed * freecam.ReadNumber('PX18S2', 3, 1, 20) end freecam.Velocity = freecam.Velocity:Lerp(direction * speed, alpha) freecam.Position = freecam.Position + freecam.Velocity * dt local frame = CFrame.new(freecam.Position) * rotation if world.ReadToggle('P1S30T1', false) then local xRatio = math.clamp(tonumber(world.ReadOption('P1S30S1', 1)) or 1, 0.01, 1) local yRatio = math.clamp(tonumber(world.ReadOption('P1S30S2', 1)) or 1, 0.01, 1) frame = frame * CFrame.new(0, 0, 0, xRatio, 0, 0, 0, yRatio, 0, 0, 0, 1) end if camera.CameraType ~= Enum.CameraType.Scriptable then camera.CameraType = Enum.CameraType.Scriptable end camera.CFrame = frame camera.Focus = frame local zoom = freecam.ReadNumber('PX18S5', 1, 0.5, 8) camera.FieldOfView = math.clamp(math.deg(2 * math.atan(math.tan(math.rad(freecam.SavedFov or 70) / 2) / zoom)), 1, 120) end function RivalsRuntimeBridge.Freecam.Enable() local freecam = RivalsRuntimeBridge.Freecam if freecam.Active then return true end local camera = Workspace.CurrentCamera if not camera then return false end local origin = camera.CFrame local look = origin.LookVector freecam.Camera = camera freecam.Position = origin.Position freecam.Pitch = math.asin(math.clamp(look.Y, -1, 1)) freecam.Yaw = math.atan2(-look.X, -look.Z) freecam.TargetPitch = freecam.Pitch freecam.TargetYaw = freecam.Yaw freecam.Velocity = Vector3.zero freecam.SavedCameraType = camera.CameraType freecam.SavedFov = camera.FieldOfView freecam.SavedMouseBehavior = UserInputService.MouseBehavior local controller = RivalsModsState.ResolveCameraController() local rotation = controller and rawget(controller, 'Rotation') freecam.SavedRotation = typeof(rotation) == 'Vector2' and rotation or nil freecam.Active = true AimbotInputState.FreecamActive = true RivalsRuntimeBridge.WorldVisuals.MotionBlurLastCFrame = nil local ContextActionService = game:GetService('ContextActionService') ContextActionService:BindActionAtPriority( 'ZkxHubFreecamSink', GuardRivalsCallback('Freecam_Sink', freecam.Sink), false, Enum.ContextActionPriority.High.Value + 100, Enum.KeyCode.W, Enum.KeyCode.A, Enum.KeyCode.S, Enum.KeyCode.D, Enum.KeyCode.Space, Enum.KeyCode.E, Enum.KeyCode.Q, Enum.KeyCode.LeftShift, Enum.KeyCode.LeftControl, Enum.UserInputType.MouseButton1, Enum.UserInputType.MouseButton2, Enum.UserInputType.MouseMovement ) RunService:BindToRenderStep( 'ZkxHubFreecamHold', Enum.RenderPriority.First.Value, GuardRivalsCallback('Freecam_Hold', freecam.Hold) ) RunService:BindToRenderStep( 'ZkxHubFreecam', Enum.RenderPriority.Camera.Value + 3, GuardRivalsCallback('Freecam_Step', freecam.Step) ) return true end function RivalsRuntimeBridge.Freecam.Disable() local freecam = RivalsRuntimeBridge.Freecam if not freecam.Active then return end freecam.Active = false AimbotInputState.FreecamActive = false pcall(function() RunService:UnbindFromRenderStep('ZkxHubFreecam') end) pcall(function() RunService:UnbindFromRenderStep('ZkxHubFreecamHold') end) pcall(function() game:GetService('ContextActionService'):UnbindAction('ZkxHubFreecamSink') end) local camera = freecam.Camera if camera and camera.Parent then camera.CameraType = freecam.SavedCameraType or Enum.CameraType.Custom if freecam.SavedFov then camera.FieldOfView = freecam.SavedFov end end if freecam.SavedMouseBehavior then UserInputService.MouseBehavior = freecam.SavedMouseBehavior end RivalsRuntimeBridge.WorldVisuals.MotionBlurLastCFrame = nil freecam.Camera = nil freecam.SavedRotation = nil freecam.Velocity = Vector3.zero end function RivalsRuntimeBridge.Freecam.Set(enabled) local freecam = RivalsRuntimeBridge.Freecam if enabled then if not freecam.Enable() and Toggles.PX18T1 then Toggles.PX18T1:SetValue(false) end else freecam.Disable() end end -- ===== executor report :: what this executor gives the camera and viewmodel features ===== RivalsRuntimeBridge.Diagnostics = { Running = false } function RivalsRuntimeBridge.Diagnostics.Build() local native = RivalsRuntimeBridge.NativeRemovals local lines = {} local function add(text) lines[#lines + 1] = text end local function section(title, body) add('') add('[' .. title .. ']') local ok, err = pcall(body) if not ok then add(' error: ' .. tostring(err)) end end section('executor', function() local name, version = 'unknown', '' if type(identifyexecutor) == 'function' then local ok, first, second = pcall(identifyexecutor) if ok then name = tostring(first) version = second ~= nil and tostring(second) or '' end end add(' ' .. name .. ' ' .. version) local caps = __ZkhHub_genv.ZkxHubCaps local missing = {} for _, fnName in ipairs({ 'getgc', 'hookfunction', 'getthreadidentity', 'setthreadidentity', 'getrawmetatable', 'getloadedmodules', 'getsenv', 'cloneref', 'debug.getupvalue', 'debug.getupvalues', 'debug.setupvalue', 'debug.getconstants', 'debug.setconstant', 'writefile', 'setclipboard', 'queue_on_teleport', }) do if not caps.has(fnName) then missing[#missing + 1] = fnName end end add(' missing or stubbed: ' .. (#missing > 0 and table.concat(missing, ', ') or 'none')) local identity = 'n/a' if not caps.stubbed.getthreadidentity and type(getthreadidentity) == 'function' then identity = tostring(getthreadidentity()) end add(' thread identity: ' .. identity) end) section('startup', function() add(' replicated state ready: ' .. tostring(ReplicatedStateReady)) add(' boot issue: ' .. tostring(RivalsRuntimeBridge.BootIssue)) local any = false for label, issue in pairs(RivalsRuntimeBridge.ModuleIssues) do any = true add(' require failed: ' .. label .. ' -> ' .. tostring(issue.err)) end if not any then add(' require failures: none') end end) section('esp weapon info', function() add(' signal tracking connected: ' .. tostring(WeaponInfoTrackingConnected)) local observers, withItem = 0, 0 for player, observer in pairs(WeaponInfoConnections) do observers += 1 if observer.Item then withItem += 1 end if observers <= 6 then local itemName = observer.Item and observer.Item.Name or nil add(string.format(' %s: item %s, ammo %s, reserve %s', tostring(player.Name), tostring(itemName), tostring(observer.Ammo), tostring(observer.AmmoReserve))) end end add(' observers: ' .. observers .. ', with an item: ' .. withItem) end) section('camera controller', function() local controller = RivalsModsState.ResolveCameraController() add(' resolved: ' .. tostring(controller ~= nil)) if not controller then return end local first = rawget(controller, '_last_dt') for _ = 1, 4 do RunService.RenderStepped:Wait() end local second = rawget(controller, '_last_dt') add( ' game camera update running: ' .. tostring(type(second) == 'number' and second > 0) .. ' (_last_dt ' .. tostring(first) .. ' -> ' .. tostring(second) .. ')' ) local cameraState = rawget(controller, 'CameraState') local publicState = nil if type(cameraState) == 'table' and type(cameraState.GetPublicState) == 'function' then local _, value = pcall(cameraState.GetPublicState, cameraState) publicState = value end add(' view state: ' .. tostring(publicState)) add(' third person override: ' .. tostring(rawget(controller, '_third_person_override'))) local offsets = rawget(controller, '_external_fov_offsets') add(' hub FOV offset: ' .. tostring(type(offsets) == 'table' and offsets.ZkxHub)) add( ' base fov: ' .. tostring(rawget(controller, '_base_fov')) .. ', camera fov: ' .. tostring(Workspace.CurrentCamera and Workspace.CurrentCamera.FieldOfView) ) end) section('viewmodel', function() local fighter = ResolveLocalFighter() add(' local fighter: ' .. tostring(fighter ~= nil)) if not fighter then return end local count = 0 local viewModel = nil for _, item in pairs(type(fighter.Items) == 'table' and fighter.Items or {}) do count += 1 if not viewModel and type(item) == 'table' and type(rawget(item, 'ViewModel')) == 'table' then viewModel = rawget(item, 'ViewModel') end end add(' items: ' .. count .. ', with a viewmodel: ' .. tostring(viewModel ~= nil)) if not viewModel then add(' (equip a weapon and run the report again for the viewmodel checks)') return end local found, total = 0, 0 for springName in pairs(RivalsModsState.MotionSpringValues) do total += 1 if type(rawget(viewModel, springName)) == 'table' then found += 1 end end add(' motion springs found: ' .. found .. '/' .. total) local animator = rawget(viewModel, 'Animator') local requiredAnimator = native.RequireModule('ViewModelAnimator') add( ' animator class from require is the live one: ' .. tostring( requiredAnimator ~= nil and type(animator) == 'table' and rawequal(requiredAnimator, getmetatable(animator)) ) ) local requiredViewModel = native.RequireModule('ClientViewModel') add( ' viewmodel class from require is the live one: ' .. tostring(requiredViewModel ~= nil and rawequal(requiredViewModel, getmetatable(viewModel))) ) add(' sprint animation key: ' .. tostring(type(animator) == 'table' and rawget(animator, '_sprint_animation'))) end) section('hooks and toggles', function() add(' animation hook installed: ' .. tostring(native.Animation.Restore ~= nil)) add(' viewmodel offset hook installed: ' .. tostring(native.ViewmodelOffset.Restore ~= nil)) for _, entry in ipairs({ { 'P4S2T6', 'No Camera Shake' }, { 'P4S2T8', 'Third Person' }, { 'PX11T1', 'Third Person Offset' }, { 'P4S2T9', 'Custom Camera FOV' }, { 'P4S2T10', 'Viewmodel Offset' }, { 'P1S31T1', 'No Motion' }, { 'P1S31T2', 'No Shoot Animation' }, { 'P1S31T3', 'No Sprint Animation' }, { 'P1S31T4', 'No Equip Animation' }, { 'P1S31T5', 'No Reload Animation' }, }) do add(' ' .. entry[2] .. ': ' .. tostring(Toggles[entry[1]] and Toggles[entry[1]].Value)) end end) return table.concat(lines, '\n') end function RivalsRuntimeBridge.Diagnostics.Run() local diag = RivalsRuntimeBridge.Diagnostics if diag.Running then return end diag.Running = true task.spawn(GuardRivalsCallback('Diagnostics_Run', function() local ok, report = pcall(diag.Build) diag.Running = false if not ok then report = 'report failed: ' .. tostring(report) end local path = 'ZkxHub/_executor_report.txt' local saved = false pcall(function() if type(isfolder) == 'function' and type(makefolder) == 'function' and not isfolder('ZkxHub') then makefolder('ZkxHub') end writefile(path, report) saved = true end) local copied = false pcall(function() if __ZkhHub_genv.ZkxHubCaps.has('setclipboard') then setclipboard(report) copied = true end end) print('[ZkxHub] executor report\n' .. report) Library:Notify({ Title = 'Executor Report', Description = (copied and 'Copied to the clipboard' or 'Could not copy to the clipboard') .. (saved and (' and saved to workspace/' .. path) or ' and could not save a file') .. '.', Time = 8, }) end)) end -- ====================================================================== -- Viewmodel visuals: chams, highlight, wireframe -- ====================================================================== RivalsRuntimeBridge.ViewmodelVisuals = { AppearanceSnapshots = setmetatable({}, { __mode = 'k' }), TextureSnapshots = {}, Highlights = {}, Crosshair = nil, LastReconcileAt = 0, FollowPosition = nil, MaterialMap = { Ghost = Enum.Material.ForceField, Flat = Enum.Material.Neon, Foil = Enum.Material.Foil, Custom = Enum.Material.SmoothPlastic, Reflective = Enum.Material.Glass, }, MaterialNames = { 'Original', 'Ghost', 'Flat', 'Foil', 'Custom', 'Reflective' }, FontMap = { Inconsolata = Enum.Font.Code, Gotham = Enum.Font.Gotham, ['Source Sans'] = Enum.Font.SourceSans, ['Roboto Mono'] = Enum.Font.RobotoMono, }, } function RivalsRuntimeBridge.ViewmodelVisuals.ReadToggle(optionId) return Toggles and Toggles[optionId] and Toggles[optionId].Value == true end function RivalsRuntimeBridge.ViewmodelVisuals.ReadOption(optionId, fallback) local option = Options and Options[optionId] if option == nil or option.Value == nil then return fallback end return option.Value end function RivalsRuntimeBridge.ViewmodelVisuals.AppendUniqueRoot(roots, seen, root) if typeof(root) ~= 'Instance' or root.Parent == nil or seen[root] then return end seen[root] = true roots[#roots + 1] = root end function RivalsRuntimeBridge.ViewmodelVisuals.ResolveScopes() local visuals = RivalsRuntimeBridge.ViewmodelVisuals local scopes = { Character = {}, Arms = {}, Item = {} } local seen = { Character = {}, Arms = {}, Item = {} } visuals.AppendUniqueRoot(scopes.Character, seen.Character, LP.Character) local fighter = ResolveLocalFighter() local equippedItem = fighter and fighter.EquippedItem or nil local clientViewModel = type(equippedItem) == 'table' and (rawget(equippedItem, '_ViewModel') or rawget(equippedItem, 'ViewModel')) or nil if type(clientViewModel) == 'table' then visuals.AppendUniqueRoot(scopes.Item, seen.Item, rawget(clientViewModel, 'ItemModel')) visuals.AppendUniqueRoot(scopes.Arms, seen.Arms, rawget(clientViewModel, '_left_arm')) visuals.AppendUniqueRoot(scopes.Arms, seen.Arms, rawget(clientViewModel, '_right_arm')) end local viewModels = Workspace:FindFirstChild('ViewModels') local firstPerson = viewModels and viewModels:FindFirstChild('FirstPerson') or nil for _, model in ipairs(firstPerson and firstPerson:GetChildren() or {}) do if model:IsA('Model') then visuals.AppendUniqueRoot(scopes.Item, seen.Item, model:FindFirstChild('ItemVisual')) visuals.AppendUniqueRoot(scopes.Arms, seen.Arms, model:FindFirstChild('LeftArm')) visuals.AppendUniqueRoot(scopes.Arms, seen.Arms, model:FindFirstChild('RightArm')) end end return scopes end function RivalsRuntimeBridge.ViewmodelVisuals.CaptureAppearance(part) local visuals = RivalsRuntimeBridge.ViewmodelVisuals local snapshot = visuals.AppearanceSnapshots[part] if snapshot then return snapshot end snapshot = { Material = part.Material, Color = part.Color, Transparency = part.Transparency, Reflectance = part.Reflectance, } visuals.AppearanceSnapshots[part] = snapshot return snapshot end function RivalsRuntimeBridge.ViewmodelVisuals.RestoreAppearance(part, snapshot) if not part or part.Parent == nil or type(snapshot) ~= 'table' then return end pcall(function() part.Material = snapshot.Material part.Color = snapshot.Color part.Transparency = snapshot.Transparency part.Reflectance = snapshot.Reflectance end) end function RivalsRuntimeBridge.ViewmodelVisuals.CaptureTexture(instance, property, root) local visuals = RivalsRuntimeBridge.ViewmodelVisuals local snapshot = visuals.TextureSnapshots[instance] if snapshot then return snapshot end snapshot = { Property = property, Root = root } if property == 'Parent' then snapshot.Value = instance.Parent else snapshot.Value = instance[property] end visuals.TextureSnapshots[instance] = snapshot return snapshot end function RivalsRuntimeBridge.ViewmodelVisuals.StripTextures(root, activeTextures) local visuals = RivalsRuntimeBridge.ViewmodelVisuals for instance, snapshot in pairs(visuals.TextureSnapshots) do if snapshot.Root == root and snapshot.Property == 'Parent' and instance.Parent == nil then activeTextures[instance] = true end end local descendants = root:GetDescendants() if root:IsA('BasePart') then table.insert(descendants, root) end for _, descendant in ipairs(descendants) do local property = nil local replacement = nil if descendant:IsA('MeshPart') then property = 'TextureID' replacement = '' elseif descendant:IsA('SpecialMesh') then property = 'TextureId' replacement = '' elseif descendant:IsA('Decal') or descendant:IsA('Texture') then property = 'Transparency' replacement = 1 elseif descendant:IsA('SurfaceAppearance') or descendant:IsA('Clothing') or descendant:IsA('ShirtGraphic') then property = 'Parent' end if property then visuals.CaptureTexture(descendant, property, root) activeTextures[descendant] = true pcall(function() if property == 'Parent' then descendant.Parent = nil else descendant[property] = replacement end end) end end end function RivalsRuntimeBridge.ViewmodelVisuals.RestoreTexture(instance, snapshot) if not instance or type(snapshot) ~= 'table' then return end pcall(function() instance[snapshot.Property] = snapshot.Value end) end function RivalsRuntimeBridge.ViewmodelVisuals.ApplyChamScope(roots, optionPrefix, activeParts, activeTextures) local visuals = RivalsRuntimeBridge.ViewmodelVisuals if not visuals.ReadToggle(optionPrefix .. 'T1') then return end local materialName = visuals.ReadOption(optionPrefix .. 'D1', 'Ghost') local material = visuals.MaterialMap[materialName] local color = visuals.ReadOption(optionPrefix .. 'C1', Color3.new(1, 1, 1)) local transparency = math.clamp(visuals.ReadOption(optionPrefix .. 'S1', 0), 0, 1) local stripTextures = visuals.ReadToggle(optionPrefix .. 'T2') for _, root in ipairs(roots) do local descendants = root:GetDescendants() if root:IsA('BasePart') then table.insert(descendants, root) end for _, descendant in ipairs(descendants) do if descendant:IsA('BasePart') then local snapshot = visuals.CaptureAppearance(descendant) activeParts[descendant] = true pcall(function() descendant.Material = material or snapshot.Material descendant.Color = color descendant.Transparency = snapshot.Transparency < 1 and transparency or snapshot.Transparency end) end end if stripTextures then visuals.StripTextures(root, activeTextures) end end end function RivalsRuntimeBridge.ViewmodelVisuals.ReconcileHighlights(scopeName, roots, optionPrefix) local visuals = RivalsRuntimeBridge.ViewmodelVisuals local entries = visuals.Highlights[scopeName] or {} visuals.Highlights[scopeName] = entries local active = {} if visuals.ReadToggle(optionPrefix .. 'T1') then local fillColor = visuals.ReadOption(optionPrefix .. 'C1', Color3.new(1, 1, 1)) local fillTransparency = math.clamp(visuals.ReadOption(optionPrefix .. 'S1', 0.5), 0, 1) local outlineColor = visuals.ReadOption(optionPrefix .. 'C2', Color3.new(1, 1, 1)) local outlineTransparency = math.clamp(visuals.ReadOption(optionPrefix .. 'S2', 0), 0, 1) for _, root in ipairs(roots) do active[root] = true local highlight = entries[root] if not highlight or highlight.Parent == nil then highlight = Instance.new('Highlight') highlight.Name = 'ZkxHubViewmodelHighlight_' .. scopeName highlight.DepthMode = Enum.HighlightDepthMode.AlwaysOnTop highlight.Adornee = root highlight.Parent = root entries[root] = highlight end highlight.FillColor = fillColor highlight.FillTransparency = fillTransparency highlight.OutlineColor = outlineColor highlight.OutlineTransparency = outlineTransparency end end for root, highlight in pairs(entries) do if not active[root] then pcall(function() highlight:Destroy() end) entries[root] = nil end end end -- Roblox cannot draw real mesh edges, so the wireframe is a thin outline box around every visible part. -- Boxes live in the shared Highlights table so Destroy() cleans them up with everything else. function RivalsRuntimeBridge.ViewmodelVisuals.ReconcileWireframes(scopeName, roots, toggleId) local visuals = RivalsRuntimeBridge.ViewmodelVisuals local key = 'Wireframe' .. scopeName local entries = visuals.Highlights[key] or {} visuals.Highlights[key] = entries local active = {} if visuals.ReadToggle(toggleId) then local color = visuals.ReadOption('PX12C1', Color3.new(1, 1, 1)) local thickness = math.clamp(tonumber(visuals.ReadOption('PX12S1', 0.02)) or 0.02, 0.005, 0.2) local transparency = math.clamp(tonumber(visuals.ReadOption('PX12S2', 0)) or 0, 0, 0.95) for _, root in ipairs(roots) do local parts = root:GetDescendants() parts[#parts + 1] = root for _, part in ipairs(parts) do if part:IsA('BasePart') and part.Transparency < 1 then active[part] = true local box = entries[part] if not box or box.Parent == nil then box = Instance.new('SelectionBox') box.Name = 'ZkxHubViewmodelWireframe' box.SurfaceTransparency = 1 box.Adornee = part box.Parent = part entries[part] = box end box.Color3 = color box.LineThickness = thickness box.Transparency = transparency end end end end for part, box in pairs(entries) do if not active[part] then pcall(function() box:Destroy() end) entries[part] = nil end end end function RivalsRuntimeBridge.ViewmodelVisuals.ReconcileModels() local visuals = RivalsRuntimeBridge.ViewmodelVisuals local scopes = visuals.ResolveScopes() local activeParts = {} local activeTextures = {} visuals.ApplyChamScope(scopes.Character, 'P1S19', activeParts, activeTextures) visuals.ApplyChamScope(scopes.Arms, 'P1S20', activeParts, activeTextures) visuals.ApplyChamScope(scopes.Item, 'P1S21', activeParts, activeTextures) for part, snapshot in pairs(visuals.AppearanceSnapshots) do if not activeParts[part] then visuals.RestoreAppearance(part, snapshot) visuals.AppearanceSnapshots[part] = nil end end for instance, snapshot in pairs(visuals.TextureSnapshots) do if not activeTextures[instance] then visuals.RestoreTexture(instance, snapshot) visuals.TextureSnapshots[instance] = nil end end visuals.ReconcileHighlights('Arms', scopes.Arms, 'P1S22') visuals.ReconcileHighlights('Item', scopes.Item, 'P1S23') visuals.ReconcileWireframes('Arms', scopes.Arms, 'PX12T1') visuals.ReconcileWireframes('Item', scopes.Item, 'PX12T2') end function RivalsRuntimeBridge.ViewmodelVisuals.ShimmerColorAt(elapsed, fraction, baseColor, fadeColor) local offset = ((1 - math.abs((elapsed * 0.5) % 2 - 1)) * 2 - 1) * 0.5 local alpha = math.clamp(fraction - offset, 0, 1) if alpha <= 0.5 then return fadeColor:Lerp(baseColor, alpha * 2) end return baseColor:Lerp(fadeColor, (alpha - 0.5) * 2) end function RivalsRuntimeBridge.ViewmodelVisuals.PerimeterColorAt(elapsed, fraction, firstColor, secondColor) local alpha = ((elapsed * 0.5) + fraction) % 1 return firstColor:Lerp(secondColor, alpha) end function RivalsRuntimeBridge.ViewmodelVisuals.PingPongColorAt(elapsed, fraction, backgroundColor, mainColor, rotation) local wave = math.sin(elapsed * math.pi) + 1 local rotationOffset = rotation / 90 local firstBand = (wave + rotationOffset) % 4 local secondBand = (4 - wave + rotationOffset) % 4 local position = (fraction * 4) % 4 local firstDistance = math.abs(position - firstBand) % 4 firstDistance = math.min(firstDistance, 4 - firstDistance) local secondDistance = math.abs(position - secondBand) % 4 secondDistance = math.min(secondDistance, 4 - secondDistance) local influence = math.max(1 - firstDistance / 0.4, 1 - secondDistance / 0.4, 0) return backgroundColor:Lerp(mainColor, math.clamp(influence, 0, 1)) end function RivalsRuntimeBridge.ViewmodelVisuals.ResolveLineColor(baseColor, fraction, elapsed) local visuals = RivalsRuntimeBridge.ViewmodelVisuals if not visuals.ReadToggle('P1S26T10') then return baseColor end local speed = visuals.ReadOption('P1S26S12', 1) local animationTime = elapsed * speed local kind = visuals.ReadOption('P1S26D3', 'Shimmer') if kind == 'Perimeter' then return visuals.PerimeterColorAt( animationTime, fraction, visuals.ReadOption('P1S26C7', Color3.fromRGB(255, 0, 0)), visuals.ReadOption('P1S26C8', Color3.fromRGB(0, 0, 255)) ) elseif kind == 'PingPong' then return visuals.PingPongColorAt( animationTime, fraction, visuals.ReadOption('P1S26C9', Color3.new(0, 0, 0)), visuals.ReadOption('P1S26C10', Color3.new(1, 1, 1)), visuals.ReadOption('P1S26S13', 0) ) end return visuals.ShimmerColorAt(animationTime, fraction, baseColor, visuals.ReadOption('P1S26C6', Color3.fromRGB(40, 40, 40))) end function RivalsRuntimeBridge.ViewmodelVisuals.ResolveTextGradient(elapsed, baseColor) local visuals = RivalsRuntimeBridge.ViewmodelVisuals if not visuals.ReadToggle('P1S26T13') then return ColorSequence.new(baseColor), Vector2.zero, 0 end local speed = visuals.ReadOption('P1S26S17', 1) local animationTime = elapsed * speed if visuals.ReadOption('P1S26D5', 'Shimmer') == 'PingPong' then local colors = {} for index = 0, 8 do local fraction = index / 8 colors[#colors + 1] = ColorSequenceKeypoint.new( fraction, visuals.PingPongColorAt( animationTime, fraction, visuals.ReadOption('P1S26C14', Color3.new(0, 0, 0)), visuals.ReadOption('P1S26C15', Color3.new(1, 1, 1)), visuals.ReadOption('P1S26S18', 0) ) ) end return ColorSequence.new(colors), Vector2.zero, visuals.ReadOption('P1S26S18', 0) end local fadeColor = visuals.ReadOption('P1S26C13', Color3.fromRGB(40, 40, 40)) return ColorSequence.new({ ColorSequenceKeypoint.new(0, fadeColor), ColorSequenceKeypoint.new(0.5, baseColor), ColorSequenceKeypoint.new(1, fadeColor), }), Vector2.new(((1 - math.abs((animationTime * 0.5) % 2 - 1)) * 2 - 1) * 0.5, 0), 45 end function RivalsRuntimeBridge.ViewmodelVisuals.CreateCrosshairArm(parent, name) local outline = Instance.new('Frame') outline.Name = name .. 'Outline' outline.AnchorPoint = Vector2.new(0.5, 0.5) outline.BorderSizePixel = 0 outline.Parent = parent local fill = Instance.new('Frame') fill.Name = name fill.AnchorPoint = Vector2.new(0.5, 0.5) fill.BorderSizePixel = 0 fill.Parent = parent return { Outline = outline, Fill = fill } end function RivalsRuntimeBridge.ViewmodelVisuals.EnsureCrosshair() local visuals = RivalsRuntimeBridge.ViewmodelVisuals if visuals.Crosshair and visuals.Crosshair.Gui and visuals.Crosshair.Gui.Parent then return visuals.Crosshair end local gui = Instance.new('ScreenGui') gui.Name = 'ZkxHubCustomCrosshair' gui.IgnoreGuiInset = true gui.ResetOnSpawn = false gui.DisplayOrder = 10000 gui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling gui.Parent = LP:WaitForChild('PlayerGui') local container = Instance.new('Frame') container.Name = 'Container' container.AnchorPoint = Vector2.new(0.5, 0.5) container.Size = UDim2.fromOffset(0, 0) container.BackgroundTransparency = 1 container.Parent = gui local image = Instance.new('ImageLabel') image.Name = 'Image' image.AnchorPoint = Vector2.new(0.5, 0.5) image.Position = UDim2.fromOffset(0, 0) image.BackgroundTransparency = 1 image.BorderSizePixel = 0 image.Visible = false image.Parent = container local textLabel = Instance.new('TextLabel') textLabel.Name = 'Text' textLabel.AnchorPoint = Vector2.new(0.5, 0) textLabel.AutomaticSize = Enum.AutomaticSize.XY textLabel.BackgroundTransparency = 1 textLabel.BorderSizePixel = 0 textLabel.Text = '' textLabel.TextColor3 = Color3.new(1, 1, 1) textLabel.Visible = false textLabel.Parent = gui local textGradient = Instance.new('UIGradient') textGradient.Name = 'ColorGradient' textGradient.Parent = textLabel local textStroke = Instance.new('UIStroke') textStroke.Name = 'Outline' textStroke.Enabled = false textStroke.Parent = textLabel visuals.Crosshair = { Gui = gui, Container = container, Image = image, Text = textLabel, TextGradient = textGradient, TextStroke = textStroke, Top = visuals.CreateCrosshairArm(container, 'Top'), Bottom = visuals.CreateCrosshairArm(container, 'Bottom'), Left = visuals.CreateCrosshairArm(container, 'Left'), Right = visuals.CreateCrosshairArm(container, 'Right'), } return visuals.Crosshair end function RivalsRuntimeBridge.ViewmodelVisuals.ResolveCrosshairImage(value) value = type(value) == 'string' and value:match('^%s*(.-)%s*$') or '' if value == '' then return '' end if value:match('^%d+$') then return 'rbxassetid://' .. value end if value:match('^rbxasset') or value:match('^https?://') then return value end if isfile and isfile(value) then local customAsset = getcustomasset or getsynasset if type(customAsset) == 'function' then local ok, asset = pcall(customAsset, value) if ok and type(asset) == 'string' then return asset end end end return value end RivalsRuntimeBridge.ViewmodelVisuals.CrosshairPresetManagedToggles = { 'P1S26T2', 'P1S26T3', 'P1S26T4', 'P1S26T5', 'P1S26T6', 'P1S26T7', 'P1S26T8', } RivalsRuntimeBridge.ViewmodelVisuals.CrosshairPresetOrder = { 'ZkxHub Default', 'CS2 Classic Green', 'Valorant Cyan', 'TenZ Cyan', 'Aspas White', 'CoD Modern', 'Fortnite Cyan', 'Overwatch Magenta', 'Rust Dot', 'Red Dot', 'T-Style', 'Krunker Cross', 'Sniper Thin', 'Rainbow Spin', } -- ====================================================================== -- Crosshair presets -- ====================================================================== RivalsRuntimeBridge.ViewmodelVisuals.CrosshairPresets = { ['ZkxHub Default'] = { Toggles = { P1S26T3 = true, P1S26T4 = true, P1S26T5 = true, P1S26T6 = true }, Options = { P1S26D1 = 'Lines', P1S26S1 = 12, P1S26S2 = 2, P1S26S3 = 6, P1S26S11 = 1 }, Colors = { P1S26C1 = { 0, 0, 0 }, P1S26C2 = { 255, 255, 255 }, P1S26C3 = { 255, 255, 255 }, P1S26C4 = { 255, 255, 255 }, P1S26C5 = { 255, 255, 255 }, }, }, ['CS2 Classic Green'] = { Toggles = { P1S26T2 = true, P1S26T3 = true, P1S26T4 = true, P1S26T5 = true, P1S26T6 = true }, Options = { P1S26D1 = 'Lines', P1S26S1 = 6, P1S26S2 = 2, P1S26S3 = 3, P1S26S11 = 1 }, Colors = { P1S26C1 = { 0, 0, 0 }, P1S26C2 = { 0, 255, 0 }, P1S26C3 = { 0, 255, 0 }, P1S26C4 = { 0, 255, 0 }, P1S26C5 = { 0, 255, 0 }, }, }, ['Valorant Cyan'] = { Toggles = { P1S26T2 = true, P1S26T3 = true, P1S26T4 = true, P1S26T5 = true, P1S26T6 = true }, Options = { P1S26D1 = 'Lines', P1S26S1 = 6, P1S26S2 = 2, P1S26S3 = 2, P1S26S11 = 1 }, Colors = { P1S26C1 = { 0, 0, 0 }, P1S26C2 = { 0, 255, 255 }, P1S26C3 = { 0, 255, 255 }, P1S26C4 = { 0, 255, 255 }, P1S26C5 = { 0, 255, 255 }, }, }, ['TenZ Cyan'] = { Toggles = { P1S26T3 = true, P1S26T4 = true, P1S26T5 = true, P1S26T6 = true }, Options = { P1S26D1 = 'Lines', P1S26S1 = 4, P1S26S2 = 1, P1S26S3 = 1, P1S26S11 = 1 }, Colors = { P1S26C1 = { 0, 0, 0 }, P1S26C2 = { 0, 255, 255 }, P1S26C3 = { 0, 255, 255 }, P1S26C4 = { 0, 255, 255 }, P1S26C5 = { 0, 255, 255 }, }, }, ['Aspas White'] = { Toggles = { P1S26T3 = true, P1S26T4 = true, P1S26T5 = true, P1S26T6 = true }, Options = { P1S26D1 = 'Lines', P1S26S1 = 4, P1S26S2 = 2, P1S26S3 = 0, P1S26S11 = 1 }, Colors = { P1S26C1 = { 0, 0, 0 }, P1S26C2 = { 255, 255, 255 }, P1S26C3 = { 255, 255, 255 }, P1S26C4 = { 255, 255, 255 }, P1S26C5 = { 255, 255, 255 }, }, }, ['CoD Modern'] = { Toggles = { P1S26T2 = true, P1S26T3 = true, P1S26T4 = true, P1S26T5 = true, P1S26T6 = true }, Options = { P1S26D1 = 'Lines', P1S26S1 = 8, P1S26S2 = 2, P1S26S3 = 4, P1S26S11 = 2 }, Colors = { P1S26C1 = { 0, 0, 0 }, P1S26C2 = { 255, 255, 255 }, P1S26C3 = { 255, 255, 255 }, P1S26C4 = { 255, 255, 255 }, P1S26C5 = { 255, 255, 255 }, }, }, ['Fortnite Cyan'] = { Toggles = { P1S26T2 = true, P1S26T3 = true, P1S26T4 = true, P1S26T5 = true, P1S26T6 = true }, Options = { P1S26D1 = 'Lines', P1S26S1 = 8, P1S26S2 = 1, P1S26S3 = 5, P1S26S11 = 1 }, Colors = { P1S26C1 = { 0, 0, 0 }, P1S26C2 = { 0, 255, 255 }, P1S26C3 = { 0, 255, 255 }, P1S26C4 = { 0, 255, 255 }, P1S26C5 = { 0, 255, 255 }, }, }, ['Overwatch Magenta'] = { Toggles = { P1S26T2 = true, P1S26T3 = true, P1S26T4 = true, P1S26T5 = true, P1S26T6 = true }, Options = { P1S26D1 = 'Lines', P1S26S1 = 14, P1S26S2 = 3, P1S26S3 = 8, P1S26S11 = 2 }, Colors = { P1S26C1 = { 0, 0, 0 }, P1S26C2 = { 255, 0, 255 }, P1S26C3 = { 255, 0, 255 }, P1S26C4 = { 255, 0, 255 }, P1S26C5 = { 255, 0, 255 }, }, }, ['Rust Dot'] = { Toggles = { P1S26T3 = true, P1S26T4 = true, P1S26T5 = true, P1S26T6 = true }, Options = { P1S26D1 = 'Lines', P1S26S1 = 3, P1S26S2 = 1, P1S26S3 = 1, P1S26S11 = 1 }, Colors = { P1S26C1 = { 0, 0, 0 }, P1S26C2 = { 255, 255, 255 }, P1S26C3 = { 255, 255, 255 }, P1S26C4 = { 255, 255, 255 }, P1S26C5 = { 255, 255, 255 }, }, }, ['Red Dot'] = { Toggles = { P1S26T2 = true, P1S26T3 = true, P1S26T4 = true, P1S26T5 = true, P1S26T6 = true }, Options = { P1S26D1 = 'Lines', P1S26S1 = 1, P1S26S2 = 2, P1S26S3 = 0, P1S26S11 = 1 }, Colors = { P1S26C1 = { 0, 0, 0 }, P1S26C2 = { 255, 60, 60 }, P1S26C3 = { 255, 60, 60 }, P1S26C4 = { 255, 60, 60 }, P1S26C5 = { 255, 60, 60 }, }, }, ['T-Style'] = { Toggles = { P1S26T2 = true, P1S26T4 = true, P1S26T5 = true, P1S26T6 = true }, Options = { P1S26D1 = 'Lines', P1S26S1 = 10, P1S26S2 = 2, P1S26S3 = 5, P1S26S11 = 1 }, Colors = { P1S26C1 = { 0, 0, 0 }, P1S26C2 = { 0, 255, 0 }, P1S26C3 = { 0, 255, 0 }, P1S26C4 = { 0, 255, 0 }, P1S26C5 = { 0, 255, 0 }, }, }, ['Krunker Cross'] = { Toggles = { P1S26T2 = true, P1S26T3 = true, P1S26T4 = true, P1S26T5 = true, P1S26T6 = true }, Options = { P1S26D1 = 'Lines', P1S26S1 = 16, P1S26S2 = 2, P1S26S3 = 5, P1S26S11 = 1 }, Colors = { P1S26C1 = { 0, 0, 0 }, P1S26C2 = { 255, 255, 255 }, P1S26C3 = { 255, 255, 255 }, P1S26C4 = { 255, 255, 255 }, P1S26C5 = { 255, 255, 255 }, }, }, ['Sniper Thin'] = { Toggles = { P1S26T2 = true, P1S26T3 = true, P1S26T4 = true, P1S26T5 = true, P1S26T6 = true }, Options = { P1S26D1 = 'Lines', P1S26S1 = 20, P1S26S2 = 1, P1S26S3 = 2, P1S26S11 = 1 }, Colors = { P1S26C1 = { 0, 0, 0 }, P1S26C2 = { 255, 255, 255 }, P1S26C3 = { 255, 255, 255 }, P1S26C4 = { 255, 255, 255 }, P1S26C5 = { 255, 255, 255 }, }, }, ['Rainbow Spin'] = { Toggles = { P1S26T2 = true, P1S26T3 = true, P1S26T4 = true, P1S26T5 = true, P1S26T6 = true, P1S26T8 = true }, Options = { P1S26D1 = 'Lines', P1S26S1 = 10, P1S26S2 = 2, P1S26S3 = 6, P1S26S11 = 1, P1S26S4 = 0, P1S26S5 = 90 }, Colors = { P1S26C1 = { 0, 0, 0 }, P1S26C2 = { 255, 0, 0 }, P1S26C3 = { 0, 255, 0 }, P1S26C4 = { 0, 0, 255 }, P1S26C5 = { 255, 255, 0 }, }, }, } function RivalsRuntimeBridge.ViewmodelVisuals.ApplyCrosshairPreset(name) local visuals = RivalsRuntimeBridge.ViewmodelVisuals local preset = visuals.CrosshairPresets[name] if not preset then return end local wanted = preset.Toggles or {} for _, toggleId in ipairs(visuals.CrosshairPresetManagedToggles) do local toggle = Toggles and Toggles[toggleId] if toggle and wanted[toggleId] ~= true and toggle.Value then toggle:SetValue(false) end end for optionId, value in pairs(preset.Options or {}) do local option = Options and Options[optionId] if option and type(option.SetValue) == 'function' then option:SetValue(value) end end for optionId, rgb in pairs(preset.Colors or {}) do local option = Options and Options[optionId] if option and type(option.SetValueRGB) == 'function' then option:SetValueRGB(Color3.fromRGB(rgb[1], rgb[2], rgb[3])) end end for _, toggleId in ipairs(visuals.CrosshairPresetManagedToggles) do local toggle = Toggles and Toggles[toggleId] if toggle and wanted[toggleId] == true and not toggle.Value then toggle:SetValue(true) end end if Toggles and Toggles.P1S26T1 and not Toggles.P1S26T1.Value then Toggles.P1S26T1:SetValue(true) end visuals.RefreshAll() end function RivalsRuntimeBridge.ViewmodelVisuals.RestoreNativeCrosshairs() RivalsRuntimeBridge.NativeRemovals.SetNativeCrosshairHidden(false) end function RivalsRuntimeBridge.ViewmodelVisuals.HideNativeCrosshairs() RivalsRuntimeBridge.NativeRemovals.SetNativeCrosshairHidden(true) end function RivalsRuntimeBridge.ViewmodelVisuals.SetCrosshairArm( arm, position, size, color, outlineColor, outlineThickness, visible ) arm.Fill.Visible = visible arm.Outline.Visible = visible and outlineThickness > 0 arm.Fill.Position = UDim2.fromOffset(position.X, position.Y) arm.Fill.Size = UDim2.fromOffset(size.X, size.Y) arm.Fill.BackgroundColor3 = color arm.Outline.Position = arm.Fill.Position arm.Outline.Size = UDim2.fromOffset(size.X + outlineThickness * 2, size.Y + outlineThickness * 2) arm.Outline.BackgroundColor3 = outlineColor end function RivalsRuntimeBridge.ViewmodelVisuals.ResolveCrosshairPosition(deltaTime, center) local visuals = RivalsRuntimeBridge.ViewmodelVisuals if not visuals.ReadToggle('P1S26T9') then visuals.FollowPosition = center return center end local targetPosition = center if visuals.ReadToggle('P2S1T1') and type(RivalsRuntimeBridge.GetBestAimbotTarget) == 'function' then local targetInfo if os.clock() - (RivalsRuntimeBridge.LastAimbotSweepAt or 0) <= 0.05 then targetInfo = RivalsRuntimeBridge.LastAimbotSweepTarget else targetInfo = RivalsRuntimeBridge.GetBestAimbotTarget(true, AimbotBridge.IsAimbotIgnoreFovEnabled()) end local camera = Workspace.CurrentCamera if targetInfo and targetInfo.worldPosition and camera then local projected, onScreen = camera:WorldToViewportPoint(targetInfo.worldPosition) if onScreen and projected.Z > 0 then targetPosition = Vector2.new(projected.X, projected.Y) end end end if not visuals.FollowPosition then visuals.FollowPosition = targetPosition end local alpha = math.clamp(1 - math.exp(-math.max(deltaTime or 0, 0) * 15), 0, 1) visuals.FollowPosition = visuals.FollowPosition:Lerp(targetPosition, alpha) return visuals.FollowPosition end function RivalsRuntimeBridge.ViewmodelVisuals.UpdateCrosshair(deltaTime) local visuals = RivalsRuntimeBridge.ViewmodelVisuals if not visuals.ReadToggle('P1S26T1') then if visuals.Crosshair and visuals.Crosshair.Gui then visuals.Crosshair.Gui.Enabled = false end visuals.FollowPosition = nil visuals.RestoreNativeCrosshairs() return end visuals.HideNativeCrosshairs() if not RivalsRuntimeBridge.NativeRemovals.IsNativeCrosshairActive() then if visuals.Crosshair and visuals.Crosshair.Gui then visuals.Crosshair.Gui.Enabled = false end return end local crosshair = visuals.EnsureCrosshair() crosshair.Gui.Enabled = true local camera = Workspace.CurrentCamera local viewport = camera and camera.ViewportSize or Vector2.new(1920, 1080) local center = Vector2.new(viewport.X * 0.5, viewport.Y * 0.5) local position = visuals.ResolveCrosshairPosition(deltaTime, center) crosshair.Container.Position = UDim2.fromOffset(math.round(position.X), math.round(position.Y)) local textPosition = center if visuals.ReadToggle('P1S26T9') and visuals.ReadOption('P1S26D2', 'Crosshair') == 'Crosshair + Text' then textPosition = position end local elapsed = os.clock() local angle = visuals.ReadOption('P1S26S4', 0) if visuals.ReadToggle('P1S26T8') then angle = (angle + elapsed * visuals.ReadOption('P1S26S5', 90)) % 360 end crosshair.Container.Rotation = angle local style = visuals.ReadOption('P1S26D1', 'Lines') local imageMode = style == 'Image' crosshair.Image.Visible = imageMode if imageMode then local size = visuals.ReadOption('P1S26S6', 32) crosshair.Image.Size = UDim2.fromOffset(size, size) crosshair.Image.Image = visuals.ResolveCrosshairImage(visuals.ReadOption('P1S26I1', '')) crosshair.Image.ImageTransparency = math.clamp(visuals.ReadOption('P1S26S7', 0), 0, 1) end local textEnabled = visuals.ReadToggle('P1S26T11') crosshair.Text.Visible = textEnabled if textEnabled then crosshair.Text.Text = tostring(visuals.ReadOption('P1S26I2', '')) crosshair.Text.TextSize = visuals.ReadOption('P1S26S14', 14) crosshair.Text.Font = visuals.FontMap[visuals.ReadOption('P1S26D4', 'Inconsolata')] or Enum.Font.Code crosshair.Text.Position = UDim2.fromOffset(math.round(textPosition.X), math.round(textPosition.Y + visuals.ReadOption('P1S26S15', 24))) local textColor = visuals.ReadOption('P1S26C11', Color3.new(1, 1, 1)) local gradientColor, gradientOffset, gradientRotation = visuals.ResolveTextGradient(elapsed, textColor) crosshair.TextGradient.Color = gradientColor crosshair.TextGradient.Offset = gradientOffset crosshair.TextGradient.Rotation = gradientRotation crosshair.TextStroke.Enabled = visuals.ReadToggle('P1S26T12') crosshair.TextStroke.Color = visuals.ReadOption('P1S26C12', Color3.new(0, 0, 0)) crosshair.TextStroke.Thickness = visuals.ReadOption('P1S26S16', 1) end local length = visuals.ReadOption('P1S26S1', 12) local thickness = visuals.ReadOption('P1S26S2', 2) local gap = visuals.ReadOption('P1S26S3', 6) if visuals.ReadToggle('P1S26T7') then local minimum = visuals.ReadOption('P1S26S8', 4) local maximum = visuals.ReadOption('P1S26S9', 16) local wave = 0.5 - 0.5 * math.cos(elapsed * visuals.ReadOption('P1S26S10', 3)) gap = gap + minimum + (maximum - minimum) * wave end local outlineThickness = visuals.ReadToggle('P1S26T2') and visuals.ReadOption('P1S26S11', 1) or 0 local outlineColor = visuals.ReadOption('P1S26C1', Color3.new(0, 0, 0)) local armDefinitions = { { Key = 'Top', Position = Vector2.new(0, -(gap + length * 0.5)), Size = Vector2.new(thickness, length), Toggle = 'P1S26T3', Color = 'P1S26C2', Fraction = 0, }, { Key = 'Bottom', Position = Vector2.new(0, gap + length * 0.5), Size = Vector2.new(thickness, length), Toggle = 'P1S26T4', Color = 'P1S26C3', Fraction = 0.5, }, { Key = 'Left', Position = Vector2.new(-(gap + length * 0.5), 0), Size = Vector2.new(length, thickness), Toggle = 'P1S26T5', Color = 'P1S26C4', Fraction = 0.75, }, { Key = 'Right', Position = Vector2.new(gap + length * 0.5, 0), Size = Vector2.new(length, thickness), Toggle = 'P1S26T6', Color = 'P1S26C5', Fraction = 0.25, }, } for _, definition in ipairs(armDefinitions) do local baseColor = visuals.ReadOption(definition.Color, Color3.new(1, 1, 1)) visuals.SetCrosshairArm( crosshair[definition.Key], definition.Position, definition.Size, visuals.ResolveLineColor(baseColor, definition.Fraction, elapsed), outlineColor, outlineThickness, not imageMode and visuals.ReadToggle(definition.Toggle) ) end end function RivalsRuntimeBridge.ViewmodelVisuals.RefreshAll() RivalsRuntimeBridge.ViewmodelVisuals.LastReconcileAt = tick() RivalsRuntimeBridge.ViewmodelVisuals.ReconcileModels() RivalsRuntimeBridge.ViewmodelVisuals.UpdateCrosshair(0) end function RivalsRuntimeBridge.ViewmodelVisuals.Update(deltaTime) local visuals = RivalsRuntimeBridge.ViewmodelVisuals if tick() - visuals.LastReconcileAt >= 0.25 then visuals.LastReconcileAt = tick() visuals.ReconcileModels() end visuals.UpdateCrosshair(deltaTime) end function RivalsRuntimeBridge.ViewmodelVisuals.Destroy() local visuals = RivalsRuntimeBridge.ViewmodelVisuals visuals.RestoreNativeCrosshairs() for part, snapshot in pairs(visuals.AppearanceSnapshots) do visuals.RestoreAppearance(part, snapshot) end visuals.AppearanceSnapshots = setmetatable({}, { __mode = 'k' }) for instance, snapshot in pairs(visuals.TextureSnapshots) do visuals.RestoreTexture(instance, snapshot) end visuals.TextureSnapshots = {} for _, entries in pairs(visuals.Highlights) do for _, highlight in pairs(entries) do pcall(function() highlight:Destroy() end) end end visuals.Highlights = {} if visuals.Crosshair and visuals.Crosshair.Gui then pcall(function() visuals.Crosshair.Gui:Destroy() end) end visuals.Crosshair = nil visuals.FollowPosition = nil visuals.LastReconcileAt = 0 end -- ====================================================================== -- Native removals: flashbang, scope, muzzle flash, hitmarker, ... -- ====================================================================== RivalsRuntimeBridge.NativeRemovals = { Modules = {}, Vignette = nil, Tracers = nil, Muzzle = nil, Hitsound = nil, Scope = { OverlayHidden = false, ReticleHidden = false, OverlayDirty = false, ReticleDirty = false, Restore = nil, }, Crosshair = { CrosshairVisible = nil, HitmarkerVisible = nil, Restore = nil, }, Animation = { Restore = nil }, } RivalsRuntimeBridge.NativeRemovals.ViewmodelOffset = { Restore = nil, Active = false, Offset = CFrame.identity } RivalsRuntimeBridge.NativeRemovals.TracerSentinel = '_Tracers\0NoGunTracers' RivalsRuntimeBridge.NativeRemovals.MuzzleStub = { PlayParticles = function() end } RivalsRuntimeBridge.NativeRemovals.ModulePaths = { Other = { 'Modules', 'ClientReplicatedClasses', 'ClientFighter', 'ClientItem', 'ItemInterface', 'Other' }, Scope = { 'Modules', 'ClientReplicatedClasses', 'ClientFighter', 'ClientItem', 'ItemInterface', 'Mouse', 'Scope' }, Gun = { 'Modules', 'ItemTypes', 'Gun' }, ClientViewModel = { 'Modules', 'ClientReplicatedClasses', 'ClientFighter', 'ClientItem', 'ClientViewModel' }, Crosshair = { 'Modules', 'Crosshair' }, ViewModelAnimator = { 'Modules', 'ClientReplicatedClasses', 'ClientFighter', 'ClientItem', 'ClientViewModel', 'ViewModelAnimator', }, } RivalsRuntimeBridge.NativeRemovals.AnimationStub = { Play = function() end, Stop = function() end, } function RivalsRuntimeBridge.NativeRemovals.RequireModule(key) local native = RivalsRuntimeBridge.NativeRemovals local cached = native.Modules[key] if cached then return cached end local path = native.ModulePaths[key] local cursor = LP:FindFirstChild('PlayerScripts') for _, segment in ipairs(path or {}) do cursor = cursor and cursor:FindFirstChild(segment) end if not cursor then return nil end local value = RivalsRuntimeBridge.RequireGameModule(cursor, key) if not value then return nil end native.Modules[key] = value return value end function RivalsRuntimeBridge.NativeRemovals.ReadConstant(method, index) for constantIndex, constantValue in debug.getconstants(method) do if constantIndex == index then return constantValue end end return nil end function RivalsRuntimeBridge.NativeRemovals.SetVignetteHidden(hidden) local native = RivalsRuntimeBridge.NativeRemovals if hidden then if native.Vignette then return end local other = native.RequireModule('Other') local update = other and rawget(other, 'Update') or nil if type(update) ~= 'function' then return end local original = native.ReadConstant(update, 2) if type(original) ~= 'number' then return end debug.setconstant(update, 2, 999) native.Vignette = { method = update, index = 2, value = original } else local record = native.Vignette if not record then return end native.Vignette = nil debug.setconstant(record.method, record.index, record.value) end end function RivalsRuntimeBridge.NativeRemovals.SetGunTracersHidden(hidden) local native = RivalsRuntimeBridge.NativeRemovals if hidden then if native.Tracers then return end local gun = native.RequireModule('Gun') local localTracers = gun and rawget(gun, '_LocalTracers') or nil if type(localTracers) ~= 'function' then return end for index, value in debug.getconstants(localTracers) do if value == '_Tracers' then rawset(gun, native.TracerSentinel, function() end) debug.setconstant(localTracers, index, native.TracerSentinel) native.Tracers = { class = gun, method = localTracers, index = index, value = value } return end end else local record = native.Tracers if not record then return end native.Tracers = nil debug.setconstant(record.method, record.index, record.value) rawset(record.class, native.TracerSentinel, nil) end end function RivalsRuntimeBridge.NativeRemovals.SetMuzzleFlashHidden(hidden) local native = RivalsRuntimeBridge.NativeRemovals if hidden then if native.Muzzle then return end local viewModel = native.RequireModule('ClientViewModel') local muzzleFlash = viewModel and rawget(viewModel, 'MuzzleFlash') or nil if type(muzzleFlash) ~= 'function' then return end local original = debug.getupvalue(muzzleFlash, 2) if type(original) ~= 'table' then return end debug.setupvalue(muzzleFlash, 2, native.MuzzleStub) native.Muzzle = { method = muzzleFlash, value = original } else local record = native.Muzzle if not record then return end native.Muzzle = nil debug.setupvalue(record.method, 2, record.value) end end function RivalsRuntimeBridge.NativeRemovals.SetGameHitsoundDisabled(disabled) local native = RivalsRuntimeBridge.NativeRemovals if disabled then if native.Hitsound then return end local viewModel = native.RequireModule('ClientViewModel') local method = viewModel and rawget(viewModel, 'PlayHitmarkerSound') or nil if type(method) ~= 'function' then return end rawset(viewModel, 'PlayHitmarkerSound', function() end) native.Hitsound = { class = viewModel, method = method } else local record = native.Hitsound if not record then return end native.Hitsound = nil rawset(record.class, 'PlayHitmarkerSound', record.method) end end function RivalsRuntimeBridge.NativeRemovals.EnsureScopeHook() local scope = RivalsRuntimeBridge.NativeRemovals.Scope if scope.Restore ~= nil or not (scope.OverlayHidden or scope.ReticleHidden) then return end local scopeClass = RivalsRuntimeBridge.NativeRemovals.RequireModule('Scope') local setActive = scopeClass and rawget(scopeClass, 'SetActive') or nil if type(setActive) ~= 'function' then return end local wrapped = function(scopeObject, active) coroutine.wrap(setActive)(scopeObject, active) if not rawget(scopeObject, '_is_scope_active') then return end if scope.OverlayDirty then local visible = not scope.OverlayHidden local circleFrame = rawget(scopeObject, 'CircleFrame') local blurFrame = rawget(scopeObject, 'BlurFrame') if circleFrame then circleFrame.Visible = visible end if blurFrame then blurFrame.Visible = visible end end if scope.ReticleDirty then local reticleContainer = rawget(scopeObject, 'ReticleContainer') if reticleContainer then reticleContainer.Visible = not scope.ReticleHidden end end end rawset(scopeClass, 'SetActive', wrapped) scope.Restore = { class = scopeClass, original = setActive } end function RivalsRuntimeBridge.NativeRemovals.RevertScopeHook() local scope = RivalsRuntimeBridge.NativeRemovals.Scope local restore = scope.Restore if not restore then return end scope.Restore = nil rawset(restore.class, 'SetActive', restore.original) end function RivalsRuntimeBridge.NativeRemovals.SetScopeOverlayHidden(hidden) local scope = RivalsRuntimeBridge.NativeRemovals.Scope scope.OverlayHidden = hidden scope.OverlayDirty = true RivalsRuntimeBridge.NativeRemovals.EnsureScopeHook() end function RivalsRuntimeBridge.NativeRemovals.SetScopeReticleHidden(hidden) local scope = RivalsRuntimeBridge.NativeRemovals.Scope scope.ReticleHidden = hidden scope.ReticleDirty = true RivalsRuntimeBridge.NativeRemovals.EnsureScopeHook() end function RivalsRuntimeBridge.NativeRemovals.EnsureCrosshairHook() local crosshair = RivalsRuntimeBridge.NativeRemovals.Crosshair if crosshair.Restore ~= nil then return end if crosshair.CrosshairVisible == nil and crosshair.HitmarkerVisible == nil then return end local class = RivalsRuntimeBridge.NativeRemovals.RequireModule('Crosshair') local update = class and rawget(class, '_Update') or nil if type(update) ~= 'function' then return end local wrapped = function(instance, ...) coroutine.wrap(update)(instance, ...) if rawget(instance, '_destroyed') then return end local frame = rawget(instance, 'Frame') if typeof(frame) ~= 'Instance' then return end local visible = crosshair.CrosshairVisible if visible ~= nil then local foreground = frame:FindFirstChild('Foreground') local background = frame:FindFirstChild('Background') if foreground then foreground.Visible = visible end if background then background.Visible = visible end end local hitmarker = crosshair.HitmarkerVisible if hitmarker ~= nil then local marker = frame:FindFirstChild('Hitmarker') if marker then marker.Visible = hitmarker end end end rawset(class, '_Update', wrapped) crosshair.Restore = { class = class, original = update } end function RivalsRuntimeBridge.NativeRemovals.EachItemCrosshair(callback) local fighter = ResolveLocalFighter() for _, item in pairs(type(fighter) == 'table' and rawget(fighter, 'Items') or {}) do local interface = type(item) == 'table' and rawget(item, 'ItemInterface') or nil local mouse = type(interface) == 'table' and rawget(interface, 'Mouse') or nil local mouseCrosshair = type(mouse) == 'table' and rawget(mouse, 'MouseCrosshair') or nil local object = type(mouseCrosshair) == 'table' and rawget(mouseCrosshair, 'Crosshair') or nil if type(object) == 'table' and not rawget(object, '_destroyed') then callback(object) end end end function RivalsRuntimeBridge.NativeRemovals.IsNativeCrosshairActive() local fighter = ResolveLocalFighter() if type(fighter) ~= 'table' then return false end local equipped = rawget(fighter, 'EquippedItem') if type(equipped) ~= 'table' then return false end local interface = rawget(equipped, 'ItemInterface') local mouse = type(interface) == 'table' and rawget(interface, 'Mouse') or nil local mouseCrosshair = type(mouse) == 'table' and rawget(mouse, 'MouseCrosshair') or nil local object = type(mouseCrosshair) == 'table' and rawget(mouseCrosshair, 'Crosshair') or nil return type(object) == 'table' and rawget(object, '_destroyed') ~= true end function RivalsRuntimeBridge.NativeRemovals.ApplyCrosshairFrames() local crosshair = RivalsRuntimeBridge.NativeRemovals.Crosshair RivalsRuntimeBridge.NativeRemovals.EachItemCrosshair(function(object) local frame = rawget(object, 'Frame') if typeof(frame) ~= 'Instance' then return end if crosshair.CrosshairVisible ~= nil then local foreground = frame:FindFirstChild('Foreground') local background = frame:FindFirstChild('Background') if foreground then foreground.Visible = crosshair.CrosshairVisible end if background then background.Visible = crosshair.CrosshairVisible end end if crosshair.HitmarkerVisible ~= nil then local marker = frame:FindFirstChild('Hitmarker') if marker then marker.Visible = crosshair.HitmarkerVisible end end end) end function RivalsRuntimeBridge.NativeRemovals.RevertCrosshairFrames() local restore = RivalsRuntimeBridge.NativeRemovals.Crosshair.Restore local original = restore and restore.original or nil if type(original) ~= 'function' then return end RivalsRuntimeBridge.NativeRemovals.EachItemCrosshair(function(object) task.spawn(function() local previousIdentity = type(getthreadidentity) == 'function' and getthreadidentity() or nil if type(setthreadidentity) == 'function' then pcall(setthreadidentity, 2) end coroutine.wrap(original)(object) if previousIdentity ~= nil and type(setthreadidentity) == 'function' then pcall(setthreadidentity, previousIdentity) end end) end) end function RivalsRuntimeBridge.NativeRemovals.RevertCrosshairHook() local crosshair = RivalsRuntimeBridge.NativeRemovals.Crosshair local restore = crosshair.Restore if not restore then return end crosshair.Restore = nil rawset(restore.class, '_Update', restore.original) end function RivalsRuntimeBridge.NativeRemovals.SyncCrosshairHook() local crosshair = RivalsRuntimeBridge.NativeRemovals.Crosshair if crosshair.CrosshairVisible == nil and crosshair.HitmarkerVisible == nil then RivalsRuntimeBridge.NativeRemovals.RevertCrosshairHook() return end RivalsRuntimeBridge.NativeRemovals.EnsureCrosshairHook() end function RivalsRuntimeBridge.NativeRemovals.SetNativeCrosshairHidden(hidden) local crosshair = RivalsRuntimeBridge.NativeRemovals.Crosshair local target = nil if hidden then target = false end if crosshair.CrosshairVisible == target then return end crosshair.CrosshairVisible = target if target == nil then RivalsRuntimeBridge.NativeRemovals.RevertCrosshairFrames() if crosshair.HitmarkerVisible ~= nil then RivalsRuntimeBridge.NativeRemovals.ApplyCrosshairFrames() end else RivalsRuntimeBridge.NativeRemovals.ApplyCrosshairFrames() end RivalsRuntimeBridge.NativeRemovals.SyncCrosshairHook() end function RivalsRuntimeBridge.NativeRemovals.SetHitmarkerHidden(hidden) local crosshair = RivalsRuntimeBridge.NativeRemovals.Crosshair local target = nil if hidden then target = false end if crosshair.HitmarkerVisible == target then return end crosshair.HitmarkerVisible = target if target == nil then RivalsRuntimeBridge.NativeRemovals.RevertCrosshairFrames() if crosshair.CrosshairVisible ~= nil then RivalsRuntimeBridge.NativeRemovals.ApplyCrosshairFrames() end else RivalsRuntimeBridge.NativeRemovals.ApplyCrosshairFrames() end RivalsRuntimeBridge.NativeRemovals.SyncCrosshairHook() end function RivalsRuntimeBridge.NativeRemovals.IsLocalAnimator(animator) local viewModel = type(animator) == 'table' and rawget(animator, 'ClientViewModel') or nil local item = type(viewModel) == 'table' and rawget(viewModel, 'ClientItem') or nil local fighter = type(item) == 'table' and rawget(item, 'ClientFighter') or nil return type(fighter) == 'table' and rawget(fighter, 'IsLocalPlayer') == true end function RivalsRuntimeBridge.NativeRemovals.EnsureAnimationHook() local native = RivalsRuntimeBridge.NativeRemovals local animation = native.Animation local class, fromInstance = native.ResolveLiveClass('ViewModelAnimator', 'PlayAnimation') if animation.Restore ~= nil then if not fromInstance or rawequal(animation.Restore.class, class) then return end native.RevertAnimationHook() end local original = class and rawget(class, 'PlayAnimation') or nil if type(original) ~= 'function' then return end local wrapped = function(animator, animationKey, ...) if not native.IsLocalAnimator(animator) or not RivalsModsState.ShouldSuppressViewmodelAnimation(animator, animationKey) then return original(animator, animationKey, ...) end local tracks = rawget(animator, '_animation_tracks') local existing = type(tracks) == 'table' and tracks[animationKey] or nil if existing == nil then return end tracks[animationKey] = native.AnimationStub local ok, err = pcall(original, animator, animationKey, ...) tracks[animationKey] = existing if not ok then error(err, 0) end end rawset(class, 'PlayAnimation', wrapped) animation.Restore = { class = class, original = original } end function RivalsRuntimeBridge.NativeRemovals.RevertAnimationHook() local animation = RivalsRuntimeBridge.NativeRemovals.Animation local restore = animation.Restore if not restore then return end animation.Restore = nil rawset(restore.class, 'PlayAnimation', restore.original) end function RivalsRuntimeBridge.NativeRemovals.ReadNumber(optionId, fallback) local option = Options and Options[optionId] if option ~= nil and type(option.Value) == 'number' then return option.Value end return fallback end function RivalsRuntimeBridge.NativeRemovals.FindLocalViewModel() local fighter = ResolveLocalFighter() local items = fighter and type(fighter.Items) == 'table' and fighter.Items or nil if not items then return nil end for _, item in pairs(items) do local viewModel = type(item) == 'table' and rawget(item, 'ViewModel') or nil if type(viewModel) == 'table' then return viewModel end end return nil end -- The class a hook patches has to be the one the live objects use. When a viewmodel exists its own metatable is that class, -- whatever require() returned on this executor; otherwise the required module is the best guess. function RivalsRuntimeBridge.NativeRemovals.ResolveLiveClass(key, method) local native = RivalsRuntimeBridge.NativeRemovals local viewModel = native.FindLocalViewModel() if viewModel then local instance = key == 'ClientViewModel' and viewModel or rawget(viewModel, 'Animator') local class = type(instance) == 'table' and getmetatable(instance) or nil -- A live viewmodel sits on a small per-weapon subclass (and some weapons add another layer); the shared class that owns the method -- is further up the metatable chain, so walk up to the table that really holds it. local depth = 0 while type(class) == 'table' and depth < 8 do if method == nil or type(rawget(class, method)) == 'function' then return class, true end class = getmetatable(class) depth = depth + 1 end end return native.RequireModule(key), false end function RivalsRuntimeBridge.NativeRemovals.IsLocalFirstPersonViewModel(viewModel) local item = type(viewModel) == 'table' and rawget(viewModel, 'ClientItem') or nil local fighter = type(item) == 'table' and rawget(item, 'ClientFighter') or nil if type(fighter) ~= 'table' or rawget(fighter, 'IsLocalPlayer') ~= true then return false end local model = rawget(viewModel, 'Model') local parent = typeof(model) == 'Instance' and model.Parent or nil return parent ~= nil and parent.Name == 'FirstPerson' end -- The camera controller resets ViewModelOffsetCFrame to identity every frame before it uses it, and the viewmodel is placed -- from the camera's final CFrame, so that field cannot move the arms. SetCFrame is the one place the viewmodel pose is -- written each frame; the offset is applied there, in camera space. function RivalsRuntimeBridge.NativeRemovals.EnsureViewmodelOffsetHook() local native = RivalsRuntimeBridge.NativeRemovals local hook = native.ViewmodelOffset local class, fromInstance = native.ResolveLiveClass('ClientViewModel', 'SetCFrame') if hook.Restore ~= nil then if not fromInstance or rawequal(hook.Restore.class, class) then return end native.RevertViewmodelOffsetHook() hook.Active = true end local original = class and rawget(class, 'SetCFrame') or nil if type(original) ~= 'function' then return end local wrapped = function(viewModel, cframe, ...) if hook.Active and typeof(cframe) == 'CFrame' and native.IsLocalFirstPersonViewModel(viewModel) then local camera = Workspace.CurrentCamera if camera then local cameraFrame = camera.CFrame cframe = cameraFrame * hook.Offset * cameraFrame:ToObjectSpace(cframe) end end return original(viewModel, cframe, ...) end rawset(class, 'SetCFrame', wrapped) hook.Restore = { class = class, original = original } end function RivalsRuntimeBridge.NativeRemovals.RevertViewmodelOffsetHook() local hook = RivalsRuntimeBridge.NativeRemovals.ViewmodelOffset hook.Active = false local restore = hook.Restore if not restore then return end hook.Restore = nil rawset(restore.class, 'SetCFrame', restore.original) end function RivalsRuntimeBridge.NativeRemovals.SyncViewmodelOffsetHook() local native = RivalsRuntimeBridge.NativeRemovals local hook = native.ViewmodelOffset if native.ReadToggle('P4S2T10') then local position = CFrame.new(native.ReadNumber('P4S2S5', 0), native.ReadNumber('P4S2S6', 0), native.ReadNumber('P4S2S7', 0)) local turn = CFrame.Angles( math.rad(native.ReadNumber('PX3S1', 0)), math.rad(native.ReadNumber('PX3S2', 0)), math.rad(native.ReadNumber('PX3S3', 0)) ) hook.Offset = position * turn hook.Active = true native.EnsureViewmodelOffsetHook() else native.RevertViewmodelOffsetHook() end end function RivalsRuntimeBridge.NativeRemovals.SyncAnimationHook() local native = RivalsRuntimeBridge.NativeRemovals if native.ReadToggle('P1S31T2') or native.ReadToggle('P1S31T3') or native.ReadToggle('P1S31T4') or native.ReadToggle('P1S31T5') then native.EnsureAnimationHook() else native.RevertAnimationHook() end end function RivalsRuntimeBridge.NativeRemovals.ReadToggle(optionId) return Toggles and Toggles[optionId] and Toggles[optionId].Value == true end function RivalsRuntimeBridge.NativeRemovals.RefreshAll() local native = RivalsRuntimeBridge.NativeRemovals local caps = __ZkhHub_genv.ZkxHubCaps native.SetVignetteHidden( native.ReadToggle('P1S27T3') and caps.gate('No ADS Vignette', 'debug.getconstants', 'debug.setconstant') ) native.SetScopeOverlayHidden(native.ReadToggle('P1S27T4')) native.SetScopeReticleHidden(native.ReadToggle('P1S27T5')) native.SetGunTracersHidden( native.ReadToggle('P1S27T6') and caps.gate('No Gun Tracers', 'debug.getconstants', 'debug.setconstant') ) native.SetMuzzleFlashHidden( native.ReadToggle('P1S27T7') and caps.gate('No Muzzle Flash', 'debug.getupvalue', 'debug.setupvalue') ) native.SetHitmarkerHidden(native.ReadToggle('P1S27T8')) native.SetGameHitsoundDisabled(native.ReadToggle('P1S15T3')) RivalsRuntimeBridge.ReplicationRemovals.RefreshAll() end function RivalsRuntimeBridge.NativeRemovals.Destroy() local native = RivalsRuntimeBridge.NativeRemovals native.SetVignetteHidden(false) native.SetGunTracersHidden(false) native.SetMuzzleFlashHidden(false) native.SetGameHitsoundDisabled(false) native.RevertScopeHook() native.RevertCrosshairFrames() native.RevertCrosshairHook() native.RevertAnimationHook() native.RevertViewmodelOffsetHook() RivalsRuntimeBridge.ReplicationRemovals.Destroy() RivalsRuntimeBridge.ReplicateHook.Destroy() local scope = native.Scope scope.OverlayHidden = false scope.ReticleHidden = false scope.OverlayDirty = false scope.ReticleDirty = false local crosshair = native.Crosshair crosshair.CrosshairVisible = nil crosshair.HitmarkerVisible = nil native.Modules = {} end RivalsRuntimeBridge.ReplicateHook = { BindingsByEnum = {}, Restore = nil, } function RivalsRuntimeBridge.ReplicateHook.ResolveController() local playerScripts = LP:FindFirstChild('PlayerScripts') local modules = playerScripts and playerScripts:FindFirstChild('Modules') local module = modules and modules:FindFirstChild('ReplicatedController') if not module then return nil end local controller = RivalsRuntimeBridge.RequireGameModule(module, 'ReplicatedController') if not controller then return nil end return controller end function RivalsRuntimeBridge.ReplicateHook.Reserve(enumNames) local hook = RivalsRuntimeBridge.ReplicateHook local enumLibrary = ResolveEnumLibrary() if not enumLibrary or type(enumLibrary.ToEnum) ~= 'function' then return nil end local tokens = {} for _, enumName in ipairs(enumNames) do local ok, token = pcall(enumLibrary.ToEnum, enumLibrary, enumName) if not ok or type(token) ~= 'string' then return nil end tokens[#tokens + 1] = token end local binding = { enabled = false, handler = nil, tokens = tokens } function binding:SetHandler(handler) self.handler = handler end function binding:SetEnabled(enabled) self.enabled = enabled and true or false if self.enabled then RivalsRuntimeBridge.ReplicateHook.Load() end end function binding:Destroy() self.handler = nil self.enabled = false for _, token in ipairs(self.tokens) do local list = hook.BindingsByEnum[token] if list then for index = #list, 1, -1 do if list[index] == self then table.remove(list, index) end end if #list == 0 then hook.BindingsByEnum[token] = nil end end end end for _, token in ipairs(tokens) do local list = hook.BindingsByEnum[token] if not list then list = {} hook.BindingsByEnum[token] = list end table.insert(list, binding) end return binding end function RivalsRuntimeBridge.ReplicateHook.Load() local hook = RivalsRuntimeBridge.ReplicateHook if hook.Restore ~= nil then return true end local controller = hook.ResolveController() local original = controller and rawget(controller, '_ObjectChanged') or nil if type(original) ~= 'function' then return false end local enumLibrary = ResolveEnumLibrary() if not enumLibrary or type(enumLibrary.FromEnum) ~= 'function' then return false end local bindingsByEnum = hook.BindingsByEnum local wrapped = function(...) local packed = table.pack(...) local offset = (type(packed[1]) == 'table') and 1 or 0 local objectId = packed[offset + 1] local token = packed[offset + 2] local bindings = type(token) == 'string' and bindingsByEnum[token] or nil if bindings == nil or type(objectId) ~= 'string' then return (coroutine.wrap(original)(...)) end local args = { n = math.max(packed.n - offset - 2, 0) } for index = 1, args.n do args[index] = packed[offset + 2 + index] end local decoded local decodeOk, decodeValue = pcall(enumLibrary.FromEnum, enumLibrary, token) if decodeOk then decoded = decodeValue end local packet = { block = false, objectId = objectId, enumName = decoded, args = args, } local identity if type(getthreadidentity) == 'function' then identity = getthreadidentity() end if type(setthreadidentity) == 'function' then pcall(setthreadidentity, 8) end for _, binding in ipairs(bindings) do if binding.enabled and binding.handler then pcall(binding.handler, packet) if packet.block then break end end end if identity ~= nil and type(setthreadidentity) == 'function' then pcall(setthreadidentity, identity) end if packet.block then return end return (coroutine.wrap(original)(...)) end rawset(controller, '_ObjectChanged', wrapped) hook.Restore = { controller = controller, original = original } return true end function RivalsRuntimeBridge.ReplicateHook.Revert() local hook = RivalsRuntimeBridge.ReplicateHook local restore = hook.Restore if not restore then return end hook.Restore = nil rawset(restore.controller, '_ObjectChanged', restore.original) end function RivalsRuntimeBridge.ReplicateHook.RevertIfIdle() local hook = RivalsRuntimeBridge.ReplicateHook if hook.Restore == nil then return end for _, list in pairs(hook.BindingsByEnum) do for _, binding in ipairs(list) do if binding.enabled then return end end end hook.Revert() end function RivalsRuntimeBridge.ReplicateHook.Destroy() local hook = RivalsRuntimeBridge.ReplicateHook hook.Revert() hook.BindingsByEnum = {} end function RivalsRuntimeBridge.ReplicateHook.DecodeArg(value) if type(value) ~= 'string' then return nil end local enumLibrary = ResolveEnumLibrary() if not enumLibrary or type(enumLibrary.FromEnum) ~= 'function' then return nil end local ok, name = pcall(enumLibrary.FromEnum, enumLibrary, value) if not ok or type(name) ~= 'string' then return nil end return name end RivalsRuntimeBridge.ReplicationRemovals = { Flashbang = nil, Burn = nil, } function RivalsRuntimeBridge.ReplicationRemovals.EnsureBindings() local removals = RivalsRuntimeBridge.ReplicationRemovals local hook = RivalsRuntimeBridge.ReplicateHook if not removals.Flashbang then local binding = hook.Reserve({ 'ItemChanged' }) if binding then binding:SetHandler(function(packet) if hook.DecodeArg(packet.args[2]) == 'BlindEffect' then packet.block = true end end) removals.Flashbang = binding end end if not removals.Burn then local binding = hook.Reserve({ 'EntityChanged' }) if binding then binding:SetHandler(function(packet) if hook.DecodeArg(packet.args[1]) ~= 'BurnEffect' then return end local fighter = ResolveLocalFighter() if not fighter then return end local entity = rawget(fighter, 'Entity') local objectId = type(entity) == 'table' and rawget(entity, 'ObjectID') or nil objectId = objectId or RivalsRuntimeBridge.CombatFeedback.ResolveFighterObjectId(fighter) if objectId == nil or objectId ~= packet.objectId then return end packet.block = true end) removals.Burn = binding end end end function RivalsRuntimeBridge.ReplicationRemovals.RefreshAll() local removals = RivalsRuntimeBridge.ReplicationRemovals local native = RivalsRuntimeBridge.NativeRemovals local wantFlash = native.ReadToggle('P1S27T1') local wantBurn = native.ReadToggle('P1S27T2') if wantFlash or wantBurn then removals.EnsureBindings() end if removals.Flashbang then removals.Flashbang:SetEnabled(wantFlash) end if removals.Burn then removals.Burn:SetEnabled(wantBurn) end RivalsRuntimeBridge.ReplicateHook.RevertIfIdle() end function RivalsRuntimeBridge.ReplicationRemovals.Destroy() local removals = RivalsRuntimeBridge.ReplicationRemovals if removals.Flashbang then removals.Flashbang:Destroy() removals.Flashbang = nil end if removals.Burn then removals.Burn:Destroy() removals.Burn = nil end end -- ====================================================================== -- Movement: speed, flight, noclip, auto strafe, long jump -- ====================================================================== RivalsRuntimeBridge.Movement = { Character = nil, Humanoid = nil, RootPart = nil, CollidableParts = {}, NoclipCharacter = nil, NoclipConnections = nil, NoclipEnabled = false, Flight = nil, MechanicsController = nil, PlayerModule = nil, WalkSpeedHookLoaded = false, WalkSpeedHookGetter = nil, WalkSpeedHookUpvalueIndex = nil, WalkSpeedHookOldUpvalue = nil, WalkMultiplierEnabled = false, WalkMultiplier = 1, SlidingMultiplierEnabled = false, SlidingMultiplier = 1, JumpPowerHookLoaded = false, JumpPowerHookDestroyed = false, JumpPowerEnabled = false, JumpPowerMultiplier = 1, LongJumpPressed = false, } function RivalsRuntimeBridge.Movement.ReadToggle(optionId) return Toggles and Toggles[optionId] and Toggles[optionId].Value == true end function RivalsRuntimeBridge.Movement.ReadNumber(optionId, fallback) local option = Options and Options[optionId] return option and type(option.Value) == 'number' and option.Value or fallback end function RivalsRuntimeBridge.Movement.IsKeyActive(optionId) local option = Options and Options[optionId] if option and type(option.GetState) == 'function' then return option:GetState() end return true end function RivalsRuntimeBridge.Movement.ReplaceJumpPowerConstants(target) local movement = RivalsRuntimeBridge.Movement for index, value in pairs(debug.getupvalues(target)) do if type(value) == 'table' then local metatable = getmetatable(value) local metaIndex = type(metatable) == 'table' and rawget(metatable, '__index') or nil local original = nil if type(metaIndex) == 'function' then local ok, wrapped = pcall(function() return value['meow\0d67'] end) original = ok and wrapped or nil elseif rawget(value, 'BASE_JUMPPOWER') ~= nil then original = value end if original ~= nil then local upvalueIndex = index debug.setupvalue( target, upvalueIndex, setmetatable({}, { __index = function(_, key) if key == 'meow\0d67' then return original end if movement.JumpPowerHookDestroyed then debug.setupvalue(target, upvalueIndex, original) return rawget(original, key) end if key ~= 'BASE_JUMPPOWER' then return rawget(original, key) end return rawget(original, 'BASE_JUMPPOWER') * (movement.JumpPowerEnabled and movement.JumpPowerMultiplier or 1) end, }) ) break end end end end function RivalsRuntimeBridge.Movement.AttemptLoadJumpPowerHook() local movement = RivalsRuntimeBridge.Movement if movement.JumpPowerHookLoaded then return true end if type(debug.getupvalues) ~= 'function' or type(debug.setupvalue) ~= 'function' or type(debug.getconstants) ~= 'function' or type(debug.getprotos) ~= 'function' or type(debug.getproto) ~= 'function' then return false end local mechanics = movement.ResolveMechanicsController() if type(mechanics) ~= 'table' then return false end local metatable = getmetatable(mechanics) local classIndex = type(metatable) == 'table' and rawget(metatable, '__index') or nil local hookFighter = type(classIndex) == 'table' and rawget(classIndex, '_HookFighter') or nil if type(hookFighter) ~= 'function' then return false end local localFighter = rawget(mechanics, 'LocalFighter') if localFighter == nil then return false end movement.ReplaceJumpPowerConstants(hookFighter) local entityAdded = rawget(localFighter, 'EntityAdded') local handler = type(entityAdded) == 'table' and rawget(entityAdded, '_handlerListHead') or nil local entityCallback = nil while type(handler) == 'table' and entityCallback == nil do local fn = rawget(handler, '_fn') if type(fn) == 'function' then for _, constant in pairs(debug.getconstants(fn)) do if constant == 'CustomGravity' then entityCallback = fn break end end end handler = rawget(handler, '_next') end if entityCallback == nil then LP:Kick('JumpPower error: ent') return false end movement.ReplaceJumpPowerConstants(entityCallback) for protoIndex in pairs(debug.getprotos(entityCallback)) do local activated = debug.getproto(entityCallback, protoIndex, true) local instance = type(activated) == 'table' and activated[1] or nil if instance ~= nil then movement.ReplaceJumpPowerConstants(instance) end end movement.JumpPowerHookDestroyed = false movement.JumpPowerHookLoaded = true return true end function RivalsRuntimeBridge.Movement.ClearNoclipCharacter() local movement = RivalsRuntimeBridge.Movement if movement.NoclipConnections then for _, connection in ipairs(movement.NoclipConnections) do connection:Disconnect() end end movement.NoclipConnections = nil movement.NoclipCharacter = nil table.clear(movement.CollidableParts) end function RivalsRuntimeBridge.Movement.BindNoclipCharacter(character) local movement = RivalsRuntimeBridge.Movement movement.ClearNoclipCharacter() movement.NoclipCharacter = character for _, descendant in ipairs(character:GetDescendants()) do if descendant:IsA('BasePart') and descendant.CanCollide then movement.CollidableParts[descendant] = true end end movement.NoclipConnections = { character.DescendantAdded:Connect(function(descendant) if descendant:IsA('BasePart') and descendant.CanCollide then movement.CollidableParts[descendant] = true end end), character.DescendantRemoving:Connect(function(descendant) if descendant:IsA('BasePart') then movement.CollidableParts[descendant] = nil end end), } end function RivalsRuntimeBridge.Movement.SetNoclipEnabled(enabled) local movement = RivalsRuntimeBridge.Movement if movement.NoclipEnabled and not enabled then for part in pairs(movement.CollidableParts) do part.CanCollide = true end end movement.NoclipEnabled = enabled == true end function RivalsRuntimeBridge.Movement.DestroyFlight() local movement = RivalsRuntimeBridge.Movement local flight = movement.Flight movement.Flight = nil if flight then pcall(function() flight.Velocity:Destroy() end) pcall(function() flight.Attachment:Destroy() end) end end function RivalsRuntimeBridge.Movement.ResolveCharacter() local movement = RivalsRuntimeBridge.Movement local character = LP.Character local humanoid = character and character:FindFirstChildOfClass('Humanoid') or nil local rootPart = character and character:FindFirstChild('HumanoidRootPart') or nil if character ~= movement.Character or humanoid ~= movement.Humanoid or rootPart ~= movement.RootPart then movement.ClearNoclipCharacter() movement.DestroyFlight() movement.Character = character movement.Humanoid = humanoid movement.RootPart = rootPart movement.LongJumpPressed = false end return character, humanoid, rootPart end function RivalsRuntimeBridge.Movement.ResolveControlsMoveVector() local movement = RivalsRuntimeBridge.Movement if movement.PlayerModule == nil then local playerScripts = LP:FindFirstChild('PlayerScripts') local moduleScript = playerScripts and playerScripts:FindFirstChild('PlayerModule') if moduleScript then local ok, playerModule = pcall(require, moduleScript) if ok and type(playerModule) == 'table' and type(playerModule.GetControls) == 'function' then movement.PlayerModule = playerModule end end end local playerModule = movement.PlayerModule if playerModule then local ok, moveVector = pcall(function() return playerModule:GetControls():GetMoveVector() end) if ok and typeof(moveVector) == 'Vector3' then return moveVector end end return Vector3.zero end function RivalsRuntimeBridge.Movement.FlightMoveDirection() local movement = RivalsRuntimeBridge.Movement if AimbotInputState.FreecamActive or UserInputService:GetFocusedTextBox() then return Vector3.zero end local direction = Vector3.zero local cframe = Workspace.CurrentCamera.CFrame if UserInputService.KeyboardEnabled then local look = cframe.LookVector local flatLook = Vector3.new(look.X, 0, look.Z) if flatLook.Magnitude > 0 then flatLook = flatLook.Unit end if UserInputService:IsKeyDown(Enum.KeyCode.W) then direction = direction + flatLook end if UserInputService:IsKeyDown(Enum.KeyCode.A) then direction = direction - cframe.RightVector end if UserInputService:IsKeyDown(Enum.KeyCode.S) then direction = direction - flatLook end if UserInputService:IsKeyDown(Enum.KeyCode.D) then direction = direction + cframe.RightVector end if UserInputService:IsKeyDown(Enum.KeyCode.Space) then direction = direction + Vector3.new(0, 1, 0) end if UserInputService:IsKeyDown(Enum.KeyCode.LeftControl) or UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) then direction = direction - Vector3.new(0, 1, 0) end else local moveVector = movement.ResolveControlsMoveVector() if moveVector.Magnitude > 0 then direction = direction + cframe.LookVector * -moveVector.Z direction = direction + cframe.RightVector * moveVector.X end end return direction end function RivalsRuntimeBridge.Movement.FlattenDirection(vector) local flat = Vector3.new(vector.X, 0, vector.Z) return flat.Magnitude > 0 and flat.Unit or Vector3.zero end function RivalsRuntimeBridge.Movement.StrafeMoveDirection() local movement = RivalsRuntimeBridge.Movement if AimbotInputState.FreecamActive or UserInputService:GetFocusedTextBox() then return Vector3.zero end local direction = Vector3.zero local cframe = Workspace.CurrentCamera.CFrame local look = movement.FlattenDirection(cframe.LookVector) local right = movement.FlattenDirection(cframe.RightVector) if UserInputService.KeyboardEnabled then if UserInputService:IsKeyDown(Enum.KeyCode.W) then direction = direction + look end if UserInputService:IsKeyDown(Enum.KeyCode.S) then direction = direction - look end if UserInputService:IsKeyDown(Enum.KeyCode.D) then direction = direction + right end if UserInputService:IsKeyDown(Enum.KeyCode.A) then direction = direction - right end else local moveVector = movement.ResolveControlsMoveVector() if moveVector.Magnitude > 0 then direction = direction + look * -moveVector.Z direction = direction + right * moveVector.X end end if direction.Magnitude > 0 then direction = direction.Unit end return direction end function RivalsRuntimeBridge.Movement.UpdateFlight(active) local movement = RivalsRuntimeBridge.Movement local rootPart = movement.RootPart if not active or not rootPart or rootPart.Parent == nil or not movement.Humanoid or movement.Humanoid.Health <= 0 then movement.DestroyFlight() return end local direction = movement.FlightMoveDirection() if direction.Magnitude > 0 then direction = direction.Unit end local velocityVector = direction * movement.ReadNumber('P10S3S4', 100) local flight = movement.Flight if flight and flight.RootPart ~= rootPart then movement.DestroyFlight() flight = nil end if flight then flight.Velocity.VectorVelocity = velocityVector return end local attachment = Instance.new('Attachment') attachment.Parent = rootPart local velocity = Instance.new('LinearVelocity') velocity.RelativeTo = Enum.ActuatorRelativeTo.World velocity.MaxForce = math.huge velocity.VectorVelocity = velocityVector velocity.Attachment0 = attachment velocity.Parent = rootPart movement.Flight = { RootPart = rootPart, Attachment = attachment, Velocity = velocity } end function RivalsRuntimeBridge.Movement.BlankFlightVelocity() local movement = RivalsRuntimeBridge.Movement if movement.Flight and movement.RootPart and movement.RootPart.Parent then movement.RootPart.AssemblyLinearVelocity = Vector3.zero end end function RivalsRuntimeBridge.Movement.UpdateNoclip(active) local movement = RivalsRuntimeBridge.Movement local character = movement.Character local alive = character ~= nil and movement.Humanoid ~= nil and movement.Humanoid.Health > 0 if not alive then movement.ClearNoclipCharacter() elseif movement.NoclipCharacter ~= character then movement.BindNoclipCharacter(character) end movement.SetNoclipEnabled(active == true and alive) if not movement.NoclipEnabled then return end for part in pairs(movement.CollidableParts) do part.CanCollide = false end end function RivalsRuntimeBridge.Movement.UpdateAutoStrafe(deltaTime, active) local movement = RivalsRuntimeBridge.Movement local rootPart = movement.RootPart if not active or not rootPart or not movement.Humanoid or movement.Humanoid.Health <= 0 or not UserInputService:IsKeyDown(Enum.KeyCode.Space) then return end local speed = movement.ReadNumber('P10S3S5', 50) rootPart.CFrame = rootPart.CFrame + movement.StrafeMoveDirection() * (speed / 100) * deltaTime * 60 rootPart.AssemblyLinearVelocity = rootPart.AssemblyLinearVelocity * Vector3.new(0, 1, 0) end function RivalsRuntimeBridge.Movement.ResolveMechanicsController() local movement = RivalsRuntimeBridge.Movement if movement.MechanicsController then return movement.MechanicsController end if not IsReplicatedStateReady() then return nil end local playerScripts = LP:FindFirstChild('PlayerScripts') local controllers = playerScripts and playerScripts:FindFirstChild('Controllers') local mechanicsModule = controllers and controllers:FindFirstChild('MechanicsController') if mechanicsModule then local ok, controller = pcall(require, mechanicsModule) movement.MechanicsController = ok and type(controller) == 'table' and controller or nil end return movement.MechanicsController end function RivalsRuntimeBridge.Movement.AttemptLoadWalkSpeedHook() local movement = RivalsRuntimeBridge.Movement if movement.WalkSpeedHookLoaded then return true end if type(debug.getupvalues) ~= 'function' or type(debug.setupvalue) ~= 'function' then return false end local mechanics = movement.ResolveMechanicsController() if type(mechanics) ~= 'table' then return false end local metatable = getmetatable(mechanics) local classIndex = type(metatable) == 'table' and rawget(metatable, '__index') or nil local getWalkSpeed = type(classIndex) == 'table' and rawget(classIndex, '_GetWalkSpeed') or nil if type(getWalkSpeed) ~= 'function' then return false end local upvalueIndex, oldUpvalue for index, value in pairs(debug.getupvalues(getWalkSpeed)) do if type(value) == 'table' and rawget(value, 'BASE_WALKSPEED') ~= nil then upvalueIndex = index oldUpvalue = value break end end if upvalueIndex == nil then return false end movement.WalkSpeedHookGetter = getWalkSpeed movement.WalkSpeedHookUpvalueIndex = upvalueIndex movement.WalkSpeedHookOldUpvalue = oldUpvalue debug.setupvalue( getWalkSpeed, upvalueIndex, setmetatable({}, { __index = function(_, key) if key ~= 'BASE_WALKSPEED' then LP:Kick('Unexpected behavior (client physics 1)') return nil end local baseWalkSpeed = rawget(oldUpvalue, 'BASE_WALKSPEED') if movement.SlidingMultiplierEnabled and debug.info(3, 'n') == 'Slide' then return baseWalkSpeed * movement.SlidingMultiplier end if movement.WalkMultiplierEnabled then return baseWalkSpeed * movement.WalkMultiplier end return baseWalkSpeed end, }) ) movement.WalkSpeedHookLoaded = true return true end function RivalsRuntimeBridge.Movement.UpdateWalkSpeed(active) local movement = RivalsRuntimeBridge.Movement movement.WalkMultiplier = movement.ReadNumber('P10S3S1', 2) movement.WalkMultiplierEnabled = active == true movement.AttemptLoadWalkSpeedHook() end function RivalsRuntimeBridge.Movement.UpdateSliding(active) local movement = RivalsRuntimeBridge.Movement movement.SlidingMultiplier = movement.ReadNumber('P10S3S2', 10) movement.SlidingMultiplierEnabled = active == true movement.AttemptLoadWalkSpeedHook() end function RivalsRuntimeBridge.Movement.UpdateJumpPower(active) local movement = RivalsRuntimeBridge.Movement movement.JumpPowerMultiplier = movement.ReadNumber('P10S3S3', 2) movement.JumpPowerEnabled = active == true movement.AttemptLoadJumpPowerHook() end function RivalsRuntimeBridge.Movement.LaunchLongJump() local movement = RivalsRuntimeBridge.Movement local fighter = ResolveLocalFighter() local entity = type(fighter) == 'table' and rawget(fighter, 'Entity') or nil if not entity and fighter and type(fighter.GetEntity) == 'function' then local ok, value = pcall(fighter.GetEntity, fighter) entity = ok and value or nil end local rootPart = movement.RootPart local camera = Workspace.CurrentCamera if type(entity) ~= 'table' or not rootPart or not camera or not movement.Humanoid or movement.Humanoid.Health <= 0 then return false end local lookVector = camera.CFrame.LookVector local direction = Vector3.new(lookVector.X, 0, lookVector.Z) if direction.Magnitude < 0.001 then local rootLook = rootPart.CFrame.LookVector direction = Vector3.new(rootLook.X, 0, rootLook.Z) end direction = direction.Unit if Options.P10S3D1 and Options.P10S3D1.Value == 'Under Feet' then direction = (direction * movement.ReadNumber('P10S3S8', 8) + Vector3.new(0, 4, 0)).Unit end local velocity = direction * movement.ReadNumber('P10S3S6', 60) + Vector3.new(0, movement.ReadNumber('P10S3S7', 20), 0) if type(entity.AirborneCancel) ~= 'function' or type(entity.AirborneTrigger) ~= 'function' then return false end pcall(entity.AirborneCancel, entity) local ok = pcall(entity.AirborneTrigger, entity, velocity, 4) return ok end function RivalsRuntimeBridge.Movement.UpdateLongJump(active) local movement = RivalsRuntimeBridge.Movement if active and not movement.LongJumpPressed then movement.LaunchLongJump() end movement.LongJumpPressed = active end function RivalsRuntimeBridge.Movement.Update(deltaTime) local movement = RivalsRuntimeBridge.Movement movement.ResolveCharacter() movement.UpdateWalkSpeed(movement.ReadToggle('P10S3T1') and movement.IsKeyActive('P10S3T1K')) movement.UpdateSliding(movement.ReadToggle('P10S3T2') and movement.IsKeyActive('P10S3T2K')) movement.UpdateJumpPower(movement.ReadToggle('P10S3T3')) movement.UpdateAutoStrafe(deltaTime, movement.ReadToggle('P10S3T6') and movement.IsKeyActive('P10S3T6K')) movement.UpdateFlight(movement.ReadToggle('P10S3T4') and movement.IsKeyActive('P10S3T4K')) movement.UpdateNoclip(movement.ReadToggle('P10S3T5') and movement.IsKeyActive('P10S3T5K')) movement.UpdateLongJump(movement.ReadToggle('P10S3T7') and movement.IsKeyActive('P10S3T7K')) end function RivalsRuntimeBridge.Movement.RefreshAll() local movement = RivalsRuntimeBridge.Movement local caps = __ZkhHub_genv.ZkxHubCaps if movement.ReadToggle('P10S3T1') or movement.ReadToggle('P10S3T2') then caps.gate('WalkSpeed / Sliding Speed', 'debug.getupvalues', 'debug.setupvalue') end if movement.ReadToggle('P10S3T3') then caps.gate( 'Jump Power', 'debug.getupvalues', 'debug.setupvalue', 'debug.getconstants', 'debug.getprotos', 'debug.getproto' ) end RivalsRuntimeBridge.Movement.Update(0) end function RivalsRuntimeBridge.Movement.Destroy() local movement = RivalsRuntimeBridge.Movement movement.SetNoclipEnabled(false) movement.ClearNoclipCharacter() movement.DestroyFlight() if movement.WalkSpeedHookLoaded and movement.WalkSpeedHookGetter and movement.WalkSpeedHookUpvalueIndex ~= nil and movement.WalkSpeedHookOldUpvalue ~= nil then pcall( debug.setupvalue, movement.WalkSpeedHookGetter, movement.WalkSpeedHookUpvalueIndex, movement.WalkSpeedHookOldUpvalue ) end movement.WalkSpeedHookLoaded = false movement.WalkSpeedHookGetter = nil movement.WalkSpeedHookUpvalueIndex = nil movement.WalkSpeedHookOldUpvalue = nil movement.WalkMultiplierEnabled = false movement.SlidingMultiplierEnabled = false movement.JumpPowerHookDestroyed = true movement.JumpPowerHookLoaded = false movement.JumpPowerEnabled = false movement.MechanicsController = nil movement.PlayerModule = nil movement.Character = nil movement.Humanoid = nil movement.RootPart = nil movement.LongJumpPressed = false end -- ====================================================================== -- Device spoof -- ====================================================================== RivalsRuntimeBridge.DeviceSpoof = { Hook = nil, Controller = nil, ControlsMap = { Desktop = 'MouseKeyboard', Mobile = 'Touch', Console = 'Gamepad', VR = 'VR', }, } function RivalsRuntimeBridge.DeviceSpoof.ResolveControlsController() local spoof = RivalsRuntimeBridge.DeviceSpoof if spoof.Controller then return spoof.Controller end if not ReplicatedStateReady then BeginReplicatedStateBoot() return nil end local playerScripts = LP:FindFirstChild('PlayerScripts') local controllers = playerScripts and playerScripts:FindFirstChild('Controllers') local module = controllers and controllers:FindFirstChild('ControlsController') if not module then return nil end local ok, controller = pcall(require, module) if ok and type(controller) == 'table' then spoof.Controller = controller end return spoof.Controller end function RivalsRuntimeBridge.DeviceSpoof.ReadEnabled() return Toggles and Toggles.P10S7T1 and Toggles.P10S7T1.Value == true end function RivalsRuntimeBridge.DeviceSpoof.ReadSpoofType() local option = Options and Options.P10S7D1 local value = option and option.Value return type(value) == 'string' and value or 'VR' end function RivalsRuntimeBridge.DeviceSpoof.ResolveReplicateControls() local fighterController = ResolveFighterController() if type(fighterController) ~= 'table' then return nil, nil end local metatable = getmetatable(fighterController) local classIndex = type(metatable) == 'table' and rawget(metatable, '__index') or nil local replicateControls = type(classIndex) == 'table' and rawget(classIndex, '_ReplicateControls') or nil if type(replicateControls) ~= 'function' then return nil, fighterController end return replicateControls, fighterController end function RivalsRuntimeBridge.DeviceSpoof.ForceReplicate() local replicateControls, fighterController = RivalsRuntimeBridge.DeviceSpoof.ResolveReplicateControls() if replicateControls and fighterController then pcall(replicateControls, fighterController) end end function RivalsRuntimeBridge.DeviceSpoof.AttemptLoadHook() local spoof = RivalsRuntimeBridge.DeviceSpoof if spoof.Hook then return true end if type(debug.getupvalues) ~= 'function' or type(debug.setupvalue) ~= 'function' then return false end local controlsController = spoof.ResolveControlsController() if type(controlsController) ~= 'table' then return false end local replicateControls = spoof.ResolveReplicateControls() if type(replicateControls) ~= 'function' then return false end local upvalueIndex for index, value in pairs(debug.getupvalues(replicateControls)) do if value == controlsController then upvalueIndex = index break end end if upvalueIndex == nil then return false end local originalValue = debug.getupvalue(replicateControls, upvalueIndex) debug.setupvalue( replicateControls, upvalueIndex, setmetatable({}, { __index = function(_, key) if key ~= 'CurrentControls' then LP:Kick('Unexpected behavior (client misc 2)') return nil end local value = spoof.ControlsMap[spoof.ReadSpoofType()] if value == nil then LP:Kick('Unexpected behavior (client misc 1)') end return value end, }) ) spoof.Hook = { Getter = replicateControls, UpvalueIndex = upvalueIndex, OriginalValue = originalValue, } return true end function RivalsRuntimeBridge.DeviceSpoof.AttemptUnloadHook() local spoof = RivalsRuntimeBridge.DeviceSpoof local hook = spoof.Hook if not hook then return end spoof.Hook = nil pcall(debug.setupvalue, hook.Getter, hook.UpvalueIndex, hook.OriginalValue) end function RivalsRuntimeBridge.DeviceSpoof.RefreshAll() local spoof = RivalsRuntimeBridge.DeviceSpoof if spoof.ReadEnabled() then __ZkhHub_genv.ZkxHubCaps.gate('Device Spoof', 'debug.getupvalues', 'debug.setupvalue') if spoof.AttemptLoadHook() then spoof.ForceReplicate() end elseif spoof.Hook then spoof.AttemptUnloadHook() spoof.ForceReplicate() end end function RivalsRuntimeBridge.DeviceSpoof.Destroy() local spoof = RivalsRuntimeBridge.DeviceSpoof if spoof.Hook then spoof.AttemptUnloadHook() spoof.ForceReplicate() end end -- ====================================================================== -- Player spoofer -- ====================================================================== RivalsRuntimeBridge.PlayerSpoofer = { Binders = setmetatable({}, { __mode = 'k' }), LifecycleConnections = {}, Started = false, Scopes = { 'You', 'Oth' }, AttributeFields = { { key = 'Winstreak', attribute = 'StatisticDuelsWinStreak', control = 'number', youDefault = '999', othDefault = '0' }, { key = 'Level', attribute = 'Level', control = 'number', youDefault = '999', othDefault = '0' }, { key = 'RankedElo', attribute = 'DisplayELO', control = 'number', youDefault = '3600', othDefault = '0' }, { key = 'NametagStatus', attribute = 'PlayerStatus', control = 'text', youDefault = 'Prime', othDefault = 'Prime' }, { key = 'Influencer', attribute = 'IsInfluencer', control = 'const', constValue = true }, { key = 'RobloxEmployee', attribute = 'IsRobloxEmployee', control = 'const', constValue = true }, { key = 'NosniyTeam', attribute = 'GroupRank', control = 'const', constValue = 255 }, }, } function RivalsRuntimeBridge.PlayerSpoofer.CreateBinder(instance, attribute) local binder = { Instance = instance, Attribute = attribute, Original = instance:GetAttribute(attribute), IsWriting = false, Spoofed = nil, Connection = nil, } binder.Connection = instance:GetAttributeChangedSignal(attribute):Connect(function() if binder.IsWriting then return end binder.Original = instance:GetAttribute(attribute) binder.IsWriting = true if binder.Spoofed ~= nil then instance:SetAttribute(attribute, binder.Spoofed.value) end binder.IsWriting = false end) return binder end function RivalsRuntimeBridge.PlayerSpoofer.BinderSet(binder, spoofed) binder.Spoofed = spoofed binder.IsWriting = true if spoofed == nil then binder.Instance:SetAttribute(binder.Attribute, binder.Original) else binder.Instance:SetAttribute(binder.Attribute, spoofed.value) end binder.IsWriting = false end function RivalsRuntimeBridge.PlayerSpoofer.BinderDestroy(binder) if binder.Connection then binder.Connection:Disconnect() end pcall(function() binder.Instance:SetAttribute(binder.Attribute, binder.Original) end) end function RivalsRuntimeBridge.PlayerSpoofer.Bind(player, attribute) local spoofer = RivalsRuntimeBridge.PlayerSpoofer local entries = spoofer.Binders[player] if entries == nil then entries = {} spoofer.Binders[player] = entries end local binder = entries[attribute] if binder == nil then binder = spoofer.CreateBinder(player, attribute) entries[attribute] = binder end return binder end function RivalsRuntimeBridge.PlayerSpoofer.Unbind(player, attribute) local spoofer = RivalsRuntimeBridge.PlayerSpoofer local entries = spoofer.Binders[player] if entries == nil then return end local binder = entries[attribute] if binder ~= nil then spoofer.BinderDestroy(binder) entries[attribute] = nil end end function RivalsRuntimeBridge.PlayerSpoofer.SetAttributeSpoof(player, attribute, enabled, value) local spoofer = RivalsRuntimeBridge.PlayerSpoofer if enabled then spoofer.BinderSet(spoofer.Bind(player, attribute), { value = value }) else spoofer.Unbind(player, attribute) end end function RivalsRuntimeBridge.PlayerSpoofer.ScopeSuffix(player) return player == LP and 'You' or 'Oth' end function RivalsRuntimeBridge.PlayerSpoofer.ReadToggle(optionId) return Toggles and Toggles[optionId] and Toggles[optionId].Value == true end function RivalsRuntimeBridge.PlayerSpoofer.ReadOption(optionId) local option = Options and Options[optionId] return option and option.Value end function RivalsRuntimeBridge.PlayerSpoofer.Apply(player) local spoofer = RivalsRuntimeBridge.PlayerSpoofer if not player or player.Parent == nil then return end local suffix = spoofer.ScopeSuffix(player) for _, field in ipairs(spoofer.AttributeFields) do local enabled = spoofer.ReadToggle('PSpoof' .. suffix .. field.key .. 'T') if field.control == 'const' then spoofer.SetAttributeSpoof(player, field.attribute, enabled, field.constValue) else local raw = spoofer.ReadOption('PSpoof' .. suffix .. field.key .. 'V') local value = raw if field.control == 'number' then value = tonumber(raw) elseif type(raw) == 'string' and raw == '' then value = nil end spoofer.SetAttributeSpoof(player, field.attribute, enabled and value ~= nil, value) end end end function RivalsRuntimeBridge.PlayerSpoofer.Cleanup(player) local spoofer = RivalsRuntimeBridge.PlayerSpoofer local entries = spoofer.Binders[player] if entries == nil then return end for _, binder in pairs(entries) do spoofer.BinderDestroy(binder) end spoofer.Binders[player] = nil end function RivalsRuntimeBridge.PlayerSpoofer.RefreshAll() local spoofer = RivalsRuntimeBridge.PlayerSpoofer spoofer.Start() for _, player in ipairs(Players:GetPlayers()) do spoofer.Apply(player) end end function RivalsRuntimeBridge.PlayerSpoofer.Start() local spoofer = RivalsRuntimeBridge.PlayerSpoofer if spoofer.Started then return end spoofer.Started = true spoofer.LifecycleConnections[#spoofer.LifecycleConnections + 1] = Players.PlayerAdded:Connect( GuardRivalsCallback('PlayerSpoofer_PlayerAdded', function(player) spoofer.Apply(player) end) ) spoofer.LifecycleConnections[#spoofer.LifecycleConnections + 1] = Players.PlayerRemoving:Connect( GuardRivalsCallback('PlayerSpoofer_PlayerRemoving', function(player) spoofer.Cleanup(player) end) ) end function RivalsRuntimeBridge.PlayerSpoofer.Destroy() local spoofer = RivalsRuntimeBridge.PlayerSpoofer for _, connection in ipairs(spoofer.LifecycleConnections) do pcall(function() connection:Disconnect() end) end spoofer.LifecycleConnections = {} spoofer.Started = false for player in pairs(spoofer.Binders) do spoofer.Cleanup(player) end end -- ====================================================================== -- Animation player -- ====================================================================== RivalsRuntimeBridge.AnimationPlayer = { Character = nil, Humanoid = nil, Track = nil, AnimatorWait = nil, AwaitingOwnership = false, LastLoadFailure = nil, } function RivalsRuntimeBridge.AnimationPlayer.ReadEnabled() return Toggles and Toggles.P10S6T1 and Toggles.P10S6T1.Value == true end function RivalsRuntimeBridge.AnimationPlayer.ReadSpeed() local option = Options and Options.P10S6S1 return option and type(option.Value) == 'number' and option.Value or 1 end function RivalsRuntimeBridge.AnimationPlayer.ResolveAnimationId() local option = Options and Options.P10S6I1 local value = option and option.Value if type(value) ~= 'string' then return nil end return value:match('%d+') end function RivalsRuntimeBridge.AnimationPlayer.Stop() local player = RivalsRuntimeBridge.AnimationPlayer if player.AnimatorWait then player.AnimatorWait:Disconnect() player.AnimatorWait = nil end local track = player.Track player.Track = nil if track then pcall(track.Stop, track) pcall(track.Destroy, track) end end function RivalsRuntimeBridge.AnimationPlayer.Apply(awaitOwnership) local player = RivalsRuntimeBridge.AnimationPlayer player.Stop() player.AwaitingOwnership = false if not player.ReadEnabled() then return end local humanoid = player.Humanoid if not humanoid or humanoid.Parent == nil then return end local animator = humanoid:FindFirstChildOfClass('Animator') if not animator then player.AnimatorWait = humanoid.ChildAdded:Connect(function(child) if child:IsA('Animator') and player.Humanoid == humanoid then player.Apply(true) end end) return end local animationId = player.ResolveAnimationId() if not animationId then return end local animation = Instance.new('Animation') animation.AnimationId = 'rbxassetid://' .. animationId local ok, track = pcall(animator.LoadAnimation, animator, animation) animation:Destroy() if not ok or not track then if player.LastLoadFailure ~= animationId then player.LastLoadFailure = animationId if Library and type(Library.Notify) == 'function' then Library:Notify({ Title = 'Animation Player', Description = 'Animation ' .. animationId .. ' could not be loaded.', Time = 3, }) end end return end player.LastLoadFailure = nil track.Priority = Enum.AnimationPriority.Action4 track.Looped = true player.Track = track track:Play() track:AdjustSpeed(player.ReadSpeed()) player.AwaitingOwnership = awaitOwnership == true end function RivalsRuntimeBridge.AnimationPlayer.AdjustSpeed() local player = RivalsRuntimeBridge.AnimationPlayer if player.Track then pcall(player.Track.AdjustSpeed, player.Track, player.ReadSpeed()) end end function RivalsRuntimeBridge.AnimationPlayer.EnforceWindow(track) local length = track.Length if type(length) ~= 'number' or length <= 0 then return end local startOption = Options and Options.P10S6S2 local endOption = Options and Options.P10S6S3 local startPercent = startOption and tonumber(startOption.Value) or 0 local endPercent = endOption and tonumber(endOption.Value) or 100 local windowStart = math.clamp(startPercent, 0, 100) / 100 * length local windowEnd = math.clamp(endPercent, 0, 100) / 100 * length if windowEnd <= windowStart then return end if track.TimePosition >= windowEnd or track.TimePosition < windowStart then track.TimePosition = windowStart end end function RivalsRuntimeBridge.AnimationPlayer.Update() local player = RivalsRuntimeBridge.AnimationPlayer local character = LP.Character local humanoid = character and character:FindFirstChildOfClass('Humanoid') or nil if character ~= player.Character or humanoid ~= player.Humanoid then player.Stop() player.Character = character player.Humanoid = humanoid player.Apply(true) return end local track = player.Track if not track then return end if not track.IsPlaying then player.Track = nil pcall(track.Destroy, track) player.Apply(false) return end if player.AwaitingOwnership and humanoid and humanoid.RootPart then player.AwaitingOwnership = false player.Apply(false) return end player.EnforceWindow(track) end function RivalsRuntimeBridge.AnimationPlayer.RefreshAll() RivalsRuntimeBridge.AnimationPlayer.Apply(false) end function RivalsRuntimeBridge.AnimationPlayer.Destroy() local player = RivalsRuntimeBridge.AnimationPlayer player.Stop() player.Character = nil player.Humanoid = nil player.AwaitingOwnership = false player.LastLoadFailure = nil end -- ====================================================================== -- Movement recorder -- ====================================================================== RivalsRuntimeBridge.MovementRecorder = { SavePath = 'ZkxHub/RIVALS Movement Recordings.json', Version = 1, Mode = 'Idle', PendingKind = nil, Recording = nil, Replay = nil, LastRecording = nil, RecordsByName = {}, RecordsByMap = {}, PersistenceLoaded = false, RefreshingOptions = false, InputConnections = {}, PlayerControls = nil, InputLibrary = nil, MechanicsController = nil, RoundActive = nil, RoundToken = nil, RoundBindingPending = false, RoundBindingGeneration = 0, WaypointDirty = true, WaypointMapName = nil, WaypointMapModel = nil, WaypointAnchor = nil, Markers = {}, Routes = {}, SelectedRecording = nil, ActivatedRecording = nil, } function RivalsRuntimeBridge.MovementRecorder.ReadToggle(optionId) return Toggles and Toggles[optionId] and Toggles[optionId].Value == true end function RivalsRuntimeBridge.MovementRecorder.ReadNumber(optionId, fallback) local option = Options and Options[optionId] return option and type(option.Value) == 'number' and option.Value or fallback end function RivalsRuntimeBridge.MovementRecorder.Notify(message) local recorder = RivalsRuntimeBridge.MovementRecorder if recorder.ReadToggle('P10S4T3') then return end if Library and type(Library.Notify) == 'function' then Library:Notify({ Title = 'Movement Recorder', Description = message, Time = 3 }) end end function RivalsRuntimeBridge.MovementRecorder.SetToggle(optionId, value) local toggle = Toggles and Toggles[optionId] if not toggle or toggle.Value == value or type(toggle.SetValue) ~= 'function' then return end task.defer(function() if Toggles and Toggles[optionId] == toggle and toggle.Value ~= value then toggle:SetValue(value) end end) end function RivalsRuntimeBridge.MovementRecorder.EncodeVector3(value) return { value.X, value.Y, value.Z } end function RivalsRuntimeBridge.MovementRecorder.DecodeVector3(value) if type(value) ~= 'table' then return nil end local x, y, z = tonumber(value[1]), tonumber(value[2]), tonumber(value[3]) if not x or not y or not z then return nil end return Vector3.new(x, y, z) end function RivalsRuntimeBridge.MovementRecorder.EncodeCFrame(value) return { value:GetComponents() } end function RivalsRuntimeBridge.MovementRecorder.DecodeCFrame(value) if type(value) ~= 'table' or #value ~= 12 then return nil end local components = table.create(12) for index = 1, 12 do components[index] = tonumber(value[index]) if components[index] == nil then return nil end end return CFrame.new(table.unpack(components)) end function RivalsRuntimeBridge.MovementRecorder.EncodeAction(action) local encoded = { kind = action.kind } if action.kind == 'Move' then encoded.direction = RivalsRuntimeBridge.MovementRecorder.EncodeVector3(action.direction) elseif action.kind == 'Look' then encoded.cframe = RivalsRuntimeBridge.MovementRecorder.EncodeCFrame(action.cframe) elseif action.kind == 'Equip' then encoded.index = action.index elseif action.kind == 'ItemInput' then encoded.inputType = action.inputType elseif action.kind == 'QuickAttack' then encoded.attackType = action.attackType elseif action.kind == 'Crouch' then encoded.crouching = action.crouching == true end return encoded end function RivalsRuntimeBridge.MovementRecorder.DecodeAction(action) if type(action) ~= 'table' or type(action.kind) ~= 'string' then return nil end local decoded = { kind = action.kind } if action.kind == 'Move' then decoded.direction = RivalsRuntimeBridge.MovementRecorder.DecodeVector3(action.direction) return decoded.direction and decoded or nil elseif action.kind == 'Look' then decoded.cframe = RivalsRuntimeBridge.MovementRecorder.DecodeCFrame(action.cframe) return decoded.cframe and decoded or nil elseif action.kind == 'Equip' then decoded.index = tonumber(action.index) return decoded.index and decoded or nil elseif action.kind == 'ItemInput' then decoded.inputType = type(action.inputType) == 'string' and action.inputType or nil return decoded.inputType and decoded or nil elseif action.kind == 'QuickAttack' then decoded.attackType = type(action.attackType) == 'string' and action.attackType or nil return decoded.attackType and decoded or nil elseif action.kind == 'Crouch' then decoded.crouching = action.crouching == true elseif action.kind ~= 'Jump' and action.kind ~= 'Slide' then return nil end return decoded end function RivalsRuntimeBridge.MovementRecorder.EncodeRecording(recording) local encoded = { mapName = recording.mapName, startCFrame = RivalsRuntimeBridge.MovementRecorder.EncodeCFrame(recording.startCFrame), displayPosition = RivalsRuntimeBridge.MovementRecorder.EncodeVector3(recording.displayPosition), startEquippedIndex = recording.startEquippedIndex, startEquipCooldown = recording.startEquipCooldown, loadoutRequirements = {}, keypoints = {}, positions = {}, } for itemName in pairs(recording.loadoutRequirements or {}) do encoded.loadoutRequirements[#encoded.loadoutRequirements + 1] = itemName end table.sort(encoded.loadoutRequirements) for _, keypoint in ipairs(recording.keypoints or {}) do encoded.keypoints[#encoded.keypoints + 1] = { offset = keypoint.offset, action = RivalsRuntimeBridge.MovementRecorder.EncodeAction(keypoint.action), } end for _, position in ipairs(recording.positions or {}) do encoded.positions[#encoded.positions + 1] = { offset = position.offset, position = RivalsRuntimeBridge.MovementRecorder.EncodeVector3(position.position), } end return encoded end function RivalsRuntimeBridge.MovementRecorder.DecodeRecording(encoded) if type(encoded) ~= 'table' or type(encoded.mapName) ~= 'string' then return nil end local recording = { mapName = encoded.mapName, startCFrame = RivalsRuntimeBridge.MovementRecorder.DecodeCFrame(encoded.startCFrame), displayPosition = RivalsRuntimeBridge.MovementRecorder.DecodeVector3(encoded.displayPosition), startEquippedIndex = tonumber(encoded.startEquippedIndex), startEquipCooldown = tonumber(encoded.startEquipCooldown), loadoutRequirements = {}, keypoints = {}, positions = {}, } if not recording.startCFrame or not recording.displayPosition then return nil end for _, itemName in ipairs(type(encoded.loadoutRequirements) == 'table' and encoded.loadoutRequirements or {}) do if type(itemName) == 'string' and itemName ~= '' then recording.loadoutRequirements[itemName] = true end end for _, keypoint in ipairs(type(encoded.keypoints) == 'table' and encoded.keypoints or {}) do local action = RivalsRuntimeBridge.MovementRecorder.DecodeAction(keypoint.action) local offset = tonumber(keypoint.offset) if action and offset then recording.keypoints[#recording.keypoints + 1] = { offset = offset, action = action } end end for _, position in ipairs(type(encoded.positions) == 'table' and encoded.positions or {}) do local value = RivalsRuntimeBridge.MovementRecorder.DecodeVector3(position.position) local offset = tonumber(position.offset) if value and offset then recording.positions[#recording.positions + 1] = { offset = offset, position = value } end end if #recording.keypoints == 0 then return nil end table.sort(recording.keypoints, function(left, right) return left.offset < right.offset end) table.sort(recording.positions, function(left, right) return left.offset < right.offset end) return recording end function RivalsRuntimeBridge.MovementRecorder.ReindexRecordings() local recorder = RivalsRuntimeBridge.MovementRecorder recorder.RecordsByMap = {} for name, recording in pairs(recorder.RecordsByName) do recording.name = name local names = recorder.RecordsByMap[recording.mapName] if not names then names = {} recorder.RecordsByMap[recording.mapName] = names end names[#names + 1] = name end for _, names in pairs(recorder.RecordsByMap) do table.sort(names) end recorder.WaypointDirty = true end function RivalsRuntimeBridge.MovementRecorder.LoadPersistence() local recorder = RivalsRuntimeBridge.MovementRecorder if recorder.PersistenceLoaded then return end recorder.PersistenceLoaded = true recorder.RecordsByName = {} if type(isfile) == 'function' and isfile(recorder.SavePath) then local ok, decoded = pcall(function() return game:GetService('HttpService'):JSONDecode(readfile(recorder.SavePath)) end) if ok and type(decoded) == 'table' and decoded.version == recorder.Version and type(decoded.recordings) == 'table' then for name, encoded in pairs(decoded.recordings) do local recording = type(name) == 'string' and recorder.DecodeRecording(encoded) or nil if recording then recorder.RecordsByName[name] = recording end end end end recorder.ReindexRecordings() end function RivalsRuntimeBridge.MovementRecorder.WritePersistence() local recorder = RivalsRuntimeBridge.MovementRecorder if type(writefile) ~= 'function' then return false end local encoded = {} for name, recording in pairs(recorder.RecordsByName) do encoded[name] = recorder.EncodeRecording(recording) end local ok = pcall(function() if type(makefolder) == 'function' and type(isfolder) == 'function' and not isfolder('ZkxHub') then makefolder('ZkxHub') end writefile( recorder.SavePath, game:GetService('HttpService'):JSONEncode({ version = recorder.Version, recordings = encoded, }) ) end) return ok end function RivalsRuntimeBridge.MovementRecorder.GetMapNames() local recorder = RivalsRuntimeBridge.MovementRecorder recorder.LoadPersistence() local maps = {} for mapName in pairs(recorder.RecordsByMap) do maps[#maps + 1] = mapName end table.sort(maps) return maps end function RivalsRuntimeBridge.MovementRecorder.GetRecordingNames(mapName) local recorder = RivalsRuntimeBridge.MovementRecorder recorder.LoadPersistence() return table.clone(recorder.RecordsByMap[mapName] or {}) end function RivalsRuntimeBridge.MovementRecorder.RefreshRecordingOptions() local recorder = RivalsRuntimeBridge.MovementRecorder if recorder.RefreshingOptions then return end local mapOption = Options and Options.P10S4D1 local recordingOption = Options and Options.P10S4D2 if not mapOption or not recordingOption then return end recorder.RefreshingOptions = true local maps = recorder.GetMapNames() local currentMap = mapOption.Value if type(mapOption.SetValues) == 'function' then mapOption:SetValues(maps) end if type(currentMap) ~= 'string' or table.find(maps, currentMap) == nil then currentMap = maps[1] if type(mapOption.SetValue) == 'function' then mapOption:SetValue(currentMap) end end local names = recorder.GetRecordingNames(currentMap) local selectedName = recordingOption.Value if type(recordingOption.SetValues) == 'function' then recordingOption:SetValues(names) end if type(selectedName) ~= 'string' or table.find(names, selectedName) == nil then if type(recordingOption.SetValue) == 'function' then recordingOption:SetValue(names[1]) end end recorder.RefreshingOptions = false end function RivalsRuntimeBridge.MovementRecorder.SaveLastRecording(name) local recorder = RivalsRuntimeBridge.MovementRecorder recorder.LoadPersistence() name = type(name) == 'string' and name:match('^%s*(.-)%s*$') or '' if name == '' then recorder.Notify('Enter a recording name first') return false end if not recorder.LastRecording then recorder.Notify('No recording to save') return false end local previous = recorder.RecordsByName[name] recorder.RecordsByName[name] = recorder.LastRecording recorder.ReindexRecordings() if not recorder.WritePersistence() then recorder.RecordsByName[name] = previous recorder.ReindexRecordings() recorder.Notify('Failed to save recording') return false end recorder.LastRecording = nil recorder.RefreshRecordingOptions() recorder.Notify('Recording saved') return true end function RivalsRuntimeBridge.MovementRecorder.DeleteRecording(name) local recorder = RivalsRuntimeBridge.MovementRecorder recorder.LoadPersistence() if type(name) ~= 'string' or name == '' or not recorder.RecordsByName[name] then recorder.Notify('Select a recording to delete') return false end local previous = recorder.RecordsByName[name] recorder.RecordsByName[name] = nil recorder.ReindexRecordings() if not recorder.WritePersistence() then recorder.RecordsByName[name] = previous recorder.ReindexRecordings() recorder.Notify('Failed to delete recording') return false end recorder.RefreshRecordingOptions() recorder.Notify('Recording deleted') return true end function RivalsRuntimeBridge.MovementRecorder.ResolveMapContext() local character = LP.Character local humanoid = character and character:FindFirstChildOfClass('Humanoid') or nil local rootPart = character and character:FindFirstChild('HumanoidRootPart') or nil if not character or not humanoid or humanoid.Health <= 0 or not rootPart then return nil end local environmentId = character:GetAttribute('EnvironmentID') if environmentId == nil then local fighter = ResolveLocalFighter() local data = type(fighter) == 'table' and rawget(fighter, 'Data') or nil environmentId = type(data) == 'table' and rawget(data, 'EnvironmentID') or nil end if environmentId == nil then return nil end local tagged = game:GetService('CollectionService'):GetTagged('RaycastWhitelist' .. tostring(environmentId)) local mapModel = tagged[1] while mapModel and mapModel ~= Workspace and not (mapModel:IsA('Model') or mapModel:GetAttribute('EnvironmentID') == environmentId) do mapModel = mapModel.Parent end if not mapModel or mapModel == Workspace or not mapModel:IsA('PVInstance') then return nil end local mapName local playerScripts = LP:FindFirstChild('PlayerScripts') local controllers = playerScripts and playerScripts:FindFirstChild('Controllers') local duelModule = controllers and controllers:FindFirstChild('DuelController') if duelModule and IsReplicatedStateReady() then local ok, duelController = pcall(require, duelModule) local duel = ok and type(duelController) == 'table' and duelController.CurrentObject or nil if duel and type(duel.GetMapName) == 'function' then local mapOk, value = pcall(duel.GetMapName, duel) mapName = mapOk and type(value) == 'string' and value ~= '' and value or nil end if not mapName and duel and type(duel.Get) == 'function' then for _, key in ipairs({ 'MapName', 'Map' }) do local mapOk, value = pcall(duel.Get, duel, key) if mapOk and type(value) == 'string' and value ~= '' then mapName = value break end end end end if not mapName then for _, attributeName in ipairs({ 'MapName', 'MatchMap' }) do local value = Workspace:GetAttribute(attributeName) if type(value) == 'string' and value ~= '' then mapName = value break end end end mapName = mapName or mapModel.Name return { Character = character, Humanoid = humanoid, RootPart = rootPart, MapModel = mapModel, MapAnchor = mapModel:GetPivot(), MapName = mapName, EnvironmentId = environmentId, } end function RivalsRuntimeBridge.MovementRecorder.IsRoundActive() local recorder = RivalsRuntimeBridge.MovementRecorder local fighter = ResolveLocalFighter() local data = type(fighter) == 'table' and rawget(fighter, 'Data') or nil if type(data) == 'table' and rawget(data, 'IsInShootingRange') == true then return true end if recorder.RoundActive ~= nil then return recorder.RoundActive == true end return FighterDataCache.LocalDuel.IsInDuel == true end function RivalsRuntimeBridge.MovementRecorder.ResolveDependencies() local recorder = RivalsRuntimeBridge.MovementRecorder if not recorder.PlayerControls then local playerModule = LP:FindFirstChild('PlayerScripts') and LP.PlayerScripts:FindFirstChild('PlayerModule') if playerModule then local ok, module = pcall(require, playerModule) if ok and type(module) == 'table' and type(module.GetControls) == 'function' then local controlsOk, controls = pcall(module.GetControls, module) recorder.PlayerControls = controlsOk and controls or nil end end end if not recorder.InputLibrary then local modules = ReplicatedStorage:FindFirstChild('Modules') local inputModule = modules and modules:FindFirstChild('InputLibrary') if inputModule then local ok, library = pcall(require, inputModule) recorder.InputLibrary = ok and type(library) == 'table' and library or nil end end if not recorder.MechanicsController and IsReplicatedStateReady() then local playerScripts = LP:FindFirstChild('PlayerScripts') local controllers = playerScripts and playerScripts:FindFirstChild('Controllers') local mechanicsModule = controllers and controllers:FindFirstChild('MechanicsController') if mechanicsModule then local ok, controller = pcall(require, mechanicsModule) recorder.MechanicsController = ok and type(controller) == 'table' and controller or nil end end return recorder.PlayerControls ~= nil and recorder.InputLibrary ~= nil end function RivalsRuntimeBridge.MovementRecorder.ResolveEquippedItem(fighter) if type(fighter) ~= 'table' then return nil end return rawget(fighter, 'EquippedItem') end function RivalsRuntimeBridge.MovementRecorder.ResolveItemIndex(item, fighter) local data = type(item) == 'table' and rawget(item, 'Data') or nil local index = type(data) == 'table' and tonumber(rawget(data, 'ItemIndex')) or nil if index then return index end for slotIndex, candidate in ipairs(type(fighter) == 'table' and rawget(fighter, 'Items') or {}) do if candidate == item then return slotIndex end end return nil end function RivalsRuntimeBridge.MovementRecorder.ResolveItemName(item) if type(item) ~= 'table' then return nil end local name = rawget(item, 'Name') if type(name) == 'string' and name ~= '' then return name end local data = rawget(item, 'Data') name = type(data) == 'table' and rawget(data, 'Name') or nil return type(name) == 'string' and name ~= '' and name or nil end function RivalsRuntimeBridge.MovementRecorder.GetOffset() local active = RivalsRuntimeBridge.MovementRecorder.Recording return active and math.max(0, os.clock() - active.StartTime) or 0 end function RivalsRuntimeBridge.MovementRecorder.RecordAction(action) local recorder = RivalsRuntimeBridge.MovementRecorder local active = recorder.Recording if not active then return end active.Data.keypoints[#active.Data.keypoints + 1] = { offset = recorder.GetOffset(), action = action, } end function RivalsRuntimeBridge.MovementRecorder.DiscardUnusedStartItem() local active = RivalsRuntimeBridge.MovementRecorder.Recording if not active or active.StartItemUsed or active.Data.startEquippedIndex == nil then return end active.Data.startEquippedIndex = nil active.Data.startEquipCooldown = nil end function RivalsRuntimeBridge.MovementRecorder.RecordItemInput(inputType) local recorder = RivalsRuntimeBridge.MovementRecorder local active = recorder.Recording if not active then return end local fighter = ResolveLocalFighter() local item = recorder.ResolveEquippedItem(fighter) local itemName = recorder.ResolveItemName(item) if itemName then active.Data.loadoutRequirements[itemName] = true end if recorder.ResolveItemIndex(item, fighter) == active.StartItemIndex then active.StartItemUsed = true end recorder.RecordAction({ kind = 'ItemInput', inputType = inputType }) end function RivalsRuntimeBridge.MovementRecorder.InputMatches(input, inputName) local library = RivalsRuntimeBridge.MovementRecorder.InputLibrary if not library or type(library.InputIs) ~= 'function' then return false end local ok, matches = pcall(library.InputIs, library, input, inputName) return ok and matches == true end function RivalsRuntimeBridge.MovementRecorder.HandleInput(input, gameProcessed, began) local recorder = RivalsRuntimeBridge.MovementRecorder if recorder.Mode ~= 'Recording' or gameProcessed then return end if began and recorder.InputMatches(input, 'Jump') then recorder.RecordAction({ kind = 'Jump' }) return end if began and recorder.InputMatches(input, 'QuickMelee') then recorder.RecordAction({ kind = 'QuickAttack', attackType = 'Melee' }) return end if began and recorder.InputMatches(input, 'QuickUtility') then recorder.RecordAction({ kind = 'QuickAttack', attackType = 'Utility' }) return end local library = recorder.InputLibrary for _, inputName in ipairs(type(library) == 'table' and library.ItemInputs or {}) do local definition = type(library.Inputs) == 'table' and library.Inputs[inputName] or nil if definition and recorder.InputMatches(input, inputName) then local inputType = began and definition.StartName or definition.FinishName if type(inputType) == 'string' then recorder.RecordItemInput(inputType) end return end end end function RivalsRuntimeBridge.MovementRecorder.DisconnectRecordingInputs() local recorder = RivalsRuntimeBridge.MovementRecorder for _, connection in ipairs(recorder.InputConnections) do connection:Disconnect() end recorder.InputConnections = {} end function RivalsRuntimeBridge.MovementRecorder.ConnectRecordingInputs() local recorder = RivalsRuntimeBridge.MovementRecorder recorder.DisconnectRecordingInputs() recorder.InputConnections = { UserInputService.InputBegan:Connect( GuardRivalsCallback( 'MovementRecorder_InputBegan', function(input, gameProcessed) recorder.HandleInput(input, gameProcessed, true) end ) ), UserInputService.InputEnded:Connect( GuardRivalsCallback( 'MovementRecorder_InputEnded', function(input, gameProcessed) recorder.HandleInput(input, gameProcessed, false) end ) ), } end function RivalsRuntimeBridge.MovementRecorder.SampleRecording() local recorder = RivalsRuntimeBridge.MovementRecorder local active = recorder.Recording if not active then return end local controls = recorder.PlayerControls local moveVector = Vector3.zero if controls and type(controls.GetMoveVector) == 'function' then local ok, value = pcall(controls.GetMoveVector, controls) if ok and typeof(value) == 'Vector3' then moveVector = value end end if moveVector ~= active.LastMoveVector then active.LastMoveVector = moveVector recorder.RecordAction({ kind = 'Move', direction = moveVector }) end local camera = Workspace.CurrentCamera if camera then local pitch, yaw = camera.CFrame:ToOrientation() local cameraRotation = Vector2.new(pitch, yaw) if (cameraRotation - active.LastCameraRotation).Magnitude > 0.0001 then active.LastCameraRotation = cameraRotation recorder.RecordAction({ kind = 'Look', cframe = active.AnchorInverse * CFrame.fromOrientation(pitch, yaw, 0), }) end end local fighter = ResolveLocalFighter() local item = recorder.ResolveEquippedItem(fighter) local equippedIndex = recorder.ResolveItemIndex(item, fighter) if equippedIndex ~= active.LastEquippedIndex then active.LastEquippedIndex = equippedIndex recorder.DiscardUnusedStartItem() if equippedIndex ~= nil then recorder.RecordAction({ kind = 'Equip', index = equippedIndex }) end end local mechanics = recorder.MechanicsController local crouching = mechanics and rawget(mechanics, 'IsCrouching') == true or false if crouching ~= active.LastCrouching then active.LastCrouching = crouching recorder.RecordAction({ kind = 'Crouch', crouching = crouching }) end local sliding = mechanics and rawget(mechanics, 'IsSliding') == true or false if sliding ~= active.LastSliding then active.LastSliding = sliding if sliding then recorder.RecordAction({ kind = 'Slide' }) end end local now = os.clock() if now - active.LastPositionSampleTime >= 0.1 then active.LastPositionSampleTime = now active.Data.positions[#active.Data.positions + 1] = { offset = recorder.GetOffset(), position = active.AnchorInverse * active.RootPart.Position, } end end function RivalsRuntimeBridge.MovementRecorder.StartRecording() local recorder = RivalsRuntimeBridge.MovementRecorder local context = recorder.ResolveMapContext() if not context or not recorder.ResolveDependencies() then recorder.Notify('You need to be alive and in a match') return false end if recorder.Mode ~= 'Idle' then recorder.Notify(recorder.Mode == 'Recording' and 'Already recording' or "Can't record while replaying") return false end local fighter = ResolveLocalFighter() local item = recorder.ResolveEquippedItem(fighter) local equippedIndex = recorder.ResolveItemIndex(item, fighter) local equipCooldown = type(item) == 'table' and tonumber(rawget(item, '_equip_cooldown')) or nil if equipCooldown then equipCooldown = math.max(0, equipCooldown - tick()) end local camera = Workspace.CurrentCamera local pitch, yaw = camera and camera.CFrame:ToOrientation() or 0, 0 if camera then pitch, yaw = camera.CFrame:ToOrientation() end local anchorInverse = context.MapAnchor:Inverse() local displayPosition = context.RootPart.Position if camera then local center = camera.ViewportSize / 2 local ray = camera:ViewportPointToRay(center.X, center.Y) displayPosition = ray.Origin + ray.Direction * 100 end recorder.Recording = { Data = { mapName = context.MapName, displayPosition = anchorInverse * displayPosition, startCFrame = anchorInverse * context.RootPart.CFrame, startEquippedIndex = equippedIndex, startEquipCooldown = equipCooldown, keypoints = { { offset = 0, action = { kind = 'Look', cframe = anchorInverse * CFrame.fromOrientation(pitch, yaw, 0), }, }, }, loadoutRequirements = {}, positions = {}, }, AnchorInverse = anchorInverse, RootPart = context.RootPart, Character = context.Character, StartTime = os.clock(), StartItemIndex = equippedIndex, StartItemUsed = false, LastMoveVector = Vector3.zero, LastCameraRotation = Vector2.new(pitch, yaw), LastEquippedIndex = equippedIndex, LastCrouching = false, LastSliding = false, LastPositionSampleTime = 0, } recorder.Mode = 'Recording' recorder.ConnectRecordingInputs() recorder.SampleRecording() recorder.Notify('Now recording') return true end function RivalsRuntimeBridge.MovementRecorder.StopRecording(saveLast, notify) local recorder = RivalsRuntimeBridge.MovementRecorder if recorder.Mode ~= 'Recording' or not recorder.Recording then return end recorder.SampleRecording() recorder.DiscardUnusedStartItem() if saveLast then recorder.LastRecording = recorder.Recording.Data end recorder.DisconnectRecordingInputs() recorder.Recording = nil recorder.Mode = 'Idle' if notify then recorder.Notify('Recording stopped') end end function RivalsRuntimeBridge.MovementRecorder.ResolveStartLook(mapAnchor, keypoints) for _, keypoint in ipairs(keypoints or {}) do local action = keypoint.action if action and action.kind == 'Look' then local pitch, yaw = (mapAnchor * action.cframe):ToOrientation() return Vector2.new(pitch, yaw) end end return nil end function RivalsRuntimeBridge.MovementRecorder.LoadoutFulfilled(recording) local owned = {} local fighter = ResolveLocalFighter() for _, item in ipairs(type(fighter) == 'table' and rawget(fighter, 'Items') or {}) do local itemName = RivalsRuntimeBridge.MovementRecorder.ResolveItemName(item) if itemName then owned[itemName] = true end end for itemName in pairs(recording.loadoutRequirements or {}) do if not owned[itemName] then return false end end return true end function RivalsRuntimeBridge.MovementRecorder.StartReplay(recording) local recorder = RivalsRuntimeBridge.MovementRecorder local context = recorder.ResolveMapContext() if not context or not recorder.ResolveDependencies() then recorder.Notify('You need to be alive and in a match') return false end if recorder.Mode ~= 'Idle' then recorder.Notify(recorder.Mode == 'Recording' and "Can't replay while recording" or 'Already replaying') return false end if not recording or recording.mapName ~= context.MapName then recorder.Notify('Stand near a recording marker first') return false end if not recorder.LoadoutFulfilled(recording) then recorder.Notify('Your loadout does not meet this recording') return false end local fighter = ResolveLocalFighter() if recording.startEquippedIndex ~= nil and fighter and type(fighter.EquipItem) == 'function' then task.spawn(function() pcall(fighter.EquipItem, fighter, recording.startEquippedIndex) end) end local controls = recorder.PlayerControls local activeController = type(controls) == 'table' and rawget(controls, 'activeController') or nil local controlsWereEnabled = type(activeController) ~= 'table' or rawget(activeController, 'enabled') ~= false if controls and type(controls.Disable) == 'function' then pcall(controls.Disable, controls) end recorder.Replay = { Data = recording, Character = context.Character, Humanoid = context.Humanoid, RootPart = context.RootPart, Fighter = fighter, MapAnchor = context.MapAnchor, State = 'Aligning', AlignElapsed = 0, StuckTime = 0, BestDistance = math.huge, LookAligned = false, StartLook = recorder.ResolveStartLook(context.MapAnchor, recording.keypoints), TargetLook = nil, MoveVector = Vector3.zero, Offset = 0, KeypointIndex = 1, PositionIndex = 1, Controls = controls, ControlsWereEnabled = controlsWereEnabled, } recorder.Mode = 'Aligning' recorder.ActivatedRecording = recording recorder.SelectedRecording = recording RivalsRuntimeBridge.ResetFlickbot() recorder.Notify('Now replaying') return true end function RivalsRuntimeBridge.MovementRecorder.StopReplay(reason, notify) local recorder = RivalsRuntimeBridge.MovementRecorder local replay = recorder.Replay if not replay then return end if replay.Humanoid and replay.Humanoid.Parent then pcall(replay.Humanoid.Move, replay.Humanoid, Vector3.zero, false) end if replay.ControlsWereEnabled and replay.Controls and type(replay.Controls.Enable) == 'function' then pcall(replay.Controls.Enable, replay.Controls) end local mechanics = recorder.MechanicsController if mechanics and type(mechanics.SetCrouching) == 'function' then pcall(mechanics.SetCrouching, mechanics, false) end recorder.Replay = nil recorder.Mode = 'Idle' recorder.ActivatedRecording = nil if notify then if reason == 'Diverged' then recorder.Notify('Replay stopped (off course)') elseif reason == 'Completed' then recorder.Notify('Finished replaying') else recorder.Notify('Stopped replaying') end end end function RivalsRuntimeBridge.MovementRecorder.IsStartItemReady() local replay = RivalsRuntimeBridge.MovementRecorder.Replay if not replay or replay.Data.startEquippedIndex == nil then return true end local fighter = replay.Fighter local item = RivalsRuntimeBridge.MovementRecorder.ResolveEquippedItem(fighter) if RivalsRuntimeBridge.MovementRecorder.ResolveItemIndex(item, fighter) ~= replay.Data.startEquippedIndex then return false end local cooldown = type(item) == 'table' and rawget(item, '_equip_cooldown') or nil return type(cooldown) ~= 'number' or cooldown <= tick() end function RivalsRuntimeBridge.MovementRecorder.StepAlign(deltaTime) local recorder = RivalsRuntimeBridge.MovementRecorder local replay = recorder.Replay if not replay then return end if not recorder.IsRoundActive() then replay.Humanoid:Move(Vector3.zero, false) return end replay.AlignElapsed = replay.AlignElapsed + deltaTime local targetPosition = (replay.MapAnchor * replay.Data.startCFrame).Position local difference = targetPosition - replay.RootPart.Position local planar = Vector3.new(difference.X, 0, difference.Z) local distance = planar.Magnitude local snapped = distance <= recorder.ReadNumber('P10S4S5', 0.2) if snapped then replay.Humanoid:Move(Vector3.zero, false) else replay.Humanoid:Move(planar.Unit, false) end if distance < replay.BestDistance - 0.1 then replay.BestDistance = distance replay.StuckTime = 0 else replay.StuckTime = replay.StuckTime + deltaTime end local aligned = (snapped and (replay.StartLook == nil or replay.LookAligned)) or replay.StuckTime >= 1 if not aligned then return end replay.Humanoid:Move(Vector3.zero, false) if not recorder.IsStartItemReady() and replay.AlignElapsed < 3 then return end replay.State = 'Playing' recorder.Mode = 'Playing' end function RivalsRuntimeBridge.MovementRecorder.HasDiverged(offset) local replay = RivalsRuntimeBridge.MovementRecorder.Replay local positions = replay and replay.Data.positions or nil if not positions or #positions == 0 then return false end while replay.PositionIndex < #positions and positions[replay.PositionIndex + 1].offset <= offset do replay.PositionIndex = replay.PositionIndex + 1 end local first = positions[replay.PositionIndex] local second = positions[replay.PositionIndex + 1] local expected = first.position if second then local span = second.offset - first.offset local alpha = span > 0 and math.clamp((offset - first.offset) / span, 0, 1) or 0 expected = first.position:Lerp(second.position, alpha) end return (replay.MapAnchor:PointToWorldSpace(expected) - replay.RootPart.Position).Magnitude > 15 end function RivalsRuntimeBridge.MovementRecorder.ApplyReplayAction(action) local recorder = RivalsRuntimeBridge.MovementRecorder local replay = recorder.Replay if not replay then return end if action.kind == 'Move' then replay.MoveVector = action.direction elseif action.kind == 'Look' then local pitch, yaw = (replay.MapAnchor * action.cframe):ToOrientation() replay.TargetLook = Vector2.new(pitch, yaw) elseif action.kind == 'Equip' and replay.Fighter and type(replay.Fighter.EquipItem) == 'function' then task.spawn(function() pcall(replay.Fighter.EquipItem, replay.Fighter, action.index) end) elseif action.kind == 'ItemInput' and replay.Fighter and type(replay.Fighter.Input) == 'function' then task.spawn(function() pcall(replay.Fighter.Input, replay.Fighter, action.inputType) end) elseif action.kind == 'QuickAttack' and replay.Fighter and type(replay.Fighter.QuickAttackDown) == 'function' then task.spawn(function() pcall(replay.Fighter.QuickAttackDown, replay.Fighter, action.attackType) task.wait(0.05) if replay.Fighter and type(replay.Fighter.QuickAttackUp) == 'function' then pcall(replay.Fighter.QuickAttackUp, replay.Fighter, action.attackType) end end) elseif action.kind == 'Jump' then local mechanics = recorder.MechanicsController if mechanics then if rawget(mechanics, 'IsSliding') == true and type(mechanics.HighJump) == 'function' then pcall(mechanics.HighJump, mechanics) else if type(mechanics.DoubleJumpRequest) == 'function' then pcall(mechanics.DoubleJumpRequest, mechanics) end if type(mechanics.JumpRequest) == 'function' then pcall(mechanics.JumpRequest, mechanics) end end end elseif action.kind == 'Crouch' then local mechanics = recorder.MechanicsController if mechanics and type(mechanics.SetCrouching) == 'function' then pcall(mechanics.SetCrouching, mechanics, action.crouching) end elseif action.kind == 'Slide' then local mechanics = recorder.MechanicsController if mechanics and type(mechanics.Slide) == 'function' then task.spawn(function() pcall(mechanics.Slide, mechanics) end) end end end function RivalsRuntimeBridge.MovementRecorder.StepPlaying(deltaTime) local recorder = RivalsRuntimeBridge.MovementRecorder local replay = recorder.Replay if not replay then return end replay.Offset = replay.Offset + deltaTime if recorder.HasDiverged(replay.Offset) then recorder.StopReplay('Diverged', true) recorder.SetToggle('P10S4T5', false) return end local keypoints = replay.Data.keypoints while replay.KeypointIndex <= #keypoints do local keypoint = keypoints[replay.KeypointIndex] if replay.Offset < keypoint.offset then break end recorder.ApplyReplayAction(keypoint.action) replay.KeypointIndex = replay.KeypointIndex + 1 end replay.Humanoid:Move(replay.MoveVector, true) if replay.KeypointIndex > #keypoints then recorder.StopReplay('Completed', true) recorder.SetToggle('P10S4T5', false) end end function RivalsRuntimeBridge.MovementRecorder.WrapAngle(value) return (value + math.pi) % (2 * math.pi) - math.pi end function RivalsRuntimeBridge.MovementRecorder.SmoothingAlpha(deltaTime) local tau = RivalsRuntimeBridge.MovementRecorder.ReadNumber('P10S4S4', 35) / 100 * 0.3 return tau <= 0 and 1 or 1 - math.exp(-deltaTime / tau) end function RivalsRuntimeBridge.MovementRecorder.ReadCameraRotation() local camera = Workspace.CurrentCamera if not camera then return nil end local pitch, yaw = camera.CFrame:ToOrientation() return Vector2.new(pitch, yaw) end function RivalsRuntimeBridge.MovementRecorder.WriteCameraRotation(rotation) local camera = Workspace.CurrentCamera if camera and rotation then camera.CFrame = CFrame.new(camera.CFrame.Position) * CFrame.fromOrientation(rotation.X, rotation.Y, 0) end end function RivalsRuntimeBridge.MovementRecorder.UpdateReplayCamera(deltaTime) local recorder = RivalsRuntimeBridge.MovementRecorder local replay = recorder.Replay local current = replay and recorder.ReadCameraRotation() or nil if not replay or not current then return end if replay.State == 'Aligning' and replay.StartLook then local difference = Vector2.new(replay.StartLook.X - current.X, recorder.WrapAngle(replay.StartLook.Y - current.Y)) local magnitude = difference.Magnitude if magnitude <= math.rad(0.1) then current = replay.StartLook replay.LookAligned = true else local step = math.min( magnitude * recorder.SmoothingAlpha(deltaTime), math.rad(recorder.ReadNumber('P10S4S3', 180)) * deltaTime ) current = current + difference.Unit * step replay.LookAligned = false end recorder.WriteCameraRotation(current) elseif replay.State == 'Playing' and replay.TargetLook then local alpha = recorder.SmoothingAlpha(deltaTime) recorder.WriteCameraRotation( Vector2.new( current.X + (replay.TargetLook.X - current.X) * alpha, current.Y + recorder.WrapAngle(replay.TargetLook.Y - current.Y) * alpha ) ) end end function RivalsRuntimeBridge.MovementRecorder.DestroyWaypoints() local recorder = RivalsRuntimeBridge.MovementRecorder for _, marker in ipairs(recorder.Markers) do pcall(function() marker.Gui:Destroy() end) pcall(function() marker.Attachment:Destroy() end) end for _, route in ipairs(recorder.Routes) do pcall(function() route.Gui:Destroy() end) pcall(function() route.Attachment:Destroy() end) end recorder.Markers = {} recorder.Routes = {} recorder.SelectedRecording = nil recorder.WaypointMapName = nil recorder.WaypointMapModel = nil recorder.WaypointAnchor = nil end function RivalsRuntimeBridge.MovementRecorder.ResolveGuiParent() if type(gethui) == 'function' then local ok, parent = pcall(gethui) if ok and typeof(parent) == 'Instance' then return parent end end return game:GetService('CoreGui') end function RivalsRuntimeBridge.MovementRecorder.CreateWaypointGui(name, position, isRoute) local attachment = Instance.new('Attachment') attachment.Name = isRoute and 'ZkxHubMovementRoute' or 'ZkxHubMovementMarker' attachment.WorldPosition = position attachment.Parent = Workspace.Terrain local gui = Instance.new('BillboardGui') gui.Name = attachment.Name gui.Adornee = attachment gui.AlwaysOnTop = true gui.LightInfluence = 0 gui.MaxDistance = math.huge gui.Size = UDim2.fromOffset(isRoute and math.max(90, #name * 8 + 24) or 110, 26) gui.Enabled = false local label = Instance.new('TextLabel') label.Name = 'Label' label.BackgroundColor3 = Color3.fromRGB(18, 20, 26) label.BackgroundTransparency = 0.2 label.BorderSizePixel = 0 label.Size = UDim2.fromScale(1, 1) label.Font = Enum.Font.GothamSemibold label.TextSize = 14 label.TextColor3 = Color3.new(1, 1, 1) label.Text = name label.Parent = gui local corner = Instance.new('UICorner') corner.CornerRadius = UDim.new(0, 6) corner.Parent = label gui.Parent = RivalsRuntimeBridge.MovementRecorder.ResolveGuiParent() return { Attachment = attachment, Gui = gui, Label = label, Position = position } end function RivalsRuntimeBridge.MovementRecorder.RebuildWaypoints(context) local recorder = RivalsRuntimeBridge.MovementRecorder recorder.DestroyWaypoints() recorder.WaypointDirty = false recorder.WaypointMapName = context.MapName recorder.WaypointMapModel = context.MapModel recorder.WaypointAnchor = context.MapAnchor local grouped = {} for _, name in ipairs(recorder.RecordsByMap[context.MapName] or {}) do local recording = recorder.RecordsByName[name] if recording then local position = recording.startCFrame.Position local key = math.floor(position.X / 8) .. ',' .. math.floor(position.Y / 8) .. ',' .. math.floor(position.Z / 8) local group = grouped[key] if not group then group = { Recordings = {}, Position = Vector3.zero } grouped[key] = group end group.Recordings[#group.Recordings + 1] = recording group.Position = group.Position + position end end for _, group in pairs(grouped) do local worldPosition = context.MapAnchor:PointToWorldSpace(group.Position / #group.Recordings) local text = #group.Recordings == 1 and '1 recording' or tostring(#group.Recordings) .. ' recordings' local marker = recorder.CreateWaypointGui(text, worldPosition, false) marker.Recordings = group.Recordings recorder.Markers[#recorder.Markers + 1] = marker for _, recording in ipairs(group.Recordings) do local route = recorder.CreateWaypointGui(recording.name, context.MapAnchor:PointToWorldSpace(recording.displayPosition), true) route.Recording = recording route.Marker = marker recorder.Routes[#recorder.Routes + 1] = route end end end function RivalsRuntimeBridge.MovementRecorder.UpdateWaypoints() local recorder = RivalsRuntimeBridge.MovementRecorder for _, marker in ipairs(recorder.Markers) do marker.Gui.Enabled = false end for _, route in ipairs(recorder.Routes) do route.Gui.Enabled = false end if not recorder.ReadToggle('P10S4T1') then recorder.SelectedRecording = nil return end local context = recorder.ResolveMapContext() if not context then recorder.SelectedRecording = nil return end if recorder.WaypointDirty or recorder.WaypointMapName ~= context.MapName or recorder.WaypointMapModel ~= context.MapModel or recorder.WaypointAnchor ~= context.MapAnchor then recorder.RebuildWaypoints(context) end local hideUi = recorder.ReadToggle('P10S4T2') if recorder.ActivatedRecording then recorder.SelectedRecording = recorder.ActivatedRecording if not hideUi then for _, route in ipairs(recorder.Routes) do if route.Recording == recorder.ActivatedRecording then route.Label.TextColor3 = Color3.new(0, 1, 0) route.Gui.Enabled = true break end end end return end local nearest, nearestDistance = nil, math.huge for _, marker in ipairs(recorder.Markers) do local distance = (marker.Position - context.RootPart.Position).Magnitude if distance < recorder.ReadNumber('P10S4S1', 3) and distance < nearestDistance then nearest, nearestDistance = marker, distance end end if nearest then local camera = Workspace.CurrentCamera local bestDistance = math.huge recorder.SelectedRecording = nil for _, route in ipairs(recorder.Routes) do if route.Marker == nearest then route.Fulfillable = recorder.LoadoutFulfilled(route.Recording) local point, visible = camera and camera:WorldToViewportPoint(route.Position) or nil, false if point then visible = point.Z >= 0 end if route.Fulfillable and visible then local screenDistance = (Vector2.new(point.X, point.Y) - camera.ViewportSize / 2).Magnitude if screenDistance < bestDistance then bestDistance = screenDistance recorder.SelectedRecording = route.Recording end end end end for _, route in ipairs(recorder.Routes) do if route.Marker == nearest then route.Label.TextColor3 = not route.Fulfillable and Color3.new(1, 0, 0) or route.Recording == recorder.SelectedRecording and Color3.new(1, 1, 0) or Color3.new(1, 1, 1) route.Gui.Enabled = not hideUi end end return end recorder.SelectedRecording = nil if not hideUi then local renderDistance = recorder.ReadNumber('P10S4S2', 50) for _, marker in ipairs(recorder.Markers) do local distance = (marker.Position - context.RootPart.Position).Magnitude if distance < renderDistance then marker.Label.Text = string.format( '%s %.0fm', #marker.Recordings == 1 and '1 recording' or tostring(#marker.Recordings) .. ' recordings', distance ) marker.Gui.Enabled = true end end end end function RivalsRuntimeBridge.MovementRecorder.Begin(kind) local recorder = RivalsRuntimeBridge.MovementRecorder if not recorder.ReadToggle('P10S4T1') then recorder.SetToggle(kind == 'Record' and 'P10S4T4' or 'P10S4T5', false) return false end if recorder.IsRoundActive() then return kind == 'Record' and recorder.StartRecording() or recorder.StartReplay(recorder.SelectedRecording) end if not recorder.ResolveMapContext() then recorder.Notify('You need to be in a match') recorder.SetToggle(kind == 'Record' and 'P10S4T4' or 'P10S4T5', false) return false end recorder.PendingKind = kind recorder.Notify(kind .. ' will start when the round begins') return true end function RivalsRuntimeBridge.MovementRecorder.OnRecordToggled(value) local recorder = RivalsRuntimeBridge.MovementRecorder if value then if recorder.Mode ~= 'Idle' or recorder.PendingKind then recorder.Notify( (recorder.Mode == 'Playing' or recorder.Mode == 'Aligning') and "Can't record while replaying" or 'Already recording' ) recorder.SetToggle('P10S4T4', false) return end if not recorder.Begin('Record') then recorder.SetToggle('P10S4T4', false) end else if recorder.PendingKind == 'Record' then recorder.PendingKind = nil end recorder.StopRecording(true, true) end end function RivalsRuntimeBridge.MovementRecorder.OnReplayToggled(value) local recorder = RivalsRuntimeBridge.MovementRecorder if value then if recorder.Mode ~= 'Idle' or recorder.PendingKind then recorder.Notify(recorder.Mode == 'Recording' and "Can't replay while recording" or 'Already replaying') recorder.SetToggle('P10S4T5', false) return end if not recorder.SelectedRecording then recorder.Notify('Stand near a recording marker first') recorder.SetToggle('P10S4T5', false) return end if not recorder.Begin('Replay') then recorder.SetToggle('P10S4T5', false) end else if recorder.PendingKind == 'Replay' then recorder.PendingKind = nil end recorder.StopReplay('Stopped', true) end end function RivalsRuntimeBridge.MovementRecorder.OnEnabledToggled(value) local recorder = RivalsRuntimeBridge.MovementRecorder recorder.LoadPersistence() recorder.WaypointDirty = true if value then return end recorder.PendingKind = nil recorder.StopRecording(false, false) recorder.StopReplay('Stopped', false) recorder.SetToggle('P10S4T4', false) recorder.SetToggle('P10S4T5', false) recorder.DestroyWaypoints() end function RivalsRuntimeBridge.MovementRecorder.HandleReplicate(_, objectId, enumToken, ...) local recorder = RivalsRuntimeBridge.MovementRecorder if enumToken ~= recorder.RoundToken then return end local fighter = ResolveLocalFighter() local data = type(fighter) == 'table' and rawget(fighter, 'Data') or nil if type(data) == 'table' and rawget(data, 'ObjectID') ~= objectId then return end local status = ... recorder.RoundActive = status == 'RoundStarted' end function RivalsRuntimeBridge.MovementRecorder.BindRoundSignal() local recorder = RivalsRuntimeBridge.MovementRecorder if recorder.RoundBindingPending or not Connections then return end recorder.RoundBindingPending = true recorder.RoundBindingGeneration = recorder.RoundBindingGeneration + 1 local generation = recorder.RoundBindingGeneration BeginReplicatedStateBoot() task.spawn(GuardRivalsCallback('MovementRecorder_BindRound', function() local deadline = tick() + 30 while generation == recorder.RoundBindingGeneration and not ReplicatedStateReady and tick() < deadline do task.wait(0.1) end local enumLibrary = ResolveEnumLibrary() local tokenOk, token = false, nil if enumLibrary and enumLibrary._enum_builder_complete == true and type(enumLibrary.ToEnum) == 'function' then tokenOk, token = pcall(enumLibrary.ToEnum, enumLibrary, 'Status') end local remotes = ReplicatedStorage:FindFirstChild('Remotes') local replication = remotes and remotes:FindFirstChild('Replication') local fighter = replication and replication:FindFirstChild('Fighter') local replicate = fighter and fighter:FindFirstChild('Replicate') if generation == recorder.RoundBindingGeneration and tokenOk and type(token) == 'string' and replicate and replicate:IsA('RemoteEvent') then recorder.RoundToken = token Connections:disconnect({ 'MovementRecorder_RoundReplicate' }) Connections:register( 'MovementRecorder_RoundReplicate', replicate.OnClientEvent:Connect( GuardRivalsCallback('MovementRecorder_RoundReplicate', recorder.HandleReplicate) ) ) end recorder.RoundBindingPending = false end)) end function RivalsRuntimeBridge.MovementRecorder.Update(deltaTime) local recorder = RivalsRuntimeBridge.MovementRecorder recorder.LoadPersistence() if not recorder.ReadToggle('P10S4T1') then return end local context = recorder.ResolveMapContext() local activeCharacter = recorder.Recording and recorder.Recording.Character or recorder.Replay and recorder.Replay.Character or nil if activeCharacter and (not context or context.Character ~= activeCharacter) then recorder.StopRecording(false, false) recorder.StopReplay('Stopped', false) recorder.SetToggle('P10S4T4', false) recorder.SetToggle('P10S4T5', false) return end if recorder.PendingKind and recorder.IsRoundActive() then local kind = recorder.PendingKind recorder.PendingKind = nil local started = kind == 'Record' and recorder.StartRecording() or recorder.StartReplay(recorder.SelectedRecording) if not started then recorder.SetToggle(kind == 'Record' and 'P10S4T4' or 'P10S4T5', false) end end if recorder.Mode == 'Recording' then recorder.SampleRecording() elseif recorder.Mode == 'Aligning' then recorder.StepAlign(math.clamp(tonumber(deltaTime) or 0, 0, 0.1)) elseif recorder.Mode == 'Playing' then recorder.StepPlaying(math.clamp(tonumber(deltaTime) or 0, 0, 0.1)) end end function RivalsRuntimeBridge.MovementRecorder.UpdateRender(deltaTime) local recorder = RivalsRuntimeBridge.MovementRecorder if not recorder.ReadToggle('P10S4T1') then return end recorder.UpdateReplayCamera(math.clamp(tonumber(deltaTime) or 0, 0, 0.1)) recorder.UpdateWaypoints() end function RivalsRuntimeBridge.MovementRecorder.IsCameraClaimed() local recorder = RivalsRuntimeBridge.MovementRecorder if AimbotInputState.FreecamActive then return true end return recorder.Replay ~= nil and (recorder.Mode == 'Aligning' or recorder.Mode == 'Playing') end function RivalsRuntimeBridge.MovementRecorder.Destroy() local recorder = RivalsRuntimeBridge.MovementRecorder recorder.RoundBindingGeneration = recorder.RoundBindingGeneration + 1 recorder.RoundBindingPending = false if Connections then Connections:disconnect({ 'MovementRecorder_RoundReplicate' }) end recorder.PendingKind = nil recorder.StopRecording(false, false) recorder.StopReplay('Stopped', false) recorder.DisconnectRecordingInputs() recorder.DestroyWaypoints() recorder.RoundActive = nil recorder.RoundToken = nil recorder.PlayerControls = nil recorder.InputLibrary = nil recorder.MechanicsController = nil recorder.Mode = 'Idle' end -- ====================================================================== -- Hit / kill feedback and bullet tracers -- ====================================================================== RivalsRuntimeBridge.CombatFeedback = { BindingPending = false, BindingConnected = false, BindingGeneration = 0, EnumTokens = {}, Chams = {}, Tracers = {}, FlashGui = nil, FlashFrame = nil, LastHitNotifyAt = 0, MaxTracers = 128, CustomSoundsLoaded = false, CustomSounds = {}, BuiltInSounds = { { name = 'Bell (Skeet)', soundId = 'rbxassetid://83717596220569' }, { name = 'Bell (Skeet Loud)', soundId = 'rbxassetid://140247876667835' }, { name = 'Neverlose', soundId = 'rbxassetid://139452805868562' }, { name = 'Hitmarker (CoD)', soundId = 'rbxassetid://160432334' }, { name = 'Hitmarker (MLG)', soundId = 'rbxassetid://121351852050830' }, { name = 'Headshot (CS2)', soundId = 'rbxassetid://133002449941130' }, { name = 'Headshot (Rust)', soundId = 'rbxassetid://128633263668964' }, { name = 'Headshot Announcer', soundId = 'rbxassetid://127613993360820' }, { name = 'TF2', soundId = 'rbxassetid://3455144981' }, { name = 'TF2 Crit', soundId = 'rbxassetid://8255306220' }, { name = 'Minecraft', soundId = 'rbxassetid://101908484385575' }, { name = 'Minecraft XP', soundId = 'rbxassetid://6647877129' }, { name = 'osu!', soundId = 'rbxassetid://7149964817' }, { name = 'Bell Ding', soundId = 'rbxassetid://124010691633262' }, { name = 'Bubble Pop', soundId = 'rbxassetid://119697580657161' }, { name = 'Typewriter', soundId = 'rbxassetid://9120300236' }, { name = 'Laser', soundId = 'rbxassetid://8561505457' }, { name = 'Punch', soundId = 'rbxassetid://138087186' }, { name = 'Roblox Oof', soundId = 'rbxassetid://5943191430' }, { name = 'Bonk', soundId = 'rbxassetid://132937320140224' }, { name = 'Among Us', soundId = 'rbxassetid://7227567562' }, { name = 'Ka-Ching', soundId = 'rbxassetid://7112275565' }, { name = 'Fatality', soundId = 'rbxassetid://158012252' }, { name = 'Emotional Damage', soundId = 'rbxassetid://8909407664' }, { name = 'AR2 Head', soundId = 'rbxassetid://2062016772' }, { name = 'AR2 Body', soundId = 'rbxassetid://2062015952' }, { name = 'AR2 Limb', soundId = 'rbxassetid://6659353525' }, { name = 'BB HitM', soundId = 'rbxassetid://4645745735' }, { name = 'BB Kill', soundId = 'rbxassetid://2636743632' }, { name = 'PD Head', soundId = 'rbxassetid://4585351098' }, { name = 'PD Body', soundId = 'rbxassetid://4585364605' }, { name = 'Neverlose (alt)', soundId = 'rbxassetid://6607204501' }, { name = 'Gamesense', soundId = 'rbxassetid://4817809188' }, { name = 'Baimware', soundId = 'rbxassetid://3124331820' }, { name = 'Steve', soundId = 'rbxassetid://4965083997' }, { name = 'Body', soundId = 'rbxassetid://3213738472' }, { name = 'Ding', soundId = 'rbxassetid://7149516994' }, { name = 'Mario', soundId = 'rbxassetid://2815207981' }, { name = 'Mario 2', soundId = 'rbxassetid://5709456554' }, { name = 'Minecraft (alt)', soundId = 'rbxassetid://4018616850' }, { name = 'Among Us (alt)', soundId = 'rbxassetid://5700183626' }, { name = 'Button', soundId = 'rbxassetid://12221967' }, { name = 'Oof', soundId = 'rbxassetid://4792539171' }, { name = 'Sparkle', soundId = 'rbxassetid://132463144859699' }, { name = 'Osu', soundId = 'rbxassetid://7149919358' }, { name = 'Osu Combobreak', soundId = 'rbxassetid://3547118594' }, { name = 'Bambi', soundId = 'rbxassetid://8437203821' }, { name = 'Click', soundId = 'rbxassetid://8053704437' }, { name = 'Snow', soundId = 'rbxassetid://6455527632' }, { name = 'Stone', soundId = 'rbxassetid://3581383408' }, { name = 'Rust', soundId = 'rbxassetid://5043539486' }, { name = 'Splat', soundId = 'rbxassetid://12222152' }, { name = 'Bell', soundId = 'rbxassetid://6534947240' }, { name = 'Slime', soundId = 'rbxassetid://6916371803' }, { name = 'Saber', soundId = 'rbxassetid://8415678813' }, { name = 'Bat', soundId = 'rbxassetid://3333907347' }, { name = 'Bubble', soundId = 'rbxassetid://6534947588' }, { name = 'Pick', soundId = 'rbxassetid://1347140027' }, { name = 'Pop', soundId = 'rbxassetid://18803667669' }, { name = 'EmptyGun', soundId = 'rbxassetid://203691822' }, { name = 'Bamboo', soundId = 'rbxassetid://3769434519' }, { name = 'Stomp', soundId = 'rbxassetid://200632875' }, { name = 'Bag', soundId = 'rbxassetid://364942410' }, { name = 'Hitmarker', soundId = 'rbxassetid://1129547534' }, { name = 'LaserSlash', soundId = 'rbxassetid://199145497' }, { name = 'RailGunF', soundId = 'rbxassetid://199145534' }, { name = 'Bruh', soundId = 'rbxassetid://4275842574' }, { name = 'Crit', soundId = 'rbxassetid://296102734' }, { name = 'Bonk (alt)', soundId = 'rbxassetid://3765689841' }, { name = 'Clink', soundId = 'rbxassetid://711751971' }, { name = 'Lazer Beam', soundId = 'rbxassetid://130791043' }, { name = 'Windows', soundId = 'rbxassetid://9066167010' }, { name = 'HL Med Kit', soundId = 'rbxassetid://4720445506' }, { name = 'HL Door', soundId = 'rbxassetid://4996094887' }, { name = 'HL Crowbar', soundId = 'rbxassetid://546410481' }, { name = 'HL Revolver', soundId = 'rbxassetid://1678424590' }, { name = 'HL Elevator', soundId = 'rbxassetid://237877850' }, { name = 'TF2 Squasher', soundId = 'rbxassetid://3466981613' }, { name = 'TF2 Retro', soundId = 'rbxassetid://3466984142' }, { name = 'TF2 Space', soundId = 'rbxassetid://3466982899' }, { name = 'TF2 Vortex', soundId = 'rbxassetid://3466980212' }, { name = 'TF2 Beepo', soundId = 'rbxassetid://3466987025' }, { name = 'TF2 Pow', soundId = 'rbxassetid://679798995' }, { name = 'TF2 You Suck', soundId = 'rbxassetid://1058417264' }, { name = 'Quake Hitsound', soundId = 'rbxassetid://4868633804' }, { name = 'Fart', soundId = 'rbxassetid://131314452' }, { name = 'Fart2', soundId = 'rbxassetid://6367774932' }, { name = 'FortniteGuns', soundId = 'rbxassetid://3008769599' }, { name = 'Crickets', soundId = 'rbxassetid://2101148' }, { name = 'ScreamingKid', soundId = 'rbxassetid://5980352978' }, { name = 'BitchBot Body', soundId = 'rbxassetid://3744371342' }, { name = 'Minecraft Experience', soundId = 'rbxassetid://1053296915' }, { name = 'BameWare', soundId = 'rbxassetid://7898991882' }, { name = 'Fatality (alt)', soundId = 'rbxassetid://7347423703' }, { name = 'Fatality MKX', soundId = 'rbxassetid://6721975770' }, { name = 'Doublekill 1', soundId = 'rbxassetid://1950547222' }, { name = 'Doublekill 2', soundId = 'rbxassetid://130819307' }, { name = 'Killing Spree 1', soundId = 'rbxassetid://723054723' }, { name = 'Killing Spree 2', soundId = 'rbxassetid://937898383' }, { name = 'Sit Dog', soundId = 'rbxassetid://7349055654' }, { name = 'Csgo', soundId = 'rbxassetid://5764885315' }, { name = 'Bop', soundId = 'rbxassetid://8829676038' }, { name = 'Grenade Hit', soundId = 'rbxassetid://5684745272' }, { name = 'KillTrocity', soundId = 'rbxassetid://6818544945' }, { name = 'Double Kill', soundId = 'rbxassetid://6818527307' }, { name = 'Triple kill', soundId = 'rbxassetid://6818526855' }, { name = 'Over Kill', soundId = 'rbxassetid://6818526995' }, { name = 'Kill Tacular', soundId = 'rbxassetid://6818527070' }, { name = 'Kill Imanjaro', soundId = 'rbxassetid://6818527258' }, { name = 'Kill Tastrophe', soundId = 'rbxassetid://6818526916' }, { name = 'Kill Pocalypse', soundId = 'rbxassetid://6818527144' }, { name = 'Kill Ionaire', soundId = 'rbxassetid://6818527200' }, { name = 'Killing Spree', soundId = 'rbxassetid://6822465178' }, { name = 'Killing Frenzy', soundId = 'rbxassetid://6822465319' }, { name = 'Carrier Kill', soundId = 'rbxassetid://7139067012' }, { name = 'Clutch Kill', soundId = 'rbxassetid://7379106527' }, { name = 'Taco Bell', soundId = 'rbxassetid://5689199277' }, { name = 'Kombat', soundId = 'rbxassetid://8527433497' }, { name = 'Headshot', soundId = 'rbxassetid://8418469749' }, { name = 'Elevator', soundId = 'rbxassetid://8322227967' }, }, SoundAssetCache = {}, SoundDropdownOptionIds = { 'P1S15D1', 'P1S15D2', 'P1S16D1', 'P1S17D1', }, CustomSoundsPath = 'ZkxHub/RIVALS Custom Sounds.json', CustomSoundsCacheFolder = 'ZkxHub/RIVALS Sounds', MaterialMap = { Ghost = Enum.Material.ForceField, Flat = Enum.Material.Neon, Foil = Enum.Material.Foil, Custom = Enum.Material.SmoothPlastic, Reflective = Enum.Material.Glass, }, MaterialNames = { 'Ghost', 'Flat', 'Foil', 'Custom', 'Reflective' }, TracerStyleNames = { 'Plain', 'Beam', 'Lightning', 'Trail', 'Zigzag', 'Heartrate', 'Chain', 'Glitch', 'Swirl' }, TracerStyles = { Plain = '', Beam = 'rbxassetid://12781852245', Lightning = 'rbxassetid://446111271', Trail = 'rbxassetid://6419989824', Zigzag = 'rbxassetid://1274380363', Heartrate = 'rbxassetid://5830549480', Chain = 'rbxassetid://9632168658', Glitch = 'rbxassetid://8089467613', Swirl = 'rbxassetid://5638168605', }, } function RivalsRuntimeBridge.CombatFeedback.ReadToggle(optionId) return Toggles and Toggles[optionId] and Toggles[optionId].Value == true end function RivalsRuntimeBridge.CombatFeedback.ReadOption(optionId, fallback) local option = Options and Options[optionId] if option == nil or option.Value == nil then return fallback end return option.Value end function RivalsRuntimeBridge.CombatFeedback.IsEnabled() local feedback = RivalsRuntimeBridge.CombatFeedback return feedback.ReadToggle('P1S15T1') or feedback.ReadToggle('P1S16T1') or feedback.ReadToggle('P1S18T1') or feedback.HitLogWanted() end function RivalsRuntimeBridge.CombatFeedback.HitLogWanted() local hitLog = RivalsRuntimeBridge.HitLog return hitLog ~= nil and hitLog.Wanted() end function RivalsRuntimeBridge.CombatFeedback.Trim(value) if type(value) ~= 'string' then return '' end return value:match('^%s*(.-)%s*$') or '' end function RivalsRuntimeBridge.CombatFeedback.NormalizeSoundSource(value) local feedback = RivalsRuntimeBridge.CombatFeedback local source = feedback.Trim(value) if source == '' then return nil end if source:match('^%d+$') then return 'rbxassetid://' .. source end return source end function RivalsRuntimeBridge.CombatFeedback.EnsureCustomSoundsLoaded() local feedback = RivalsRuntimeBridge.CombatFeedback if feedback.CustomSoundsLoaded then return end feedback.CustomSoundsLoaded = true feedback.CustomSounds = {} if type(isfile) ~= 'function' or not isfile(feedback.CustomSoundsPath) then return end local ok, decoded = pcall( function() return game:GetService('HttpService'):JSONDecode(readfile(feedback.CustomSoundsPath)) end ) local records = ok and type(decoded) == 'table' and (decoded.sounds or decoded) or nil if type(records) ~= 'table' then return end local seen = {} for _, record in ipairs(records) do if type(record) == 'table' then local name = feedback.Trim(record.name) local source = feedback.NormalizeSoundSource(record.soundId or record.source) if name ~= '' and source and not seen[name] then seen[name] = true table.insert(feedback.CustomSounds, { name = name, soundId = source, }) end end end table.sort(feedback.CustomSounds, function(left, right) return left.name < right.name end) end function RivalsRuntimeBridge.CombatFeedback.SaveCustomSounds() local feedback = RivalsRuntimeBridge.CombatFeedback if type(writefile) ~= 'function' then return false end local ok = pcall(function() if type(makefolder) == 'function' and type(isfolder) == 'function' and not isfolder('ZkxHub') then makefolder('ZkxHub') end writefile( feedback.CustomSoundsPath, game:GetService('HttpService'):JSONEncode({ version = 1, sounds = feedback.CustomSounds, }) ) end) return ok end function RivalsRuntimeBridge.CombatFeedback.ResolveUserSoundNames() local feedback = RivalsRuntimeBridge.CombatFeedback feedback.EnsureCustomSoundsLoaded() local names = {} for _, record in ipairs(feedback.CustomSounds) do table.insert(names, record.name) end return names end function RivalsRuntimeBridge.CombatFeedback.ResolveCustomSoundNames() local feedback = RivalsRuntimeBridge.CombatFeedback local userNames = feedback.ResolveUserSoundNames() local seen = {} for _, name in ipairs(userNames) do seen[name] = true end local names = {} for _, record in ipairs(feedback.BuiltInSounds) do if not seen[record.name] then table.insert(names, record.name) end end for _, name in ipairs(userNames) do table.insert(names, name) end return names end function RivalsRuntimeBridge.CombatFeedback.RefreshCustomSoundDropdowns() local feedback = RivalsRuntimeBridge.CombatFeedback local names = feedback.ResolveCustomSoundNames() for _, optionId in ipairs(feedback.SoundDropdownOptionIds) do local option = Options and Options[optionId] if option and type(option.SetValues) == 'function' then option:SetValues(optionId == 'P1S17D1' and feedback.ResolveUserSoundNames() or names) end end end function RivalsRuntimeBridge.CombatFeedback.AddCustomSound(nameValue, sourceValue) local feedback = RivalsRuntimeBridge.CombatFeedback feedback.EnsureCustomSoundsLoaded() local name = feedback.Trim(nameValue) local source = feedback.NormalizeSoundSource(sourceValue) if name == '' or not source then return false end for _, record in ipairs(feedback.CustomSounds) do if record.name == name then return false end end for _, record in ipairs(feedback.BuiltInSounds) do if record.name == name then return false end end local previous = table.clone(feedback.CustomSounds) table.insert(feedback.CustomSounds, { name = name, soundId = source }) table.sort(feedback.CustomSounds, function(left, right) return left.name < right.name end) if not feedback.SaveCustomSounds() then feedback.CustomSounds = previous return false end feedback.RefreshCustomSoundDropdowns() return true end function RivalsRuntimeBridge.CombatFeedback.RemoveCustomSound(nameValue) local feedback = RivalsRuntimeBridge.CombatFeedback feedback.EnsureCustomSoundsLoaded() local name = feedback.Trim(nameValue) if name == '' then return false end local index = nil for recordIndex, record in ipairs(feedback.CustomSounds) do if record.name == name then index = recordIndex break end end if not index then return false end local previous = table.clone(feedback.CustomSounds) table.remove(feedback.CustomSounds, index) if not feedback.SaveCustomSounds() then feedback.CustomSounds = previous return false end feedback.SoundAssetCache[name] = nil for _, optionId in ipairs({ 'P1S15D1', 'P1S15D2', 'P1S16D1' }) do local option = Options and Options[optionId] if option and option.Value == name and type(option.SetValue) == 'function' then option:SetValue(nil) end end feedback.RefreshCustomSoundDropdowns() return true end function RivalsRuntimeBridge.CombatFeedback.ResolveCustomSoundSource(name) local feedback = RivalsRuntimeBridge.CombatFeedback feedback.EnsureCustomSoundsLoaded() for _, record in ipairs(feedback.CustomSounds) do if record.name == name then return record.soundId end end for _, record in ipairs(feedback.BuiltInSounds) do if record.name == name then return record.soundId end end return nil end function RivalsRuntimeBridge.CombatFeedback.HashSoundUrl(url) local hash = 5381 for index = 1, #url do hash = (hash * 33 + string.byte(url, index)) % 2147483647 end return tostring(hash) end function RivalsRuntimeBridge.CombatFeedback.ResolveSoundAsset(name) local feedback = RivalsRuntimeBridge.CombatFeedback if type(name) ~= 'string' or name == '' then return nil end local cached = feedback.SoundAssetCache[name] if cached ~= nil then return cached or nil end local source = feedback.ResolveCustomSoundSource(name) if not source then feedback.SoundAssetCache[name] = false return nil end if source:match('^rbxassetid://') or source:match('^rbxasset://') then feedback.SoundAssetCache[name] = source return source end local customAsset = type(getcustomasset) == 'function' and getcustomasset or (type(getsynasset) == 'function' and getsynasset or nil) if source:match('^https?://') then if type(writefile) ~= 'function' or type(customAsset) ~= 'function' then feedback.SoundAssetCache[name] = false return nil end local extension = source:match('(%.[%w]+)[%?#]') or source:match('(%.[%w]+)$') or '.mp3' extension = extension:lower() if extension ~= '.mp3' and extension ~= '.ogg' and extension ~= '.wav' then extension = '.mp3' end local cachePath = string.format('%s/%s%s', feedback.CustomSoundsCacheFolder, feedback.HashSoundUrl(source), extension) local ok = pcall(function() if type(makefolder) == 'function' and type(isfolder) == 'function' then if not isfolder('ZkxHub') then makefolder('ZkxHub') end if not isfolder(feedback.CustomSoundsCacheFolder) then makefolder(feedback.CustomSoundsCacheFolder) end end if type(isfile) ~= 'function' or not isfile(cachePath) then local requestFunction = type(request) == 'function' and request or (type(http_request) == 'function' and http_request or nil) local body = nil if requestFunction then local response = requestFunction({ Url = source, Method = 'GET' }) local status = tonumber(response and (response.StatusCode or response.Status)) or 0 if status < 200 or status >= 300 then error('download failed') end body = response.Body else body = game:HttpGet(source) end if type(body) ~= 'string' or body == '' then error('empty download') end writefile(cachePath, body) end end) if ok then local assetOk, asset = pcall(customAsset, cachePath) if assetOk and type(asset) == 'string' and asset ~= '' then feedback.SoundAssetCache[name] = asset return asset end end feedback.SoundAssetCache[name] = false return nil end if type(customAsset) == 'function' and type(isfile) == 'function' and isfile(source) then local ok, asset = pcall(customAsset, source) if ok and type(asset) == 'string' and asset ~= '' then feedback.SoundAssetCache[name] = asset return asset end end feedback.SoundAssetCache[name] = false return nil end function RivalsRuntimeBridge.CombatFeedback.PlaySound(name, volume, pitch) local feedback = RivalsRuntimeBridge.CombatFeedback local asset = feedback.ResolveSoundAsset(name) if not asset then return end local sound = Instance.new('Sound') sound.Name = 'ZkxHubCombatFeedback' sound.SoundId = asset sound.Volume = math.max(0, tonumber(volume) or 1) sound.PlaybackSpeed = math.max(0.01, tonumber(pitch) or 1) sound.Parent = game:GetService('SoundService') game:GetService('Debris'):AddItem(sound, 20) sound:Play() end function RivalsRuntimeBridge.CombatFeedback.CreateChams(character, color, transparency, duration, materialName) local feedback = RivalsRuntimeBridge.CombatFeedback if typeof(character) ~= 'Instance' or not character:IsA('Model') then return end local model = Instance.new('Model') model.Name = 'ZkxHubCombatFeedbackChams' local parts = {} local material = feedback.MaterialMap[materialName] or Enum.Material.ForceField local baseTransparency = math.clamp(tonumber(transparency) or 0, 0, 1) for _, part in ipairs(character:GetDescendants()) do if part:IsA('BasePart') and part.Name ~= 'HumanoidRootPart' and part.Transparency < 1 then local ok, ghost = pcall(function() return part:Clone() end) if not ok or not ghost or not ghost:IsA('BasePart') then ghost = Instance.new('Part') ghost.Size = part.Size ghost.Shape = part:IsA('Part') and part.Shape or Enum.PartType.Block end for _, descendant in ipairs(ghost:GetDescendants()) do if not descendant:IsA('DataModelMesh') then descendant:Destroy() end end ghost.Name = part.Name ghost.Anchored = true ghost.CanCollide = false ghost.CanQuery = false ghost.CanTouch = false ghost.CastShadow = false ghost.Color = typeof(color) == 'Color3' and color or Color3.fromRGB(255, 0, 0) ghost.Material = material ghost.Transparency = baseTransparency ghost.CFrame = part.CFrame pcall(function() ghost.MaterialVariant = '' end) ghost.Parent = model table.insert(parts, { Source = part, Ghost = ghost }) end end if #parts == 0 then model:Destroy() return end model.Parent = Workspace table.insert(feedback.Chams, { Model = model, Parts = parts, StartedAt = tick(), Duration = math.max(0.05, tonumber(duration) or 0.8), Transparency = baseTransparency, }) end function RivalsRuntimeBridge.CombatFeedback.ResolveItemByObjectId(objectId) local function findIn(fighter) if type(fighter) ~= 'table' then return nil end for _, item in pairs(rawget(fighter, 'Items') or {}) do local data = type(item) == 'table' and rawget(item, 'Data') or nil local itemObjectId = type(data) == 'table' and rawget(data, 'ObjectID') or nil if (itemObjectId or ReadItemValue(item, 'ObjectID')) == objectId then return item end end return nil end local item = findIn(ResolveLocalFighter()) if item then return item end local fighterController = ResolveFighterController() for _, fighter in pairs(fighterController and rawget(fighterController, 'Objects') or {}) do item = findIn(fighter) if item then return item end end return nil end function RivalsRuntimeBridge.CombatFeedback.ResolveTracerSpringClass() local feedback = RivalsRuntimeBridge.CombatFeedback if feedback.TracerSpringClass ~= nil then return feedback.TracerSpringClass or nil end local modules = ReplicatedStorage:FindFirstChild('Modules') local springModule = modules and modules:FindFirstChild('Spring') local ok, springClass = false, nil if springModule then ok, springClass = pcall(require, springModule) end feedback.TracerSpringClass = ok and type(springClass) == 'table' and springClass or false return feedback.TracerSpringClass or nil end function RivalsRuntimeBridge.CombatFeedback.CreateTracer(startPosition, finishPosition) local feedback = RivalsRuntimeBridge.CombatFeedback if typeof(startPosition) ~= 'Vector3' or typeof(finishPosition) ~= 'Vector3' then return end local springClass = feedback.ResolveTracerSpringClass() if not springClass or type(springClass.new) ~= 'function' then return end while #feedback.Tracers >= feedback.MaxTracers do local oldest = table.remove(feedback.Tracers, 1) if oldest then pcall(function() oldest.Beam:Destroy() oldest.Near:Destroy() oldest.Far:Destroy() end) end end local near = Instance.new('Attachment') near.Position = startPosition near.Parent = Workspace.Terrain local far = Instance.new('Attachment') far.Position = startPosition far.Parent = Workspace.Terrain local beam = Instance.new('Beam') beam.Name = 'ZkxHubBulletTracer' beam.Attachment0 = near beam.Attachment1 = far beam.Color = ColorSequence.new(feedback.ReadOption('P1S18C1', Color3.fromRGB(120, 220, 255))) beam.Width0 = feedback.ReadOption('P1S18S1', 0.06) beam.Width1 = beam.Width0 beam.FaceCamera = true beam.LightEmission = feedback.ReadOption('P1S18S4', 1) beam.LightInfluence = 0 local glow = math.clamp(feedback.ReadOption('P1S18S5', 1), 1, 25) beam.Brightness = 10000 ^ ((glow - 1) / 24) beam.Transparency = NumberSequence.new(0) local style = feedback.ReadOption('P1S18D1', 'Beam') local texture = feedback.TracerStyles[style] or '' if texture ~= '' then beam.Texture = texture beam.TextureLength = feedback.ReadOption('P1S18S2', 4) beam.TextureSpeed = feedback.ReadOption('P1S18S3', 1) end beam.Parent = Workspace.Terrain local spring = springClass.new(0) spring.Speed = feedback.ReadOption('P1S18S8', 18) spring.Damper = feedback.ReadOption('P1S18S9', 0.7) spring.Target = 1 local expand = feedback.ReadToggle('P1S18T2') if not expand then spring.Position = 1 far.Position = finishPosition end table.insert(feedback.Tracers, { Beam = beam, Near = near, Far = far, Spring = spring, Start = startPosition, Finish = finishPosition, CreatedAt = tick(), Lifetime = math.max(0.1, feedback.ReadOption('P1S18S6', 0.6)), FadeTime = math.max(0, feedback.ReadOption('P1S18S7', 0.35)), Expand = expand, }) end function RivalsRuntimeBridge.CombatFeedback.HandleShootEffect(objectId, raycastResults) local feedback = RivalsRuntimeBridge.CombatFeedback if not feedback.ReadToggle('P1S18T1') or type(raycastResults) ~= 'table' then return end local item = feedback.ResolveItemByObjectId(objectId) local viewModel = type(item) == 'table' and rawget(item, 'ViewModel') or nil if type(viewModel) ~= 'table' or type(viewModel.GetMuzzlePosition) ~= 'function' then return end local ok, muzzlePosition = pcall(viewModel.GetMuzzlePosition, viewModel) local camera = Workspace.CurrentCamera if not ok or typeof(muzzlePosition) ~= 'Vector3' or not camera then return end local startPosition = muzzlePosition + camera.CFrame.LookVector for _, encodedResult in pairs(raycastResults) do local finishPosition = type(encodedResult) == 'table' and encodedResult[utf8.char(0)] or nil if typeof(finishPosition) == 'Vector3' then feedback.CreateTracer(startPosition, finishPosition) end end end function RivalsRuntimeBridge.CombatFeedback.UpdateTracers() local feedback = RivalsRuntimeBridge.CombatFeedback local now = tick() local currentColor = feedback.ReadOption('P1S18C1', Color3.fromRGB(120, 220, 255)) for index = #feedback.Tracers, 1, -1 do local tracer = feedback.Tracers[index] local age = now - tracer.CreatedAt local expired = age >= tracer.Lifetime + tracer.FadeTime or not tracer.Beam or tracer.Beam.Parent == nil if expired then pcall(function() tracer.Beam:Destroy() tracer.Near:Destroy() tracer.Far:Destroy() end) table.remove(feedback.Tracers, index) else tracer.Beam.Color = ColorSequence.new(currentColor) if tracer.Expand then tracer.Far.Position = tracer.Start:Lerp(tracer.Finish, math.clamp(tracer.Spring.Position, 0, 1)) end local alpha = tracer.FadeTime > 0 and math.clamp((age - tracer.Lifetime) / tracer.FadeTime, 0, 1) or 0 tracer.Beam.Transparency = NumberSequence.new(alpha) end end end function RivalsRuntimeBridge.CombatFeedback.Update() local feedback = RivalsRuntimeBridge.CombatFeedback feedback.UpdateTracers() local now = tick() for index = #feedback.Chams, 1, -1 do local cham = feedback.Chams[index] local progress = math.clamp((now - cham.StartedAt) / cham.Duration, 0, 1) if progress >= 1 or not cham.Model or cham.Model.Parent == nil then if cham.Model then pcall(function() cham.Model:Destroy() end) end table.remove(feedback.Chams, index) else for partIndex = #cham.Parts, 1, -1 do local pair = cham.Parts[partIndex] if pair.Source and pair.Source.Parent and pair.Ghost and pair.Ghost.Parent then pair.Ghost.CFrame = pair.Source.CFrame pair.Ghost.Transparency = cham.Transparency + (1 - cham.Transparency) * progress else if pair.Ghost then pair.Ghost:Destroy() end table.remove(cham.Parts, partIndex) end end end end end function RivalsRuntimeBridge.CombatFeedback.ResolveFighterObjectId(fighter) local data = type(fighter) == 'table' and rawget(fighter, 'Data') or nil local objectId = type(data) == 'table' and rawget(data, 'ObjectID') or nil return objectId or ReadFighterValue(fighter, 'ObjectID') end function RivalsRuntimeBridge.CombatFeedback.ResolveCharacterByObjectId(objectId) local feedback = RivalsRuntimeBridge.CombatFeedback local controllers = {} controllers[#controllers + 1] = ResolveFighterController() controllers[#controllers + 1] = ResolveEnemyController() for _, controller in ipairs(controllers) do for _, subject in ipairs(controller.Objects or {}) do if feedback.ResolveFighterObjectId(subject) == objectId then local entity = type(subject) == 'table' and rawget(subject, 'Entity') or nil local model = type(entity) == 'table' and rawget(entity, 'Model') or nil if typeof(model) == 'Instance' and model:IsA('Model') then return model end local player = type(subject) == 'table' and rawget(subject, 'Player') or nil if typeof(player) == 'Instance' and player:IsA('Player') then return player.Character end end end end return nil end function RivalsRuntimeBridge.CombatFeedback.Flash(color, intensity, duration) local feedback = RivalsRuntimeBridge.CombatFeedback local frame = feedback.FlashFrame if not frame or not frame.Parent then local gui = Instance.new('ScreenGui') gui.Name = 'ZkxHubFeedbackFlash' gui.IgnoreGuiInset = true gui.ResetOnSpawn = false gui.DisplayOrder = 9999 gui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling gui.Parent = LP:WaitForChild('PlayerGui') frame = Instance.new('Frame') frame.Name = 'Flash' frame.Size = UDim2.fromScale(1, 1) frame.BorderSizePixel = 0 frame.BackgroundTransparency = 1 frame.Active = false frame.Parent = gui feedback.FlashGui = gui feedback.FlashFrame = frame end frame.BackgroundColor3 = typeof(color) == 'Color3' and color or Color3.new(1, 1, 1) frame.BackgroundTransparency = 1 - math.clamp(tonumber(intensity) or 0.35, 0.02, 1) game:GetService('TweenService') :Create( frame, TweenInfo.new(math.clamp(tonumber(duration) or 0.2, 0.05, 2), Enum.EasingStyle.Quad, Enum.EasingDirection.Out), { BackgroundTransparency = 1 } ) :Play() end function RivalsRuntimeBridge.CombatFeedback.ResolveCharacterLabel(character) local player = typeof(character) == 'Instance' and Players:GetPlayerFromCharacter(character) or nil if player then return player.DisplayName ~= '' and player.DisplayName or player.Name end return typeof(character) == 'Instance' and character.Name or 'player' end function RivalsRuntimeBridge.CombatFeedback.HandleHit(damage, partName, character) local feedback = RivalsRuntimeBridge.CombatFeedback if not feedback.ReadToggle('P1S15T1') then return end if feedback.ReadToggle('PX4T1') then local colorOption = Options and Options.PX4C1 feedback.Flash(colorOption and colorOption.Value, feedback.ReadOption('PX4S1', 0.3), feedback.ReadOption('PX4S2', 0.15)) end if feedback.ReadToggle('PX4T2') and os.clock() - feedback.LastHitNotifyAt >= 0.2 then feedback.LastHitNotifyAt = os.clock() local amount = tonumber(damage) Library:Notify({ Title = partName == 'Head' and 'Headshot' or 'Hit', Description = RivalsRuntimeBridge.Extras.FeedbackText( 'Hit', character, damage, partName, feedback.ResolveCharacterLabel(character) .. (amount and (' -' .. math.floor(amount + 0.5)) or '') ), Time = 1.5, }) end if feedback.ReadToggle('P1S15T2') then local isHead = partName == 'Head' local soundName = isHead and feedback.ReadOption('P1S15D1', nil) or nil if type(soundName) ~= 'string' or soundName == '' then soundName = feedback.ReadOption('P1S15D2', nil) end feedback.PlaySound( soundName, feedback.ReadOption(isHead and 'P1S15S1' or 'P1S15S3', 1), feedback.ReadOption(isHead and 'P1S15S2' or 'P1S15S4', 1) ) end if feedback.ReadToggle('P1S15T4') then local colorOption = Options and Options.P1S15C1 feedback.CreateChams( character, colorOption and colorOption.Value, colorOption and colorOption.Transparency, feedback.ReadOption('P1S15S5', 0.8), feedback.ReadOption('P1S15D3', 'Ghost') ) end end function RivalsRuntimeBridge.CombatFeedback.HandleKill(character) local feedback = RivalsRuntimeBridge.CombatFeedback if RivalsRuntimeBridge.HitLog and RivalsRuntimeBridge.HitLog.IsDuplicateKill(character) then return end if RivalsRuntimeBridge.HitLog then RivalsRuntimeBridge.HitLog.OnKill(character) end if not feedback.ReadToggle('P1S16T1') then return end if feedback.ReadToggle('PX5T1') then local colorOption = Options and Options.PX5C1 feedback.Flash(colorOption and colorOption.Value, feedback.ReadOption('PX5S1', 0.4), feedback.ReadOption('PX5S2', 0.3)) end if feedback.ReadToggle('PX5T2') then Library:Notify({ Title = 'Eliminated', Description = RivalsRuntimeBridge.Extras.FeedbackText( 'Kill', character, nil, nil, feedback.ResolveCharacterLabel(character) ), Time = 2.5, }) end if feedback.ReadToggle('P1S16T2') then feedback.PlaySound( feedback.ReadOption('P1S16D1', nil), feedback.ReadOption('P1S16S1', 1), feedback.ReadOption('P1S16S2', 1) ) end if feedback.ReadToggle('P1S16T3') then local colorOption = Options and Options.P1S16C1 feedback.CreateChams( character, colorOption and colorOption.Value, colorOption and colorOption.Transparency, feedback.ReadOption('P1S16S3', 0.8), feedback.ReadOption('P1S16D2', 'Ghost') ) end end function RivalsRuntimeBridge.CombatFeedback.ResolveEffectPacket(packet) local tokens = RivalsRuntimeBridge.CombatFeedback.EnumTokens for index = 3, packet.n do local value = packet[index] local matched = nil if type(value) == 'string' then for name, token in pairs(tokens) do if value == token then matched = name break end end end if matched then local objectId = (index == 4) and packet[2] or packet[index - 1] if type(objectId) == 'string' then return matched, objectId, index end end end return nil end function RivalsRuntimeBridge.CombatFeedback.HandleReplicate(...) local feedback = RivalsRuntimeBridge.CombatFeedback local packet = table.pack(...) local effectName, objectId, index = feedback.ResolveEffectPacket(packet) if not effectName then return end if effectName == 'ShootEffect' and (feedback.ReadToggle('P1S18T1') or feedback.HitLogWanted()) then if feedback.HitLogWanted() then RivalsRuntimeBridge.HitLog.NoteShots(objectId, packet[index + 1]) end feedback.HandleShootEffect(objectId, packet[index + 1]) elseif effectName == 'DamageNumberEffect' and (feedback.ReadToggle('P1S15T1') or feedback.HitLogWanted()) then local localFighter = ResolveLocalFighter() if not localFighter or feedback.ResolveFighterObjectId(localFighter) ~= objectId then return end local hitPart, damage, isHead = packet[index + 1], packet[index + 2], packet[index + 3] if typeof(hitPart) ~= 'Instance' then return end local character = hitPart.Parent if typeof(character) ~= 'Instance' or not character:IsA('Model') then return end feedback.HandleHit(damage, isHead == true and 'Head' or 'Body', character) if RivalsRuntimeBridge.HitLog then RivalsRuntimeBridge.HitLog.OnHit(hitPart, damage, isHead == true, character) end elseif effectName == 'EliminationEffect' and (feedback.ReadToggle('P1S16T1') or feedback.HitLogWanted()) then local localFighter = ResolveLocalFighter() if not localFighter or feedback.ResolveFighterObjectId(localFighter) ~= objectId then return end local eliminated = packet[index + 1] if eliminated == LP or (LP.Character and eliminated == LP.Character) then return end local character = nil if typeof(eliminated) == 'Instance' then if eliminated:IsA('Player') then character = eliminated.Character elseif eliminated:IsA('Model') then character = eliminated end end feedback.HandleKill(character) elseif effectName == 'FinisherEffect' and (feedback.ReadToggle('P1S16T1') or feedback.HitLogWanted()) then if packet[index + 3] ~= LP then return end feedback.HandleKill(feedback.ResolveCharacterByObjectId(objectId)) end end function RivalsRuntimeBridge.CombatFeedback.ResolveEnumTokens() local feedback = RivalsRuntimeBridge.CombatFeedback local enumLibrary = ResolveEnumLibrary() if not enumLibrary or enumLibrary._enum_builder_complete ~= true or type(enumLibrary.ToEnum) ~= 'function' then return false end for _, enumName in ipairs({ 'ShootEffect', 'DamageNumberEffect', 'EliminationEffect', 'FinisherEffect' }) do local ok, token = pcall(enumLibrary.ToEnum, enumLibrary, enumName) if not ok or type(token) ~= 'string' then return false end feedback.EnumTokens[enumName] = token end return true end function RivalsRuntimeBridge.CombatFeedback.RefreshBinding() local feedback = RivalsRuntimeBridge.CombatFeedback if not feedback.IsEnabled() then feedback.BindingGeneration = feedback.BindingGeneration + 1 feedback.BindingPending = false feedback.BindingConnected = false if Connections then Connections:disconnect({ 'CombatFeedback_Replicate', 'CombatFeedback_ReplicateUnreliable', 'CombatFeedback_EnemyReplicate', 'CombatFeedback_EnemyReplicateUnreliable', }) end return end if feedback.BindingConnected or feedback.BindingPending or not Connections then return end feedback.BindingPending = true feedback.BindingGeneration = feedback.BindingGeneration + 1 local generation = feedback.BindingGeneration BeginReplicatedStateBoot() task.spawn(GuardRivalsCallback('CombatFeedback_Bind', function() local deadline = tick() + 30 while feedback.IsEnabled() and feedback.BindingGeneration == generation and not ReplicatedStateReady and tick() < deadline do task.wait(0.1) end if not feedback.IsEnabled() or feedback.BindingGeneration ~= generation or not ReplicatedStateReady or not feedback.ResolveEnumTokens() then feedback.BindingPending = false return end local remotes = ReplicatedStorage:FindFirstChild('Remotes') local replication = remotes and remotes:FindFirstChild('Replication') local fighter = replication and replication:FindFirstChild('Fighter') local enemy = replication and replication:FindFirstChild('Enemy') local replicate = fighter and fighter:FindFirstChild('Replicate') local unreliable = fighter and fighter:FindFirstChild('ReplicateUnreliable') local enemyReplicate = enemy and enemy:FindFirstChild('Replicate') local enemyUnreliable = enemy and enemy:FindFirstChild('ReplicateUnreliable') if not replicate and not unreliable then feedback.BindingPending = false return end Connections:disconnect({ 'CombatFeedback_Replicate', 'CombatFeedback_ReplicateUnreliable', 'CombatFeedback_EnemyReplicate', 'CombatFeedback_EnemyReplicateUnreliable', }) if replicate then Connections:register( 'CombatFeedback_Replicate', replicate.OnClientEvent:Connect(GuardRivalsCallback('CombatFeedback_Replicate', feedback.HandleReplicate)) ) end if unreliable then Connections:register( 'CombatFeedback_ReplicateUnreliable', unreliable.OnClientEvent:Connect( GuardRivalsCallback('CombatFeedback_ReplicateUnreliable', feedback.HandleReplicate) ) ) end if enemyReplicate then Connections:register( 'CombatFeedback_EnemyReplicate', enemyReplicate.OnClientEvent:Connect( GuardRivalsCallback('CombatFeedback_EnemyReplicate', feedback.HandleReplicate) ) ) end if enemyUnreliable then Connections:register( 'CombatFeedback_EnemyReplicateUnreliable', enemyUnreliable.OnClientEvent:Connect( GuardRivalsCallback('CombatFeedback_EnemyReplicateUnreliable', feedback.HandleReplicate) ) ) end feedback.BindingPending = false feedback.BindingConnected = true end)) end function RivalsRuntimeBridge.CombatFeedback.Destroy() local feedback = RivalsRuntimeBridge.CombatFeedback feedback.BindingGeneration = feedback.BindingGeneration + 1 feedback.BindingPending = false feedback.BindingConnected = false if Connections then Connections:disconnect({ 'CombatFeedback_Replicate', 'CombatFeedback_ReplicateUnreliable', 'CombatFeedback_EnemyReplicate', 'CombatFeedback_EnemyReplicateUnreliable', }) end for _, cham in ipairs(feedback.Chams) do if cham.Model then pcall(function() cham.Model:Destroy() end) end end feedback.Chams = {} for _, tracer in ipairs(feedback.Tracers) do pcall(function() tracer.Beam:Destroy() tracer.Near:Destroy() tracer.Far:Destroy() end) end feedback.Tracers = {} if feedback.FlashGui then pcall(function() feedback.FlashGui:Destroy() end) end feedback.FlashGui = nil feedback.FlashFrame = nil feedback.EnumTokens = {} end -- ====================================================================== -- Kill feed (hit log) and hit markers -- ====================================================================== -- Fed by the game's own damage replication (CombatFeedback.HandleReplicate): only hits the local fighter lands. -- The two big server hitboxes cover the whole body, so the body part is read from where the shot ended on the rig -- (the ShootEffect ray end), and the hit marker sits on that same point. do local RunService = game:GetService('RunService') local TweenService = game:GetService('TweenService') local HitLog = { Lines = {}, Markers = {}, Shots = {} } RivalsRuntimeBridge.HitLog = HitLog local PositionNames = { 'Top Left', 'Top Center', 'Top Right', 'Center Left', 'Center Right', 'Bottom Left', 'Bottom Center', 'Bottom Right', 'Above Crosshair', } -- anchor, position, text alignment, vertical alignment of the stack local PositionLayouts = { ['Top Left'] = { Vector2.new(0, 0), UDim2.new(0, 14, 0, 96), Enum.TextXAlignment.Left, Enum.VerticalAlignment.Top }, ['Top Center'] = { Vector2.new(0.5, 0), UDim2.new(0.5, 0, 0, 96), Enum.TextXAlignment.Center, Enum.VerticalAlignment.Top }, ['Top Right'] = { Vector2.new(1, 0), UDim2.new(1, -14, 0, 190), Enum.TextXAlignment.Right, Enum.VerticalAlignment.Top }, ['Center Left'] = { Vector2.new(0, 0.5), UDim2.new(0, 14, 0.5, 0), Enum.TextXAlignment.Left, Enum.VerticalAlignment.Center }, ['Center Right'] = { Vector2.new(1, 0.5), UDim2.new(1, -14, 0.5, 0), Enum.TextXAlignment.Right, Enum.VerticalAlignment.Center }, ['Bottom Left'] = { Vector2.new(0, 1), UDim2.new(0, 14, 1, -120), Enum.TextXAlignment.Left, Enum.VerticalAlignment.Bottom }, ['Bottom Center'] = { Vector2.new(0.5, 1), UDim2.new(0.5, 0, 1, -120), Enum.TextXAlignment.Center, Enum.VerticalAlignment.Bottom }, ['Bottom Right'] = { Vector2.new(1, 1), UDim2.new(1, -14, 1, -120), Enum.TextXAlignment.Right, Enum.VerticalAlignment.Bottom }, ['Above Crosshair'] = { Vector2.new(0.5, 1), UDim2.new(0.5, 0, 0.5, -80), Enum.TextXAlignment.Center, Enum.VerticalAlignment.Bottom }, } -- R15 limb -> label; the hitbox parts that cover one zone are listed too local PartLabels = { Head = 'head', HitboxHead = 'head', HitboxHeadSmall = 'head', PhysicalHitboxHead = 'head', UpperTorso = 'chest', Torso = 'chest', LowerTorso = 'stomach', LeftUpperArm = 'arm', LeftLowerArm = 'arm', RightUpperArm = 'arm', RightLowerArm = 'arm', ['Left Arm'] = 'arm', ['Right Arm'] = 'arm', LeftHand = 'hand', RightHand = 'hand', LeftUpperLeg = 'leg', LeftLowerLeg = 'leg', RightUpperLeg = 'leg', RightLowerLeg = 'leg', ['Left Leg'] = 'leg', ['Right Leg'] = 'leg', LeftFoot = 'foot', RightFoot = 'foot', } local LimbNames = {} for name in pairs(PartLabels) do if not name:find('Hitbox') then LimbNames[#LimbNames + 1] = name end end table.sort(LimbNames) local BodyHitboxes = { HitboxBody = true, HitboxBodySmall = true, HumanoidRootPart = true } local function toggleOn(id) return Toggles ~= nil and Toggles[id] ~= nil and Toggles[id].Value == true end local function optionValue(id, fallback) local option = Options and Options[id] if option == nil or option.Value == nil then return fallback end return option.Value end local function colorHex(id, fallback) local color = optionValue(id, fallback) if typeof(color) ~= 'Color3' then color = fallback end return color:ToHex() end local function escapeRich(text) return (tostring(text):gsub('&', '&'):gsub('<', '<'):gsub('>', '>')) end local function guiParent() local ok, hidden = pcall(function() return type(gethui) == 'function' and gethui() or nil end) if ok and hidden then return hidden end ok, hidden = pcall(function() return game:GetService('CoreGui') end) if ok and hidden then return hidden end return LP:WaitForChild('PlayerGui') end function HitLog.Wanted() return toggleOn('ZKFT1') or toggleOn('ZKMT1') end -- ---------------------------------------------------------------- body part from the point the shot landed local function distanceToPart(part, point) local rel = part.CFrame:PointToObjectSpace(point) local half = part.Size / 2 local dx = math.max(math.abs(rel.X) - half.X, 0) local dy = math.max(math.abs(rel.Y) - half.Y, 0) local dz = math.max(math.abs(rel.Z) - half.Z, 0) return math.sqrt(dx * dx + dy * dy + dz * dz) end function HitLog.LabelAt(character, point) local best, bestDistance = nil, nil for _, name in ipairs(LimbNames) do local part = character:FindFirstChild(name) if part and part:IsA('BasePart') then local distance = distanceToPart(part, point) if bestDistance == nil or distance < bestDistance then best, bestDistance = PartLabels[name], distance end end end return best end function HitLog.PartLabel(character, hitPart, point, isHead) if isHead then return 'head' end local name = typeof(hitPart) == 'Instance' and hitPart.Name or nil local direct = name and PartLabels[name] if direct and not BodyHitboxes[name] then return direct end if typeof(point) == 'Vector3' and typeof(character) == 'Instance' then local nearest = HitLog.LabelAt(character, point) if nearest then return nearest end end return 'body' end -- ---------------------------------------------------------------- shots the local fighter fired (ray end points) function HitLog.NoteShots(objectId, raycastResults) if type(raycastResults) ~= 'table' then return end local feedback = RivalsRuntimeBridge.CombatFeedback local localFighter = ResolveLocalFighter() if not localFighter or feedback.ResolveFighterObjectId(localFighter) ~= objectId then return end local ends = {} for _, encodedResult in pairs(raycastResults) do local finish = type(encodedResult) == 'table' and encodedResult[utf8.char(0)] or nil if typeof(finish) == 'Vector3' then ends[#ends + 1] = finish end end if #ends == 0 then return end table.insert(HitLog.Shots, { At = os.clock(), Ends = ends }) while #HitLog.Shots > 6 do table.remove(HitLog.Shots, 1) end end -- Hits arrive in the order of the shots, so each hit takes the oldest ray end that is still unused and landed on that player -- (the ends of misses and of other targets are far from the player and are skipped). function HitLog.ResolvePoint(character, hitPart) local reference = nil if typeof(character) == 'Instance' then reference = character:FindFirstChild('HitboxBody') or character:FindFirstChild('HumanoidRootPart') end if not reference and typeof(hitPart) == 'Instance' and hitPart:IsA('BasePart') then reference = hitPart end local now = os.clock() for index = #HitLog.Shots, 1, -1 do local shot = HitLog.Shots[index] if now - shot.At > 1 or #shot.Ends == 0 then table.remove(HitLog.Shots, index) end end if reference then for _, shot in ipairs(HitLog.Shots) do for index, finish in ipairs(shot.Ends) do if (finish - reference.Position).Magnitude <= 4.5 then table.remove(shot.Ends, index) return finish end end end end if typeof(hitPart) == 'Instance' and hitPart:IsA('BasePart') then return hitPart.Position end return reference and reference.Position or nil end -- ---------------------------------------------------------------- the screen layer local function ensureGui() if HitLog.Gui and HitLog.Gui.Parent then return end local gui = Instance.new('ScreenGui') gui.Name = 'ZkxHubHitLog' gui.IgnoreGuiInset = true gui.ResetOnSpawn = false gui.DisplayOrder = 9997 gui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling local feed = Instance.new('Frame') feed.Name = 'Feed' feed.BackgroundTransparency = 1 feed.Size = UDim2.fromOffset(460, 260) feed.Active = false feed.Parent = gui local layout = Instance.new('UIListLayout') layout.SortOrder = Enum.SortOrder.LayoutOrder layout.Padding = UDim.new(0, 2) layout.Parent = feed local markers = Instance.new('Frame') markers.Name = 'Markers' markers.BackgroundTransparency = 1 markers.Size = UDim2.fromScale(1, 1) markers.Active = false markers.ZIndex = 2 markers.Parent = gui gui.Parent = guiParent() HitLog.Gui, HitLog.Feed, HitLog.FeedLayout, HitLog.MarkerLayer = gui, feed, layout, markers HitLog.Apply() end -- The underline of every hit closes while the hit is up, then the hit fades. Where the bar is pinned decides which way it shrinks. local UnderlineModes = { ['Close From Sides'] = 0.5, ['Shrink Left'] = 0, ['Shrink Right'] = 1, ['Static'] = false, } HitLog.UnderlineModeNames = { 'Close From Sides', 'Shrink Left', 'Shrink Right', 'Static' } -- 0.5 = pinned in the middle (closes from both sides), 0 / 1 = pinned to one end, false = does not move function HitLog.UnderlineAnchor(mode) local anchor = UnderlineModes[mode] if anchor == nil then anchor = UnderlineModes['Close From Sides'] end return anchor end local RAINBOW = ColorSequence.new({ ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 85, 85)), ColorSequenceKeypoint.new(0.2, Color3.fromRGB(255, 190, 60)), ColorSequenceKeypoint.new(0.4, Color3.fromRGB(120, 255, 120)), ColorSequenceKeypoint.new(0.6, Color3.fromRGB(70, 210, 255)), ColorSequenceKeypoint.new(0.8, Color3.fromRGB(150, 110, 255)), ColorSequenceKeypoint.new(1, Color3.fromRGB(255, 90, 200)), }) local function horizontalFor(alignment) if alignment == Enum.TextXAlignment.Left then return Enum.HorizontalAlignment.Left end if alignment == Enum.TextXAlignment.Right then return Enum.HorizontalAlignment.Right end return Enum.HorizontalAlignment.Center end function HitLog.Apply() local feed, layout = HitLog.Feed, HitLog.FeedLayout if not feed or not feed.Parent then return end local spec = PositionLayouts[optionValue('ZKFD1', 'Center Left')] or PositionLayouts['Center Left'] feed.AnchorPoint, feed.Position = spec[1], spec[2] layout.HorizontalAlignment = horizontalFor(spec[3]) layout.VerticalAlignment = spec[4] local size = math.floor(tonumber(optionValue('ZKFS3', 15)) or 15) for _, line in ipairs(HitLog.Lines) do if line.Root.Parent then line.Label.TextXAlignment = spec[3] line.Label.TextSize = size line.Layout.HorizontalAlignment = horizontalFor(spec[3]) end end end local function styleLabel(label) local face = Library.Font if typeof(face) == 'Font' then label.FontFace = face else label.Font = Enum.Font.Code end end local function removeLine(line) for index, candidate in ipairs(HitLog.Lines) do if candidate == line then table.remove(HitLog.Lines, index) break end end if line.Root.Parent then line.Root:Destroy() end end local function play(instance, goal, seconds, style) TweenService:Create(instance, TweenInfo.new(seconds, style or Enum.EasingStyle.Quad, Enum.EasingDirection.Out), goal):Play() end -- Rainbow text: every letter gets its own hue and the hues travel along the text. The colour spans of the normal text (name, body -- part, damage, health) are dropped, the rainbow replaces them. A line like that is rebuilt about 30 times a second while it is up. function HitLog.PlainText(rich) return (tostring(rich):gsub('<[^>]*>', '')) end function HitLog.RainbowText(plain, phase) local out, shown, i = {}, 0, 1 while i <= #plain do local unit = plain:match('^&%a+;', i) or plain:match('^[\1-\127\194-\244][\128-\191]*', i) or plain:sub(i, i) i = i + #unit if unit:match('^%s+$') then out[#out + 1] = unit else shown = shown + 1 out[#out + 1] = string.format('%s', Color3.fromHSV((phase - shown * 0.035) % 1, 0.75, 1):ToHex(), unit) end end return table.concat(out) end function HitLog.RainbowPhase() return (os.clock() * (tonumber(optionValue('ZKFS7', 3)) or 3) * 0.12) % 1 end HitLog.RainbowAt = 0 function HitLog.StepRainbow() local now = os.clock() if now - HitLog.RainbowAt < 1 / 30 then return end HitLog.RainbowAt = now local phase = HitLog.RainbowPhase() local live = false for _, line in ipairs(HitLog.Lines) do if line.Plain and line.Label.Parent then live = true line.Label.Text = HitLog.RainbowText(line.Plain, phase) end end if not live then HitLog.RainbowOn = false if Connections then Connections:disconnect({ 'HitLog_Rainbow' }) end end end function HitLog.EnsureRainbow() if HitLog.RainbowOn or not Connections then return end HitLog.RainbowOn = true Connections:register('HitLog_Rainbow', RunService.RenderStepped:Connect(GuardRivalsCallback('HitLog_Rainbow', HitLog.StepRainbow))) end -- One feed entry: the text, an optional dark panel behind it and an underline that closes before the entry fades out. function HitLog.PushLine(text) ensureGui() local spec = PositionLayouts[optionValue('ZKFD1', 'Center Left')] or PositionLayouts['Center Left'] local panelOn = toggleOn('ZKFT6') local panelTransparency = math.clamp(tonumber(optionValue('ZKFS5', 0.45)) or 0.45, 0, 1) local underlineOn = toggleOn('ZKFT3') local anchor = HitLog.UnderlineAnchor(optionValue('ZKFD2', 'Close From Sides')) local thickness = math.clamp(math.floor(tonumber(optionValue('ZKFS4', 2)) or 2), 1, 8) local fadeTime = math.clamp(tonumber(optionValue('ZKFS6', 0.4)) or 0.4, 0.05, 2) local intro = toggleOn('ZKFT5') local duration = math.max(0.3, tonumber(optionValue('ZKFS2', 4)) or 4) local introTime = intro and 0.22 or 0 local root = Instance.new('Frame') root.Name = 'Line' root.AutomaticSize = Enum.AutomaticSize.XY root.Size = UDim2.fromOffset(0, 0) root.BorderSizePixel = 0 root.BackgroundColor3 = Color3.fromRGB(12, 12, 14) root.BackgroundTransparency = panelOn and (intro and 1 or panelTransparency) or 1 HitLog.LineCounter = (HitLog.LineCounter or 0) + 1 root.LayoutOrder = HitLog.LineCounter if panelOn then local padding = Instance.new('UIPadding') padding.PaddingLeft, padding.PaddingRight = UDim.new(0, 7), UDim.new(0, 7) padding.PaddingTop, padding.PaddingBottom = UDim.new(0, 3), UDim.new(0, 3) padding.Parent = root end local layout = Instance.new('UIListLayout') layout.SortOrder = Enum.SortOrder.LayoutOrder layout.Padding = UDim.new(0, 2) layout.HorizontalAlignment = horizontalFor(spec[3]) layout.Parent = root local label = Instance.new('TextLabel') label.Name = 'Text' label.BackgroundTransparency = 1 label.AutomaticSize = Enum.AutomaticSize.XY label.Size = UDim2.fromOffset(0, 0) label.RichText = true label.TextWrapped = false label.TextXAlignment = spec[3] label.TextSize = math.floor(tonumber(optionValue('ZKFS3', 15)) or 15) label.TextColor3 = Color3.new(1, 1, 1) label.TextStrokeColor3 = Color3.new(0, 0, 0) label.TextStrokeTransparency = intro and 1 or 0.35 label.TextTransparency = intro and 1 or 0 local plain = nil if toggleOn('ZKFT8') then plain = HitLog.PlainText(text) label.Text = HitLog.RainbowText(plain, HitLog.RainbowPhase()) else label.Text = text end label.LayoutOrder = 1 styleLabel(label) label.Parent = root local bar = nil if underlineOn then local holder = Instance.new('Frame') holder.Name = 'UnderlineHolder' holder.BackgroundTransparency = 1 holder.BorderSizePixel = 0 holder.Size = UDim2.new(1, 0, 0, thickness) holder.LayoutOrder = 2 holder.Parent = root local pin = anchor == false and 0.5 or anchor bar = Instance.new('Frame') bar.Name = 'Underline' bar.BorderSizePixel = 0 bar.AnchorPoint = Vector2.new(pin, 0) bar.Position = UDim2.new(pin, 0, 0, 0) bar.Size = UDim2.new(intro and 0 or 1, 0, 1, 0) bar.BackgroundColor3 = toggleOn('ZKFT4') and Color3.new(1, 1, 1) or (optionValue('ZKFC5', Color3.fromRGB(125, 85, 255))) if toggleOn('ZKFT4') then local gradient = Instance.new('UIGradient') gradient.Color = RAINBOW gradient.Parent = bar end bar.Parent = holder end root.Parent = HitLog.Feed local line = { Root = root, Label = label, Layout = layout, Bar = bar, Plain = plain } table.insert(HitLog.Lines, line) if plain then HitLog.EnsureRainbow() end local maxLines = math.floor(tonumber(optionValue('ZKFS1', 5)) or 5) while #HitLog.Lines > maxLines do removeLine(HitLog.Lines[1]) end -- in: text fades up, the panel fills, the underline is drawn out to full width if intro then play(label, { TextTransparency = 0, TextStrokeTransparency = 0.35 }, 0.2) if panelOn then play(root, { BackgroundTransparency = panelTransparency }, 0.2) end if bar then play(bar, { Size = UDim2.new(1, 0, 1, 0) }, introTime, Enum.EasingStyle.Quint) end end -- the underline closes for the rest of the time the hit is on screen if bar and anchor ~= false then task.delay(introTime, function() if not bar.Parent then return end play(bar, { Size = UDim2.new(0, 0, 1, 0) }, math.max(0.1, duration - introTime), Enum.EasingStyle.Linear) end) end -- out: once it has closed, the whole entry fades task.delay(duration, function() if not root.Parent then return end play(label, { TextTransparency = 1, TextStrokeTransparency = 1 }, fadeTime) if panelOn then play(root, { BackgroundTransparency = 1 }, fadeTime) end if bar then play(bar, { BackgroundTransparency = 1 }, fadeTime) end task.delay(fadeTime + 0.05, function() removeLine(line) end) end) return line end -- ---------------------------------------------------------------- health remaining -- Reading Humanoid.Health a moment after the damage packet is wrong in rapid fire (the next hit has landed by then), so each -- target keeps a running total: the first hit of a burst takes the health bar as it is (the target is assumed to have been -- at full health, so a bar that already shows the loss is the answer, otherwise the damage is taken off it), every later hit -- takes its damage off the previous result. The total is dropped after 2.5 s without a hit. HitLog.Chain = setmetatable({}, { __mode = "k" }) function HitLog.HealthAfter(humanoid, damage) local amount = math.max(0, tonumber(damage) or 0) local now = os.clock() local chain = HitLog.Chain[humanoid] local remaining if chain and now - chain.At < 2.5 then remaining = chain.Remaining - amount else local health, maxHealth = humanoid.Health, humanoid.MaxHealth if health <= maxHealth - amount + 0.5 then remaining = health else remaining = health - amount end end remaining = math.max(0, remaining) HitLog.Chain[humanoid] = { Remaining = remaining, At = now } return remaining end -- ---------------------------------------------------------------- hit text function HitLog.HitText(name, part, damage, health) local text = string.format( 'Hit %s in the %s for %d damage', colorHex('ZKFC1', Color3.fromRGB(150, 255, 150)), escapeRich(name), colorHex('ZKFC2', Color3.fromRGB(120, 230, 120)), escapeRich(part), colorHex('ZKFC3', Color3.fromRGB(255, 170, 60)), math.floor((tonumber(damage) or 0) + 0.5) ) local showHealth = Toggles == nil or Toggles.ZKFT7 == nil or Toggles.ZKFT7.Value == true if health ~= nil and showHealth then text = text .. string.format(' (%d health remaining)', colorHex('ZKFC4', Color3.fromRGB(255, 255, 255)), math.max(0, math.floor(health + 0.5))) end return text end function HitLog.KillText(name) return string.format( 'Eliminated %s', colorHex('ZKFC3', Color3.fromRGB(255, 170, 60)), colorHex('ZKFC1', Color3.fromRGB(150, 255, 150)), escapeRich(name) ) end -- ---------------------------------------------------------------- hit markers local MarkerBars = { { -1, -1, -45 }, { 1, -1, 45 }, { -1, 1, 45 }, { 1, 1, -45 }, } local function buildMarker(size) local root = Instance.new('Frame') root.Name = 'Marker' root.BackgroundTransparency = 1 root.AnchorPoint = Vector2.new(0.5, 0.5) root.Size = UDim2.fromOffset(size * 2, size * 2) root.Visible = false local bars = {} for _, spec in ipairs(MarkerBars) do local bar = Instance.new('Frame') bar.BorderSizePixel = 0 bar.AnchorPoint = Vector2.new(0.5, 0.5) bar.Size = UDim2.fromOffset(math.max(2, math.floor(size / 5)), math.floor(size * 0.62)) bar.Position = UDim2.new(0.5, spec[1] * size * 0.42, 0.5, spec[2] * size * 0.42) bar.Rotation = spec[3] local stroke = Instance.new('UIStroke') stroke.Thickness = 1 stroke.Color = Color3.new(0, 0, 0) stroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border stroke.Parent = bar bar.Parent = root bars[#bars + 1] = { Bar = bar, Stroke = stroke } end return root, bars end local function stepMarkers() local camera = Workspace.CurrentCamera local now = os.clock() for index = #HitLog.Markers, 1, -1 do local marker = HitLog.Markers[index] local age = now - marker.At if age >= marker.Duration or not marker.Root.Parent then marker.Root:Destroy() table.remove(HitLog.Markers, index) else local screen = nil if marker.Crosshair then if camera then screen = Vector2.new(camera.ViewportSize.X / 2, camera.ViewportSize.Y / 2) end elseif camera then local world = marker.World if marker.Part and marker.Part.Parent then world = marker.Part.Position + marker.Offset end local projected = camera:WorldToViewportPoint(world) if projected.Z > 0 then screen = Vector2.new(projected.X, projected.Y) end end if screen then marker.Root.Visible = true marker.Root.Position = UDim2.fromOffset(screen.X, screen.Y) local pop = 1 + 0.4 * math.max(0, 1 - age / 0.12) marker.Root.Size = UDim2.fromOffset(marker.Size * 2 * pop, marker.Size * 2 * pop) local fade = math.clamp((age / marker.Duration - 0.45) / 0.55, 0, 1) for _, piece in ipairs(marker.Bars) do piece.Bar.BackgroundTransparency = fade piece.Stroke.Transparency = fade end else marker.Root.Visible = false end end end if #HitLog.Markers == 0 and Connections then Connections:disconnect({ 'HitLog_Render' }) end end function HitLog.AddMarker(world, part, isHead) ensureGui() local size = math.clamp(tonumber(optionValue('ZKMS1', 12)) or 12, 5, 40) local duration = math.clamp(tonumber(optionValue('ZKMS2', 0.5)) or 0.5, 0.15, 3) local root, bars = buildMarker(size) local color = optionValue(isHead and 'ZKMC2' or 'ZKMC1', isHead and Color3.fromRGB(255, 80, 80) or Color3.new(1, 1, 1)) if typeof(color) ~= 'Color3' then color = Color3.new(1, 1, 1) end for _, piece in ipairs(bars) do piece.Bar.BackgroundColor3 = color end root.Parent = HitLog.MarkerLayer local marker = { Root = root, Bars = bars, At = os.clock(), Duration = duration, Size = size, World = world, Part = typeof(part) == 'Instance' and part:IsA('BasePart') and part or nil, Crosshair = optionValue('ZKMD1', 'Hit Location') == 'Crosshair' or typeof(world) ~= 'Vector3', } marker.Offset = (marker.Part and typeof(world) == 'Vector3') and (world - marker.Part.Position) or Vector3.zero table.insert(HitLog.Markers, marker) if Connections and #HitLog.Markers == 1 then Connections:register('HitLog_Render', RunService.RenderStepped:Connect(GuardRivalsCallback('HitLog_Render', stepMarkers))) end return marker end -- ---------------------------------------------------------------- events from CombatFeedback -- ---------------------------------------------------------------- where the shot was aimed -- The damage packet only names the two big server hitboxes, so it cannot tell a leg from the torso and always sits at the middle of the -- body. The place is worked out here instead: (1) the part the aimbot / silent aim was on, else (2) the first part of the player the -- crosshair ray touched in the last moments (camera rays are sampled every frame while the feed or the markers are on), else (3) the -- end of the ray the game announced, else (4) nothing: it then says "body" and puts the marker on the hitbox. HitLog.Rays = {} function HitLog.NoteRay(origin, direction) local now = os.clock() table.insert(HitLog.Rays, { Origin = origin, Direction = direction.Unit, At = now }) while #HitLog.Rays > 0 and now - HitLog.Rays[1].At > 0.45 do table.remove(HitLog.Rays, 1) end end function HitLog.SampleCamera() local camera = Workspace.CurrentCamera if not camera then return end -- the crosshair is the middle of the screen or the mouse, whichever the hub aims with local ok, pointer = pcall(function() return AimbotBridge.GetAimbotPointerPosition() end) if ok and typeof(pointer) == 'Vector2' then local ray = camera:ViewportPointToRay(pointer.X, pointer.Y) HitLog.NoteRay(ray.Origin, ray.Direction) else HitLog.NoteRay(camera.CFrame.Position, camera.CFrame.LookVector) end end function HitLog.EnsureSampler() if not Connections then return end if HitLog.Wanted() then if not HitLog.SamplerOn then HitLog.SamplerOn = true Connections:register('HitLog_Camera', RunService.RenderStepped:Connect(GuardRivalsCallback('HitLog_Camera', HitLog.SampleCamera))) end elseif HitLog.SamplerOn then HitLog.SamplerOn = false HitLog.Rays = {} Connections:disconnect({ 'HitLog_Camera' }) end end -- distance along a unit direction to where the ray enters the part's box (0 when it starts inside), nil when it misses function HitLog.RayBox(origin, direction, part) local cframe = part.CFrame local localOrigin = cframe:PointToObjectSpace(origin) local localDirection = cframe:VectorToObjectSpace(direction) local half = part.Size / 2 local tMin, tMax = 0, math.huge for _, axis in ipairs({ 'X', 'Y', 'Z' }) do local o, d, h = localOrigin[axis], localDirection[axis], half[axis] if math.abs(d) < 1e-9 then if math.abs(o) > h then return nil end else local t1, t2 = (-h - o) / d, (h - o) / d if t1 > t2 then t1, t2 = t2, t1 end if t1 > tMin then tMin = t1 end if t2 < tMax then tMax = t2 end if tMin > tMax then return nil end end end return tMin end -- the first part of this player the crosshair was on (newest ray first); the body hitbox when it only touched that function HitLog.AimPoint(character) if typeof(character) ~= 'Instance' then return nil end local limbs = {} for _, name in ipairs(LimbNames) do local part = character:FindFirstChild(name) if part and part:IsA('BasePart') then limbs[#limbs + 1] = part end end local body = character:FindFirstChild('HitboxBody') for index = #HitLog.Rays, 1, -1 do local ray = HitLog.Rays[index] local bestDistance, bestPart = nil, nil for _, part in ipairs(limbs) do local distance = HitLog.RayBox(ray.Origin, ray.Direction, part) if distance and (bestDistance == nil or distance < bestDistance) then bestDistance, bestPart = distance, part end end if bestPart then return ray.Origin + ray.Direction * bestDistance, bestPart end if body and body:IsA('BasePart') then local distance = HitLog.RayBox(ray.Origin, ray.Direction, body) if distance then return ray.Origin + ray.Direction * distance, body end end end return nil end -- what Aimbot / Silent Aim aimed at a moment ago, when it was on this player function HitLog.AimedTarget(character) local targeting = RivalsRuntimeBridge.AimbotTargeting local target = targeting and targeting.LastTarget if type(target) ~= 'table' or os.clock() - (targeting.LastTargetAt or 0) > 0.35 then return nil end local part = target.part if typeof(part) ~= 'Instance' or typeof(character) ~= 'Instance' or not part:IsDescendantOf(character) then return nil end local position = typeof(target.worldPosition) == 'Vector3' and target.worldPosition or part.Position return position, part end -- everything above in order; returns the point, the part it is on (nil for the hitbox fallback) and where the answer came from function HitLog.ResolveHit(character, hitPart, isHead) local point, part, source = HitLog.AimedTarget(character) if point then source = 'aimed' else point, part = HitLog.AimPoint(character) if point then source = 'crosshair' else point = HitLog.ResolvePoint(character, hitPart) local reported = typeof(hitPart) == 'Instance' and hitPart.Position or nil -- a point that is just the middle of the reported part tells nothing about where on the body it was source = (point ~= nil and reported ~= nil and point ~= reported) and 'shot' or 'unknown' part = nil end end if isHead then -- a headshot is on the head whatever else is known local head = character:FindFirstChild('Head') or character:FindFirstChild('HitboxHead') if head and not (part and (part.Name == 'Head' or part.Name == 'HitboxHead')) then point, part, source = head.Position, head, source == 'unknown' and 'head' or source end end return point, part, source end function HitLog.OnHit(hitPart, damage, isHead, character) if not HitLog.Wanted() then return end HitLog.EnsureSampler() local point, part, source = HitLog.ResolveHit(character, hitPart, isHead == true) if toggleOn('ZKMT1') then HitLog.AddMarker(point, part or hitPart, isHead == true) end if not toggleOn('ZKFT1') then return end local name = RivalsRuntimeBridge.CombatFeedback.ResolveCharacterLabel(character) -- no information about where on the body: say so instead of guessing chest or stomach local label = HitLog.PartLabel(character, part or hitPart, source ~= 'unknown' and point or nil, isHead == true) local humanoid = typeof(character) == 'Instance' and character:FindFirstChildOfClass('Humanoid') or nil local health = humanoid and HitLog.HealthAfter(humanoid, damage) or nil HitLog.PushLine(HitLog.HitText(name, label, damage, health)) end -- A kill is announced more than once (an elimination packet and a finisher packet, on up to four remotes). The same player -- inside 1.5 s, or any two kills inside 0.35 s when one of them has no resolved player, count once. HitLog.LastKill = nil function HitLog.IsDuplicateKill(character) local now = os.clock() local last = HitLog.LastKill if last then local gap = now - last.At local same = character ~= nil and last.Character == character local unknown = character == nil or last.Character == nil if (same and gap < 1.5) or (unknown and gap < 0.35) then return true end end HitLog.LastKill = { Character = character, At = now } return false end function HitLog.OnKill(character) if not toggleOn('ZKFT1') or not toggleOn('ZKFT2') then return end HitLog.PushLine(HitLog.KillText(RivalsRuntimeBridge.CombatFeedback.ResolveCharacterLabel(character))) end function HitLog.Destroy() if Connections then Connections:disconnect({ 'HitLog_Render', 'HitLog_Camera', 'HitLog_Rainbow' }) end HitLog.RainbowOn = false HitLog.SamplerOn = false HitLog.Rays = {} for _, marker in ipairs(HitLog.Markers) do pcall(function() marker.Root:Destroy() end) end HitLog.Markers = {} HitLog.Lines = {} HitLog.Shots = {} if HitLog.Gui then pcall(function() HitLog.Gui:Destroy() end) end HitLog.Gui, HitLog.Feed, HitLog.FeedLayout, HitLog.MarkerLayer = nil, nil, nil, nil end -- ---------------------------------------------------------------- menu (called from the Client Effects tab builder) function HitLog.BuildMenu(tab) local feedback = RivalsRuntimeBridge.CombatFeedback local feedGroup = tab:AddLeftGroupbox('Kill Feed') local feedToggle = feedGroup:AddToggle('ZKFT1', { Text = 'Kill Feed', Default = false, Tooltip = 'Lists your hits on screen: who, where, how much damage and how much health they have left.', }) feedToggle:OnChanged(GuardRivalsCallback('HitLog_FeedToggle', feedback.RefreshBinding)) feedToggle:OnChanged(GuardRivalsCallback('HitLog_FeedSampler', HitLog.EnsureSampler)) local feedBox = feedGroup:AddDependencyBox() feedBox:AddDropdown('ZKFD1', { Text = 'Position', Values = PositionNames, Default = 'Center Left', Multi = false, Tooltip = 'Where the list sits on the screen.', }) feedBox:AddSlider('ZKFS1', { Text = 'Lines', Default = 5, Min = 1, Max = 10, Rounding = 0, Compact = true }) feedBox:AddSlider('ZKFS2', { Text = 'Duration', Default = 4, Min = 1, Max = 10, Rounding = 1, Compact = true, Suffix = 's' }) feedBox:AddSlider('ZKFS3', { Text = 'Text Size', Default = 15, Min = 10, Max = 28, Rounding = 0, Compact = true }) feedBox:AddToggle('ZKFT2', { Text = 'Show Eliminations', Default = true, Tooltip = 'Adds an Eliminated line when you finish someone off.', }) feedBox:AddLabel('Name Color'):AddColorPicker('ZKFC1', { Default = Color3.fromRGB(150, 255, 150), Title = 'Kill Feed Name Color' }) feedBox:AddLabel('Body Part Color'):AddColorPicker('ZKFC2', { Default = Color3.fromRGB(120, 230, 120), Title = 'Kill Feed Body Part Color' }) feedBox:AddLabel('Damage Color'):AddColorPicker('ZKFC3', { Default = Color3.fromRGB(255, 170, 60), Title = 'Kill Feed Damage Color' }) feedBox:AddToggle('ZKFT7', { Text = 'Show Health Remaining', Default = true, Tooltip = 'Adds (n health remaining) to every hit.' }) feedBox:AddLabel('Health Color'):AddColorPicker('ZKFC4', { Default = Color3.fromRGB(255, 255, 255), Title = 'Kill Feed Health Color' }) feedBox:AddToggle('ZKFT8', { Text = 'Rainbow Text', Default = false, Tooltip = 'Every letter cycles through the rainbow instead of using the name / part / damage / health colors.', }) local rainbowText = feedBox:AddDependencyBox() rainbowText:AddSlider('ZKFS7', { Text = 'Rainbow Speed', Default = 3, Min = 0.5, Max = 12, Rounding = 1, Compact = true }) rainbowText:SetupDependencies({ { Toggles.ZKFT8, true } }) feedBox:AddToggle('ZKFT3', { Text = 'Underline', Default = true, Tooltip = 'A line under every hit that closes while the hit is on screen; then the hit fades.', }) feedBox:AddDropdown('ZKFD2', { Text = 'Underline Style', Values = HitLog.UnderlineModeNames, Default = 'Close From Sides', Multi = false, Tooltip = 'Close From Sides: shrinks to the middle. Shrink Left / Right: shrinks to one end. Static: stays until the hit fades.', }) feedBox:AddLabel('Underline Color'):AddColorPicker('ZKFC5', { Default = Color3.fromRGB(125, 85, 255), Title = 'Kill Feed Underline Color' }) feedBox:AddToggle('ZKFT4', { Text = 'Rainbow Underline', Default = false }) feedBox:AddSlider('ZKFS4', { Text = 'Underline Thickness', Default = 2, Min = 1, Max = 5, Rounding = 0, Compact = true, Suffix = ' px' }) feedBox:AddToggle('ZKFT6', { Text = 'Background', Default = true, Tooltip = 'A soft dark panel behind every hit.' }) feedBox:AddSlider('ZKFS5', { Text = 'Background Transparency', Default = 0.45, Min = 0, Max = 1, Rounding = 2, Compact = true }) feedBox:AddToggle('ZKFT5', { Text = 'Intro Animation', Default = true, Tooltip = 'The text fades in and the underline is drawn out before it starts closing.' }) feedBox:AddSlider('ZKFS6', { Text = 'Fade Time', Default = 0.4, Min = 0.1, Max = 1.5, Rounding = 2, Compact = true, Suffix = ' s' }) feedBox:AddButton({ Text = 'Preview', Tooltip = 'Adds two sample lines so you can see the position and colors.', Func = GuardRivalsCallback('HitLog_FeedPreview', function() HitLog.PushLine(HitLog.HitText('Taloneya', 'stomach', 50, 50)) HitLog.PushLine(HitLog.HitText('Taloneya', 'head', 61, 0)) end), }) feedBox:SetupDependencies({ { Toggles.ZKFT1, true } }) local onChange = GuardRivalsCallback('HitLog_FeedApply', HitLog.Apply) Options.ZKFD1:OnChanged(onChange) Options.ZKFS3:OnChanged(onChange) local markerGroup = tab:AddLeftGroupbox('Hit Markers') local markerToggle = markerGroup:AddToggle('ZKMT1', { Text = 'Hit Markers', Default = false, Tooltip = 'An X where your shot landed (head, body, arm, leg) when you hit someone.', }) markerToggle:OnChanged(GuardRivalsCallback('HitLog_MarkerToggle', feedback.RefreshBinding)) markerToggle:OnChanged(GuardRivalsCallback('HitLog_MarkerSampler', HitLog.EnsureSampler)) local markerBox = markerGroup:AddDependencyBox() markerBox:AddDropdown('ZKMD1', { Text = 'Anchor', Values = { 'Hit Location', 'Crosshair' }, Default = 'Hit Location', Multi = false, Tooltip = 'Hit Location puts the marker on the body part you hit; Crosshair keeps it in the middle of the screen.', }) markerBox:AddSlider('ZKMS1', { Text = 'Size', Default = 12, Min = 5, Max = 40, Rounding = 0, Compact = true }) markerBox:AddSlider('ZKMS2', { Text = 'Duration', Default = 0.5, Min = 0.15, Max = 3, Rounding = 2, Compact = true, Suffix = 's' }) markerBox:AddLabel('Marker Color'):AddColorPicker('ZKMC1', { Default = Color3.fromRGB(255, 255, 255), Title = 'Hit Marker Color' }) markerBox:AddLabel('Headshot Color'):AddColorPicker('ZKMC2', { Default = Color3.fromRGB(255, 80, 80), Title = 'Hit Marker Headshot Color' }) markerBox:AddButton({ Text = 'Preview', Tooltip = 'Drops a sample marker a little in front of you.', Func = GuardRivalsCallback('HitLog_MarkerPreview', function() local camera = Workspace.CurrentCamera if not camera then return end HitLog.AddMarker(camera.CFrame.Position + camera.CFrame.LookVector * 12 + camera.CFrame.RightVector * 2, nil, false) end), }) markerBox:SetupDependencies({ { Toggles.ZKMT1, true } }) end if type(getgenv) == 'function' then local probe = getgenv().__ZkxHubTest if type(probe) == 'table' then probe.HitLog = HitLog probe.Bridge = RivalsRuntimeBridge end end end -- ====================================================================== -- Name Style: your name's colour / animation on the nametag (lobby) and in the kill feed (duels) -- ====================================================================== -- Local only: it recolours labels on YOUR screen, nothing is sent anywhere. The nametag is PlayerScripts.Modules.Nametag's BillboardGui -- (Elements.NameDisplay.DisplayName, plus the Prime / Contraband overlay the game puts on top); the kill feed card is -- PlayerScripts.UserInterface.EliminationFeedSlot (Container.Container.Eliminator / Victim .TextLabel). A UIGradient on the label does the colouring, -- so the text itself is never touched and everything is put back when the style is switched off. do local RunService = game:GetService('RunService') local Players = game:GetService('Players') -- plain tables on purpose: a weak table of Instances loses live entries whenever the garbage collector runs (checked live) local NameStyle = { Tracked = {}, OverlayOriginals = {}, Watch = {}, Clock = 0, Accumulated = 0, Scan = 0 } RivalsRuntimeBridge.NameStyle = NameStyle local GRADIENT_NAME = 'ZkxHubNameFx' local MODES = { 'Solid', 'Rainbow', 'Wave Rainbow', 'Pulse', 'Gradient Flow' } local function toggleOn(id) return Toggles ~= nil and Toggles[id] ~= nil and Toggles[id].Value == true end local function optionValue(id, fallback) local option = Options and Options[id] if option == nil or option.Value == nil then return fallback end return option.Value end -- the game's modules cannot be required from an executor thread (identity 8); the game's own threads run at 2 local function atGameIdentity(fn) local getId, setId = getthreadidentity, setthreadidentity or setidentity local old = getId and getId() or nil if setId then pcall(setId, 2) end local ok, value = pcall(fn) if setId and old then pcall(setId, old) end return ok and value or nil end -- the names the game may print for you (display name, filtered name, user name) function NameStyle.MyNames() local me = Players.LocalPlayer local names = {} local function add(name) if type(name) == 'string' and name ~= '' then names[#names + 1] = name end end if me then add(atGameIdentity(function() return require(me.PlayerScripts.Controllers.ComplianceController):GetName(me) end)) add(me.DisplayName) add(me.Name) end return names end -- ---------------------------------------------------------------------------------------------------------------- colours local function sequenceFor(mode, now, speed, colorA, colorB) if mode == 'Rainbow' then return ColorSequence.new(Color3.fromHSV((now * speed * 0.2) % 1, 1, 1)) elseif mode == 'Wave Rainbow' then local points = {} for index = 0, 7 do points[#points + 1] = ColorSequenceKeypoint.new(index / 7, Color3.fromHSV((now * speed * 0.2 + index / 7 * 0.8) % 1, 1, 1)) end return ColorSequence.new(points) elseif mode == 'Pulse' then return ColorSequence.new(colorA:Lerp(colorB, (math.sin(now * speed * 2) + 1) / 2)) elseif mode == 'Gradient Flow' then local points = {} for index = 0, 4 do local blend = (math.sin(index / 4 * math.pi * 2 + now * speed * 2) + 1) / 2 points[#points + 1] = ColorSequenceKeypoint.new(index / 4, colorA:Lerp(colorB, blend)) end return ColorSequence.new(points) end return ColorSequence.new(colorA) end NameStyle.SequenceFor = sequenceFor function NameStyle.CurrentSequence() return sequenceFor(optionValue('ZKND2', 'Rainbow'), NameStyle.Clock, optionValue('ZKNS1', 1), optionValue('ZKNC1', Color3.fromRGB(125, 85, 255)), optionValue('ZKNC2', Color3.fromRGB(255, 255, 255))) end -- ---------------------------------------------------------------------------------------------------------------- attach / detach local function overlayGradient(label) local overlay = label:FindFirstChild('_ITEMSTATUSCLONE') local highlight = overlay and overlay:FindFirstChild('Highlight') return highlight and highlight:FindFirstChildOfClass('UIGradient') or nil end function NameStyle.Attach(label, kind) if NameStyle.Tracked[label] then return end local gradient = Instance.new('UIGradient') gradient.Name = GRADIENT_NAME gradient.Color = NameStyle.CurrentSequence() gradient.Parent = label NameStyle.Tracked[label] = { Gradient = gradient, Kind = kind, Color = label.TextColor3 } label.TextColor3 = Color3.new(1, 1, 1) -- the gradient multiplies the text colour: white shows the gradient as it is end local function release(label, state) if state.Gradient then pcall(function() state.Gradient:Destroy() end) end pcall(function() if label.Parent then label.TextColor3 = state.Color end end) end function NameStyle.Clear(kind) for label, state in pairs(NameStyle.Tracked) do if kind == nil or state.Kind == kind then release(label, state) NameStyle.Tracked[label] = nil end end if kind == nil or kind == 'nametag' then for gradient, original in pairs(NameStyle.OverlayOriginals) do pcall(function() gradient.Color = original end) NameStyle.OverlayOriginals[gradient] = nil end end end -- ---------------------------------------------------------------------------------------------------------------- nametags (lobby) local function nametagLabel(gui) local elements = gui:FindFirstChild('Elements') local display = elements and elements:FindFirstChild('NameDisplay') return display and display:FindFirstChild('DisplayName') or nil end function NameStyle.ScanNametags() local onlyMe = optionValue('ZKND1', 'Only Me') == 'Only Me' for _, player in ipairs(Players:GetPlayers()) do if not onlyMe or player == Players.LocalPlayer then local character = player.Character local root = character and character:FindFirstChild('HumanoidRootPart') local gui = root and root:FindFirstChild('NametagGui') local label = gui and nametagLabel(gui) if label then NameStyle.Attach(label, 'nametag') end end end end -- ---------------------------------------------------------------------------------------------------------------- kill feed (duels) function NameStyle.IsMine(text) if not NameStyle.Names then NameStyle.Names = NameStyle.MyNames() end for _, name in ipairs(NameStyle.Names) do if string.find(text, name, 1, true) then return true end end return false end function NameStyle.StyleFeedSlot(slot) if not toggleOn('ZKNT2') then return end local outer = slot:FindFirstChild('Container') local inner = outer and outer:FindFirstChild('Container') if not inner then return end local everyone = optionValue('ZKND1', 'Only Me') == 'Everyone' for _, role in ipairs({ 'Eliminator', 'Victim' }) do local holder = inner:FindFirstChild(role) local label = holder and holder:FindFirstChild('TextLabel') if label and label.Text ~= '' and (everyone or NameStyle.IsMine(label.Text)) then NameStyle.Attach(label, 'feed') end end end function NameStyle.ScanFeed() local gui = Players.LocalPlayer and Players.LocalPlayer:FindFirstChildOfClass('PlayerGui') if not gui then return end for _, inst in ipairs(gui:GetDescendants()) do if inst.Name == 'EliminationFeedSlot' then NameStyle.StyleFeedSlot(inst) end end end -- ---------------------------------------------------------------------------------------------------------------- tick function NameStyle.Tick(dt) NameStyle.Clock += dt NameStyle.Accumulated += dt NameStyle.Scan += dt if NameStyle.Accumulated < 1 / 30 then return end NameStyle.Accumulated = 0 if NameStyle.Scan >= 0.3 then NameStyle.Scan = 0 if toggleOn('ZKNT1') then NameStyle.ScanNametags() end for gradient in pairs(NameStyle.OverlayOriginals) do if gradient.Parent == nil then NameStyle.OverlayOriginals[gradient] = nil end -- the nametag was rebuilt: its overlay is gone end end if next(NameStyle.Tracked) == nil then return end local sequence = NameStyle.CurrentSequence() for label, state in pairs(NameStyle.Tracked) do if not label.Parent then NameStyle.Tracked[label] = nil else state.Gradient.Color = sequence if state.Kind == 'nametag' then local overlay = overlayGradient(label) if overlay then if NameStyle.OverlayOriginals[overlay] == nil then NameStyle.OverlayOriginals[overlay] = overlay.Color end overlay.Color = sequence end end end end end -- ---------------------------------------------------------------------------------------------------------------- switches function NameStyle.Refresh() NameStyle.Names = nil NameStyle.Clear() for key, connection in pairs(NameStyle.Watch) do pcall(function() connection:Disconnect() end) NameStyle.Watch[key] = nil end local nametag, feed = toggleOn('ZKNT1'), toggleOn('ZKNT2') if not nametag and not feed then if Connections then Connections:disconnect({ 'NameStyle_Tick' }) end return end if Connections then Connections:disconnect({ 'NameStyle_Tick' }) Connections:register('NameStyle_Tick', RunService.RenderStepped:Connect(GuardRivalsCallback('NameStyle_Tick', NameStyle.Tick))) end if nametag then NameStyle.ScanNametags() end if feed then local gui = Players.LocalPlayer and Players.LocalPlayer:FindFirstChildOfClass('PlayerGui') if gui then NameStyle.Watch.Feed = gui.DescendantAdded:Connect(function(inst) if inst.Name == 'EliminationFeedSlot' then task.defer(NameStyle.StyleFeedSlot, inst) end end) end NameStyle.ScanFeed() end end -- a sample card built from the game's own template, so the colours can be seen without a duel function NameStyle.Preview() local me = Players.LocalPlayer local template = me and me.PlayerScripts:FindFirstChild('UserInterface') and me.PlayerScripts.UserInterface:FindFirstChild('EliminationFeedSlot') local playerGui = me and me:FindFirstChildOfClass('PlayerGui') if not template or not playerGui then return false end local old = playerGui:FindFirstChild('ZkxHubNameStylePreview') if old then old:Destroy() end local screen = Instance.new('ScreenGui') screen.Name = 'ZkxHubNameStylePreview' screen.ResetOnSpawn = false screen.DisplayOrder = 50 local slot = template:Clone() local inner = slot.Container.Container local myName = NameStyle.MyNames()[1] or 'You' inner.Eliminator.TextLabel.Text = myName inner.Victim.TextLabel.Text = 'Enemy' inner.Eliminator.Visible = true inner.Critical.Visible, inner.Blinded.Visible, inner.Noscope.Visible = false, false, false local gradient = slot.Container.Background.Background:FindFirstChildOfClass('UIGradient') if gradient then gradient.Color = ColorSequence.new({ ColorSequenceKeypoint.new(0, Color3.fromRGB(235, 177, 2)), ColorSequenceKeypoint.new(0.5, Color3.new(0, 0, 0)), ColorSequenceKeypoint.new(1, Color3.fromRGB(102, 51, 220)) }) end slot.AnchorPoint = Vector2.new(1, 0) slot.Position = UDim2.new(1, -16, 0, 90) slot.Size = UDim2.new(0, 320, 0, 38) slot.Parent = screen screen.Parent = playerGui -- after the slot is filled: the watcher above styles it like a real card task.delay(8, function() if screen.Parent then screen:Destroy() end end) return true end function NameStyle.Destroy() if Connections then Connections:disconnect({ 'NameStyle_Tick' }) end for key, connection in pairs(NameStyle.Watch) do pcall(function() connection:Disconnect() end) NameStyle.Watch[key] = nil end NameStyle.Clear() local me = Players.LocalPlayer local gui = me and me:FindFirstChildOfClass('PlayerGui') local preview = gui and gui:FindFirstChild('ZkxHubNameStylePreview') if preview then pcall(function() preview:Destroy() end) end end -- ---------------------------------------------------------------------------------------------------------------- menu function NameStyle.BuildMenu(tab) local group = tab:AddRightGroupbox('Name Style') local nametag = group:AddToggle('ZKNT1', { Text = 'Nametag Style', Default = false, Tooltip = 'Colours or animates the name over your head (the nametag shows in the lobby). Only you see it.', }) nametag:OnChanged(GuardRivalsCallback('NameStyle_Nametag', NameStyle.Refresh)) local feed = group:AddToggle('ZKNT2', { Text = 'Kill Feed Name Style', Default = false, Tooltip = 'Colours or animates the names in the kill feed cards at the top right. Only you see it.', }) feed:OnChanged(GuardRivalsCallback('NameStyle_Feed', NameStyle.Refresh)) local box = group box:AddDropdown('ZKND1', { Text = 'Apply To', Values = { 'Only Me', 'Everyone' }, Default = 'Only Me', Multi = false, Tooltip = 'Only Me: your own name. Everyone: every name (the team colours of the kill feed are replaced).', Callback = GuardRivalsCallback('NameStyle_Scope', NameStyle.Refresh), }) box:AddDropdown('ZKND2', { Text = 'Style', Values = MODES, Default = 'Rainbow', Multi = false, Tooltip = 'Solid: one colour. Rainbow: the whole name cycles. Wave Rainbow: a rainbow runs through the letters. Pulse: fades between the two colours. Gradient Flow: two colours flow through the name.', }) box:AddLabel('Color'):AddColorPicker('ZKNC1', { Default = Color3.fromRGB(125, 85, 255), Title = 'Name Style Color' }) box:AddLabel('Second Color'):AddColorPicker('ZKNC2', { Default = Color3.fromRGB(255, 255, 255), Title = 'Name Style Second Color' }) box:AddSlider('ZKNS1', { Text = 'Speed', Default = 1, Min = 0.2, Max = 5, Rounding = 1, Compact = true }) box:AddButton({ Text = 'Preview Kill Feed', Tooltip = 'Shows a sample kill feed card for a few seconds (needs Kill Feed Name Style on).', Func = GuardRivalsCallback('NameStyle_Preview', function() if not toggleOn('ZKNT2') then Library:Notify({ Title = 'Name Style', Description = 'Switch Kill Feed Name Style on first.', Time = 4 }) return end NameStyle.Preview() end), }) end if type(getgenv) == 'function' then local probe = getgenv().__ZkxHubTest if type(probe) == 'table' then probe.NameStyle = NameStyle end end end -- ====================================================================== -- Aimbot diagnostics -- ====================================================================== do local diag = { Counts = {}, LastRejections = {}, RecentLocks = {}, WindowStart = 0, Notification = nil, LastDescription = nil, LastAcquiredName = nil, LastAcquiredAt = 0, Unavailable = false, } local function isEnabled() return Toggles.P9S2T19 and Toggles.P9S2T19.Value == true end function RivalsRuntimeBridge.RecordTargetValidityRejection(gate, subject, player, targetType, extras) if not isEnabled() then return end local now = tick() if now - diag.WindowStart > 1 then diag.Counts = {} diag.WindowStart = now end diag.Counts[gate] = (diag.Counts[gate] or 0) + 1 local name = 'unknown' if player then name = player.DisplayName or player.Name or name elseif subject and subject.Name then name = subject.Name end local rejections = diag.LastRejections rejections[#rejections + 1] = { name = name, gate = gate, at = now, targetType = targetType, extras = extras, } while #rejections > 5 do table.remove(rejections, 1) end end function RivalsRuntimeBridge.RecordTargetValidityAcquired(name, info) if not isEnabled() then return end local now = tick() diag.LastAcquiredName = name or 'unknown' diag.LastAcquiredAt = now local locks = diag.RecentLocks locks[#locks + 1] = { name = diag.LastAcquiredName, at = now, targetType = info and info.targetType or nil, aimPart = info and info.aimPart or nil, isVisible = info and info.isVisible == true, screenDistance = info and info.screenDistance or nil, worldDistance = info and info.worldDistance or nil, } while #locks > 3 do table.remove(locks, 1) end end function RivalsRuntimeBridge.RecordTargetValidityUpstreamGate(gate) if not isEnabled() then return end local now = tick() if now - diag.WindowStart > 1 then diag.Counts = {} diag.WindowStart = now end diag.Counts[gate] = (diag.Counts[gate] or 0) + 1 end local function buildReadinessSnapshot() local function safeBool(fn) if type(fn) ~= 'function' then return false end local ok, v = pcall(fn) return ok and v == true end local fighter = ResolveLocalFighter() local entity = fighter and fighter.Entity local liveChar = LP and LP.Character local duel = FighterDataCache and FighterDataCache.LocalDuel or nil local localReadyState = RivalsRuntimeBridge.LocalReadyState local rangeCount, dummyCount = 0, 0 for _ in pairs(TrackedRangeTargets) do rangeCount = rangeCount + 1 end for _ in pairs(TrackedPracticeDummies) do dummyCount = dummyCount + 1 end return { cameraReady = safeBool(IsAimbotCameraReady), gameReady = safeBool(IsAimbotGameReady), charInWorkspace = liveChar ~= nil and liveChar.Parent == Workspace, entityInWorld = type(entity) == 'table' and rawget(entity, '_is_in_world') == true, invincible = type(entity) == 'table' and type(entity.Data) == 'table' and entity.Data.IsInvincible == true, shieldSeen = type(localReadyState) == 'table' and localReadyState.ShieldSeen == true, readyNotified = type(localReadyState) == 'table' and localReadyState.ReadyNotified == true, duelSeeded = type(duel) == 'table' and duel.Seeded == true, inDuel = type(duel) == 'table' and duel.IsInDuel == true, inShootingRange = type(duel) == 'table' and duel.Seeded == true and duel.IsInShootingRange == true, localReady = safeBool(RivalsRuntimeBridge.IsLocalReadyToFight), readyToFight = safeBool(RivalsRuntimeBridge.IsReadyToFight), rangeCount = rangeCount, dummyCount = dummyCount, } end local function buildText() local now = tick() local lines = { 'Target Validity Diagnostic' } local counts = diag.Counts local keys = {} for k in pairs(counts) do keys[#keys + 1] = k end table.sort(keys, function(a, b) return counts[a] > counts[b] end) if #keys == 0 then lines[#lines + 1] = 'Last 1s rejections: none' else lines[#lines + 1] = 'Last 1s rejections:' for _, k in ipairs(keys) do lines[#lines + 1] = string.format(' %s x %d', k, counts[k]) end end local rej = diag.LastRejections if #rej > 0 then local visible = {} for i = 1, #rej do local r = rej[i] if not r.targetType or r.targetType == 'Players' then visible[#visible + 1] = r end end if #visible > 0 then lines[#lines + 1] = 'Recent:' for i = #visible, math.max(1, #visible - 2), -1 do local r = visible[i] lines[#lines + 1] = string.format(' %s: %s (%.2fs)', r.name, r.gate, math.max(0, now - r.at)) end end end local locks = diag.RecentLocks if #locks > 0 then lines[#lines + 1] = 'Recent locks:' for i = #locks, math.max(1, #locks - 2), -1 do local l = locks[i] local visTag if l.isVisible then visTag = 'y' else visTag = 'n' end local detail = string.format('aim=%s vis=%s', tostring(l.aimPart or '?'), visTag) if l.screenDistance then detail = detail .. string.format(' sd=%d', math.floor(l.screenDistance + 0.5)) end lines[#lines + 1] = string.format(' %s [%s] (%.2fs)', l.name, detail, math.max(0, now - l.at)) end elseif diag.LastAcquiredName then lines[#lines + 1] = string.format('Last lock: %s (%.2fs ago)', diag.LastAcquiredName, math.max(0, now - diag.LastAcquiredAt)) else lines[#lines + 1] = 'Last lock: none' end local snapshotOk, snapshot = pcall(buildReadinessSnapshot) if snapshotOk and type(snapshot) == 'table' then local function tag(b) return b and 'Y' or 'N' end lines[#lines + 1] = 'Gates:' lines[#lines + 1] = string.format(' CamReady=%s GameReady=%s', tag(snapshot.cameraReady), tag(snapshot.gameReady)) lines[#lines + 1] = string.format(' CharInWS=%s EntityInWorld=%s', tag(snapshot.charInWorkspace), tag(snapshot.entityInWorld)) lines[#lines + 1] = string.format( ' Invincible=%s ShieldSeen=%s ReadyNotified=%s', tag(snapshot.invincible), tag(snapshot.shieldSeen), tag(snapshot.readyNotified) ) lines[#lines + 1] = string.format( ' DuelSeeded=%s InDuel=%s InShootingRange=%s', tag(snapshot.duelSeeded), tag(snapshot.inDuel), tag(snapshot.inShootingRange) ) lines[#lines + 1] = string.format(' LocalReady=%s ReadyToFight=%s', tag(snapshot.localReady), tag(snapshot.readyToFight)) lines[#lines + 1] = string.format('Pools: range=%d dummies=%d', snapshot.rangeCount, snapshot.dummyCount) end return table.concat(lines, '\n') end local function ensureNotif(description) if diag.Notification then return true end if diag.Unavailable then return false end if not Library or type(Library.Notify) ~= 'function' then return false end local ok, n = pcall( function() return Library:Notify({ Title = 'Target Validity', Description = description, Persist = true }) end ) if ok and n then diag.Notification = n return true end diag.Unavailable = true return false end local function destroyNotif() if diag.Notification then pcall(function() diag.Notification:Destroy() end) diag.Notification = nil end diag.LastDescription = nil diag.Unavailable = false end local function updateNotif() if not isEnabled() then destroyNotif() return end local description = buildText() if diag.LastDescription == description then return end if not ensureNotif(description) then return end pcall(function() diag.Notification:ChangeDescription(description) end) diag.LastDescription = description end task.spawn(function() while not Library.Unloaded and task.wait(0.25) do pcall(updateNotif) end destroyNotif() end) end -- ====================================================================== -- Silent aim: hook target resolution -- ====================================================================== do local ResolveAimbotSilentHookTarget = function(item) local state = AimbotSilentState local now = tick() local cacheWindow = AimbotBridge.ResolveAimbotSilentHookTargetCacheWindow(item) local cachedTarget = state.HookTarget if cachedTarget and state.HookTargetCapturedAt > 0 and (now - state.HookTargetCapturedAt) <= cacheWindow then local aimPart = Options.P2S1D2 and Options.P2S1D2.Value or 'Auto' local mousePosition = AimbotBridge.GetAimbotPointerPosition() local maxDistance = DEFAULT_ESP_MAX_DISTANCE local refreshedTarget = RivalsRuntimeBridge.RefreshAimbotTargetInfo( cachedTarget, aimPart, mousePosition, maxDistance, AIMBOT_VISIBILITY_CACHE_PROFILE ) if refreshedTarget then state.HookTarget = refreshedTarget return refreshedTarget end end local targetInfo = RivalsRuntimeBridge.GetFilteredAimbotTarget() state.HookTarget = targetInfo state.HookTargetCapturedAt = targetInfo and now or 0 return targetInfo end local ShouldBypassAimbotSilentReloadBlock = function(item, state, holdWindow, now, queuePending, inputSpammingFlag) if AimbotBridge.ReadAimbotSilentToggle('P9S2T6', true) and queuePending and not (AimbotBridge.ReadAimbotSilentToggle('P9S2T7', true) and inputSpammingFlag) then return true end if AimbotBridge.ReadAimbotSilentToggle('P9S2T14', true) and holdWindow > 0 and state.ShotSignalAt > 0 and (now - state.ShotSignalAt) <= holdWindow then return true end if AimbotBridge.ReadAimbotSilentToggle('P9S2T15', true) and holdWindow > 0 and state.ProjectileShotAt > 0 and (now - state.ProjectileShotAt) <= holdWindow then return true end if AimbotBridge.ReadAimbotSilentToggle('P9S2T16', true) and item and rawget(item, '_is_using') then return true end if AimbotBridge.ReadAimbotSilentToggle('P9S2T17', true) and item and rawget(item, '_is_throwing') then return true end return false end local EvaluateAimbotSilentRuntime = function(item, shootInputHeld) AimbotBridge.EnsureAimbotSilentConnections(item) local state = AimbotSilentState local lastShot = AimbotBridge.ReadAimbotLastShot(item) if type(lastShot) == 'number' and type(state.LastShot) == 'number' and lastShot > state.LastShot then state.LastShotChangedAt = tick() end state.LastShot = lastShot local info = item and item.Info or nil local shootRemaining = AimbotBridge.ReadAimbotShootRemaining(item) local reloadRemaining = AimbotBridge.ReadAimbotReloadRemaining(item) local burstCount = type(item and rawget(item, '_burst_count')) == 'number' and rawget(item, '_burst_count') or 0 local queuePending = item and rawget(item, '_is_input_queueing') == true local isCharging = AimbotBridge.ReadAimbotIsCharging(item) local inputSpammingFlag = info and info.InputSpammingEnabled and info.InputSpammingEnabled.StartShooting local holdWindow = AimbotBridge.ResolveAimbotSilentHoldWindow(item) local now = tick() local reasons = {} local shouldAim = true local blockedReason = nil local bypassReloadBlock = ShouldBypassAimbotSilentReloadBlock(item, state, holdWindow, now, queuePending, inputSpammingFlag) if AimbotBridge.ReadAimbotSilentToggle('P9S2T2', true) and shootInputHeld and reloadRemaining > 0 and not isCharging and not bypassReloadBlock then blockedReason = 'reload_cooldown' shouldAim = false end if AimbotBridge.ReadAimbotSilentToggle('P9S2T18', true) and isCharging then reasons[#reasons + 1] = 'is_charging' end if AimbotBridge.ReadAimbotSilentToggle('P9S2T3', true) and shootRemaining > 0 then reasons[#reasons + 1] = 'shoot_cooldown' end if AimbotBridge.ReadAimbotSilentToggle('P9S2T4', true) and holdWindow > 0 and state.LastShotChangedAt > 0 and (now - state.LastShotChangedAt) <= holdWindow then reasons[#reasons + 1] = 'last_shot' end if AimbotBridge.ReadAimbotSilentToggle('P9S2T5', true) and burstCount > 0 then reasons[#reasons + 1] = 'burst_count' end if AimbotBridge.ReadAimbotSilentToggle('P9S2T6', true) and queuePending and not (AimbotBridge.ReadAimbotSilentToggle('P9S2T7', true) and inputSpammingFlag) then reasons[#reasons + 1] = 'queue_pending' end if AimbotBridge.ReadAimbotSilentToggle('P9S2T9', true) and type(info and info.ShootBurst) == 'number' and info.ShootBurst > 1 and burstCount > 0 then reasons[#reasons + 1] = 'shoot_burst_info' end if AimbotBridge.ReadAimbotSilentToggle('P9S2T14', true) and holdWindow > 0 and state.ShotSignalAt > 0 and (now - state.ShotSignalAt) <= holdWindow then reasons[#reasons + 1] = 'shot_signal' end if AimbotBridge.ReadAimbotSilentToggle('P9S2T15', true) and holdWindow > 0 and state.ProjectileShotAt > 0 and (now - state.ProjectileShotAt) <= holdWindow then reasons[#reasons + 1] = 'projectile_signal' end if AimbotBridge.ReadAimbotSilentToggle('P9S2T16', true) and item and rawget(item, '_is_using') then reasons[#reasons + 1] = 'is_using' end if AimbotBridge.ReadAimbotSilentToggle('P9S2T17', true) and item and rawget(item, '_is_throwing') then reasons[#reasons + 1] = 'is_throwing' end if blockedReason then return { shouldAim = false, blockedReason = blockedReason, reasons = reasons, shootRemaining = shootRemaining, reloadRemaining = reloadRemaining, burstCount = burstCount, queuePending = queuePending, isCharging = isCharging, inputSpammingFlag = inputSpammingFlag, holdWindow = holdWindow, lastShot = lastShot, aimScopePercent = info and info.AimScopePercent or nil, toggleAimEnabled = item and item.ToggleAimEnabled or nil, } end if #reasons == 0 then reasons[#reasons + 1] = shootInputHeld and 'held_input' or 'keybind' end return { shouldAim = shouldAim, blockedReason = nil, reasons = reasons, shootRemaining = shootRemaining, reloadRemaining = reloadRemaining, burstCount = burstCount, queuePending = queuePending, isCharging = isCharging, inputSpammingFlag = inputSpammingFlag, holdWindow = holdWindow, lastShot = lastShot, aimScopePercent = info and info.AimScopePercent or nil, toggleAimEnabled = item and item.ToggleAimEnabled or nil, } end local EnsureAimbotStatusNotification = function(description) if AimbotStatusNotification then return true end local notification = Library:Notify({ Title = 'Aimbot', Description = description, Persist = true, }) if notification then AimbotStatusNotification = notification return true end return false end local FormatAimbotStatusNumber = function(value) if type(value) ~= 'number' then return '--' end return string.format('%.3f', value) end local DestroyAimbotStatusNotification = function() if AimbotStatusNotification then pcall(function() AimbotStatusNotification:Destroy() end) AimbotStatusNotification = nil end AimbotStatusKey = nil AimbotStatusDescription = nil end local NotifyAimbotStatus = function(statusKey, description, force) local statusNotifications = Toggles.P2S1T6 and Toggles.P2S1T6.Value if not force and not statusNotifications then DestroyAimbotStatusNotification() return end description = tostring(description or 'Idle') if AimbotStatusKey == statusKey and AimbotStatusDescription == description then return end if not EnsureAimbotStatusNotification(description) then return end if AimbotStatusDescription ~= description then pcall(function() AimbotStatusNotification:ChangeDescription(description) end) end AimbotStatusKey = statusKey AimbotStatusDescription = description end local BuildAimbotSilentStatus = function(targetInfo, evaluation) local targetName = targetInfo and targetInfo.displayName or 'No target' if not AimbotBridge.ResolveAimbotSilentDebugEnabled() then if evaluation.blockedReason then return string.format('Silent Camera blocked: %s', evaluation.blockedReason) end return string.format('Silent Camera: %s', targetName) end local lines = { string.format('Silent Camera: %s', targetName), string.format('Reason: %s', evaluation.blockedReason or table.concat(evaluation.reasons, ', ')), string.format( 'Live: sc %s | rc %s | bc %s | q %s | chg %s | hw %s', FormatAimbotStatusNumber(evaluation.shootRemaining), FormatAimbotStatusNumber(evaluation.reloadRemaining), tostring(evaluation.burstCount), evaluation.queuePending and 'yes' or 'no', evaluation.isCharging and 'yes' or 'no', FormatAimbotStatusNumber(evaluation.holdWindow) ), } if AimbotBridge.ReadAimbotSilentToggle('P9S2T7', true) then lines[#lines + 1] = string.format('InputSpam: %s', tostring(evaluation.inputSpammingFlag)) end if AimbotBridge.ReadAimbotSilentToggle('P9S2T12', false) then lines[#lines + 1] = string.format('AimScopePercent: %s', tostring(evaluation.aimScopePercent)) end if AimbotBridge.ReadAimbotSilentToggle('P9S2T13', false) then lines[#lines + 1] = string.format('ToggleAimEnabled: %s', tostring(evaluation.toggleAimEnabled)) end if AimbotBridge.ReadAimbotSilentToggle('P9S2T4', true) then lines[#lines + 1] = string.format('LastShot: %s', FormatAimbotStatusNumber(evaluation.lastShot)) end return table.concat(lines, '\n') end local AppendAimbotTargetPartCandidate = function(candidates, seen, candidate) if candidate and candidate:IsA('BasePart') and not seen[candidate] then seen[candidate] = true candidates[#candidates + 1] = candidate end end local GetAimbotTargetPartCandidates = function(subject, aimPart) if not subject then return {} end if subject:IsA('BasePart') then if DedicatedAimbotHitboxPartNames[subject.Name] then return { subject } end return {} end if not subject:IsA('Model') then return {} end local candidates = {} local seen = {} local function addNamed(name) AppendAimbotTargetPartCandidate(candidates, seen, subject:FindFirstChild(name)) end if aimPart == 'Head' then addNamed('HitboxHead') elseif aimPart == 'HumanoidRootPart' then addNamed('HitboxBody') elseif aimPart == 'Closest' then addNamed('HitboxBody') addNamed('HitboxHead') else addNamed('HitboxHead') addNamed('HitboxBody') end if #candidates == 0 then addNamed('HumanoidRootPart') end return RivalsRuntimeBridge.Extras.FilterCandidates(candidates) end local GetAimbotTargetPartSamplePoints = function(part, targetType) if not part or not part:IsA('BasePart') then return {} end local extrasSamples = RivalsRuntimeBridge.Extras.SamplePoints(part, targetType) if extrasSamples then return extrasSamples end local samples = { part.Position } local useExtendedSampling = targetType == 'Players' or targetType == 'Practice Dummies' useExtendedSampling = useExtendedSampling and DedicatedAimbotHitboxPartNames[part.Name] if not useExtendedSampling then return samples end local cframe = part.CFrame local halfSize = part.Size * 0.5 local insetX = halfSize.X * 0.9 local insetY = halfSize.Y * 0.9 local insetZ = halfSize.Z * 0.9 local isHeadPart = part.Name == 'HitboxHead' local function addLocalPoint(x, y, z) samples[#samples + 1] = cframe:PointToWorldSpace(Vector3.new(x, y, z)) end if isHeadPart then samples = {} samples[#samples + 1] = part.Position addLocalPoint(0, halfSize.Y, 0) addLocalPoint(0, insetY * 0.82, insetZ * 0.45) addLocalPoint(0, insetY * 0.82, -insetZ * 0.45) addLocalPoint(insetX * 0.55, insetY * 0.78, 0) addLocalPoint(-insetX * 0.55, insetY * 0.78, 0) return samples end addLocalPoint(insetX, 0, 0) addLocalPoint(-insetX, 0, 0) addLocalPoint(0, insetY, 0) addLocalPoint(0, -insetY, 0) addLocalPoint(0, 0, insetZ) addLocalPoint(0, 0, -insetZ) return samples end local ResolveVisiblePointOnAimbotPart = function(subject, candidate, targetType) for _, samplePoint in ipairs(GetAimbotTargetPartSamplePoints(candidate, targetType)) do if IsVisible(subject, candidate, samplePoint) then return samplePoint end end return nil end local IsHeadAimbotTargetPart = function(candidate) if not candidate then return false end local name = candidate.Name return name == 'HitboxHead' end RivalsRuntimeBridge.IsHeadAimbotTargetPart = IsHeadAimbotTargetPart local ResolveRandomVisibleAimbotTargetPart = function(subject, candidates, fallback, targetType) local headCandidates = {} local bodyCandidates = {} for _, candidate in ipairs(candidates) do if IsHeadAimbotTargetPart(candidate) then headCandidates[#headCandidates + 1] = candidate else bodyCandidates[#bodyCandidates + 1] = candidate end end local randomBucketRoll = RivalsRuntimeBridge.Extras.RandomBucketRoll() local bucketOrder = randomBucketRoll == 1 and { headCandidates, bodyCandidates } or { bodyCandidates, headCandidates } for _, bucket in ipairs(bucketOrder) do local visibleCandidates = {} for _, candidate in ipairs(bucket) do local visiblePoint = ResolveVisiblePointOnAimbotPart(subject, candidate, targetType) if visiblePoint then visibleCandidates[#visibleCandidates + 1] = { part = candidate, point = visiblePoint, } end end if #visibleCandidates > 0 then local randomIndex = math.random(1, #visibleCandidates) local randomEntry = visibleCandidates[randomIndex] return randomEntry.part, randomEntry.point, true end end return fallback, fallback and fallback.Position or nil, false end local ResolveVisibleAimbotTargetPart local ResolvePrefilteredAimbotTargetPart do local ResolveVisibilityCacheProfileId = function(cacheProfile) local profileId = cacheProfile and cacheProfile.Id if type(profileId) == 'string' and profileId ~= '' then return profileId end return AIMBOT_VISIBILITY_CACHE_PROFILE.Id end local ReadCachedAimbotVisibility = function(subject, aimPart, cacheProfile) local now = os.clock() local cachedByProfile = AimbotVisibilityCache[subject] local profileId = ResolveVisibilityCacheProfileId(cacheProfile) local cachedByAimPart = cachedByProfile and cachedByProfile[profileId] local cachedEntry = cachedByAimPart and cachedByAimPart[aimPart] if cachedEntry and cachedEntry.expiresAt >= now then local cachedPart = cachedEntry.part if not cachedPart or cachedPart.Parent then return cachedEntry.part, cachedEntry.point, cachedEntry.isVisible, true end end return nil, nil, nil, false end local WriteCachedAimbotVisibility = function(subject, aimPart, part, point, isVisible, cacheProfile) if not subject then return end local hiddenCacheWindow = cacheProfile and cacheProfile.HiddenWindow or AIMBOT_HIDDEN_CACHE_WINDOW local visibleCacheWindow = cacheProfile and cacheProfile.VisibleWindow or AIMBOT_VISIBLE_CACHE_WINDOW local cacheWindow = isVisible and visibleCacheWindow or hiddenCacheWindow local profileId = ResolveVisibilityCacheProfileId(cacheProfile) if cacheWindow <= 0 then local cachedByProfile = AimbotVisibilityCache[subject] local cachedByAimPart = cachedByProfile and cachedByProfile[profileId] if cachedByAimPart then cachedByAimPart[aimPart] = nil end return end local cachedByProfile = AimbotVisibilityCache[subject] if not cachedByProfile then cachedByProfile = {} AimbotVisibilityCache[subject] = cachedByProfile end local cachedByAimPart = cachedByProfile[profileId] if not cachedByAimPart then cachedByAimPart = {} cachedByProfile[profileId] = cachedByAimPart end cachedByAimPart[aimPart] = { part = part, point = point, isVisible = isVisible, expiresAt = os.clock() + cacheWindow, } end ResolveVisibleAimbotTargetPart = function(subject, aimPart, targetType, cacheProfile) cacheProfile = cacheProfile or AIMBOT_VISIBILITY_CACHE_PROFILE if aimPart ~= 'Random' then local cachedPart, cachedPoint, cachedVisible, hasCachedEntry = ReadCachedAimbotVisibility(subject, aimPart, cacheProfile) if hasCachedEntry then return cachedPart, cachedPoint, cachedVisible end end local candidates = GetAimbotTargetPartCandidates(subject, aimPart) local fallback = candidates[1] if aimPart == 'Random' then return ResolveRandomVisibleAimbotTargetPart(subject, candidates, fallback, targetType) end for _, candidate in ipairs(candidates) do local visiblePoint = ResolveVisiblePointOnAimbotPart(subject, candidate, targetType) if visiblePoint then WriteCachedAimbotVisibility(subject, aimPart, candidate, visiblePoint, true, cacheProfile) return candidate, visiblePoint, true end end local fallbackPoint = fallback and fallback.Position or nil WriteCachedAimbotVisibility(subject, aimPart, fallback, fallbackPoint, false, cacheProfile) return fallback, fallbackPoint, false end ResolvePrefilteredAimbotTargetPart = function( subject, aimPart, targetType, mousePosition, maxDistance, fovRadius, ignoreFov, camera, cacheProfile ) if not subject or not camera then return nil, nil, false end cacheProfile = cacheProfile or AIMBOT_VISIBILITY_CACHE_PROFILE if aimPart ~= 'Random' then local cachedPart, cachedPoint, cachedVisible, hasCachedEntry = ReadCachedAimbotVisibility(subject, aimPart, cacheProfile) if hasCachedEntry then return cachedPart, cachedPoint, cachedVisible end end local rootPosition = HumanoidRootPart and HumanoidRootPart.Position or nil local maxDistanceSquared = maxDistance * maxDistance local function SamplePointPassesAimbotPrefilter(samplePoint) if rootPosition then local samplePointOffset = samplePoint - rootPosition if samplePointOffset:Dot(samplePointOffset) > maxDistanceSquared then return false end end if ignoreFov then return true end local screenPosition, onScreen = camera:WorldToViewportPoint(samplePoint) if not onScreen or screenPosition.Z <= 0 then return false end local screenDistance = (Vector2.new(screenPosition.X, screenPosition.Y) - mousePosition).Magnitude return screenDistance <= fovRadius end local function ResolvePrefilteredVisiblePoint(candidate) if not candidate or not candidate:IsA('BasePart') then return nil, false, false end local fallbackPoint = nil for _, samplePoint in ipairs(GetAimbotTargetPartSamplePoints(candidate, targetType)) do if SamplePointPassesAimbotPrefilter(samplePoint) then fallbackPoint = samplePoint if IsVisible(subject, candidate, samplePoint) then return samplePoint, true, true end end end return fallbackPoint, false, fallbackPoint ~= nil end local candidates = GetAimbotTargetPartCandidates(subject, aimPart) if aimPart == 'CrosshairClosest' then local bestPart, bestPoint, bestVisible, bestScore = nil, nil, false, nil for _, candidate in ipairs(candidates) do local point, visible, passed = ResolvePrefilteredVisiblePoint(candidate) if passed and point then local screenPoint = camera:WorldToViewportPoint(point) local score = (Vector2.new(screenPoint.X, screenPoint.Y) - mousePosition).Magnitude if (visible and not bestVisible) or (visible == bestVisible and (bestScore == nil or score < bestScore)) then bestPart, bestPoint, bestVisible, bestScore = candidate, point, visible, score end end end if bestPart then WriteCachedAimbotVisibility(subject, aimPart, bestPart, bestPoint, bestVisible, cacheProfile) end return bestPart, bestPoint, bestVisible end if aimPart == 'Closest' then local closestOrigin = rootPosition or camera.CFrame.Position local closestVisiblePart = nil local closestVisiblePoint = nil local closestVisibleDistanceSquared = nil local closestFallbackPart = nil local closestFallbackPoint = nil local closestFallbackDistanceSquared = nil for _, candidate in ipairs(candidates) do if candidate and candidate:IsA('BasePart') then for _, samplePoint in ipairs(GetAimbotTargetPartSamplePoints(candidate, targetType)) do if SamplePointPassesAimbotPrefilter(samplePoint) then local samplePointOffset = samplePoint - closestOrigin local samplePointDistanceSquared = samplePointOffset:Dot(samplePointOffset) if IsVisible(subject, candidate, samplePoint) then if not closestVisibleDistanceSquared or samplePointDistanceSquared < closestVisibleDistanceSquared then closestVisiblePart = candidate closestVisiblePoint = samplePoint closestVisibleDistanceSquared = samplePointDistanceSquared end elseif not closestFallbackDistanceSquared or samplePointDistanceSquared < closestFallbackDistanceSquared then closestFallbackPart = candidate closestFallbackPoint = samplePoint closestFallbackDistanceSquared = samplePointDistanceSquared end end end end end if closestVisiblePart then WriteCachedAimbotVisibility(subject, aimPart, closestVisiblePart, closestVisiblePoint, true, cacheProfile) return closestVisiblePart, closestVisiblePoint, true end if closestFallbackPart then WriteCachedAimbotVisibility( subject, aimPart, closestFallbackPart, closestFallbackPoint, false, cacheProfile ) end return closestFallbackPart, closestFallbackPoint, false end if aimPart == 'Random' then local headCandidates = {} local bodyCandidates = {} for _, candidate in ipairs(candidates) do if IsHeadAimbotTargetPart(candidate) then headCandidates[#headCandidates + 1] = candidate else bodyCandidates[#bodyCandidates + 1] = candidate end end local randomBucketRoll = RivalsRuntimeBridge.Extras.RandomBucketRoll() local bucketOrder = randomBucketRoll == 1 and { headCandidates, bodyCandidates } or { bodyCandidates, headCandidates } local fallbackPart = nil local fallbackPoint = nil for _, bucket in ipairs(bucketOrder) do local visibleCandidates = {} for _, candidate in ipairs(bucket) do local visiblePoint, isVisible, passedPrefilter = ResolvePrefilteredVisiblePoint(candidate) if passedPrefilter and not fallbackPart then fallbackPart = candidate fallbackPoint = visiblePoint end if isVisible then visibleCandidates[#visibleCandidates + 1] = { part = candidate, point = visiblePoint, } end end if #visibleCandidates > 0 then local randomIndex = math.random(1, #visibleCandidates) local randomEntry = visibleCandidates[randomIndex] return randomEntry.part, randomEntry.point, true end end return fallbackPart, fallbackPoint, false end local fallbackPart = nil local fallbackPoint = nil for _, candidate in ipairs(candidates) do local visiblePoint, isVisible, passedPrefilter = ResolvePrefilteredVisiblePoint(candidate) if passedPrefilter and not fallbackPart then fallbackPart = candidate fallbackPoint = visiblePoint end if isVisible then WriteCachedAimbotVisibility(subject, aimPart, candidate, visiblePoint, true, cacheProfile) return candidate, visiblePoint, true end end if fallbackPart then WriteCachedAimbotVisibility(subject, aimPart, fallbackPart, fallbackPoint, false, cacheProfile) end return fallbackPart, fallbackPoint, false end end local IsAimbotPlayerTargetAlive = function(subject) local humanoid = subject:FindFirstChild('Humanoid') return humanoid ~= nil and humanoid.Health > 0 end local BuildAimbotTargetInfo = function( subject, targetType, aimPart, mousePosition, player, maxDistance, fovRadius, ignoreFov, camera, cacheProfile, allowHiddenTargets, requireVisible ) if not subject or not subject.Parent then RivalsRuntimeBridge.RecordTargetValidityRejection('subject_invalid', subject, player, targetType) return nil end if player and not IsAimbotPlayerTargetAlive(subject) then local humanoid = subject and subject:FindFirstChild('Humanoid') RivalsRuntimeBridge.RecordTargetValidityRejection('not_alive', subject, player, targetType, { humanoidHealth = humanoid and humanoid.Health, hasHumanoid = humanoid ~= nil, }) return nil end if player and AimbotBridge.ShouldIgnoreAimbotKatanaDeflectTarget(player, AimbotBridge.ResolveAimbotEquippedItem()) then RivalsRuntimeBridge.RecordTargetValidityRejection('katana_deflect', subject, player, targetType) return nil end camera = camera or Workspace.CurrentCamera if not camera then RivalsRuntimeBridge.RecordTargetValidityRejection('no_camera', subject, player, targetType) return nil end maxDistance = maxDistance or DEFAULT_ESP_MAX_DISTANCE if ignoreFov == nil then ignoreFov = AimbotBridge.IsAimbotIgnoreFovEnabled() end if not ignoreFov and type(fovRadius) ~= 'number' then fovRadius = Options.P2S1S2 and Options.P2S1S2.Value or 170 end allowHiddenTargets = allowHiddenTargets == true requireVisible = requireVisible == true local equippedItem = AimbotBridge.ResolveAimbotEquippedItem() local equippedInfo = equippedItem and equippedItem.Info or nil local effectiveAimPart = RivalsRuntimeBridge.Extras.OverrideAimPart( ((equippedInfo and equippedInfo.Class == 'Melee') or (equippedInfo and equippedInfo.Type == 'Melee')) and 'Closest' or aimPart ) local rootPosition = HumanoidRootPart and HumanoidRootPart.Position or nil local maxDistanceSquared = maxDistance * maxDistance local part, worldPosition, isVisible = ResolvePrefilteredAimbotTargetPart( subject, effectiveAimPart, targetType, mousePosition, maxDistance, fovRadius, ignoreFov, camera, cacheProfile ) if not part or not part:IsA('BasePart') or not worldPosition then RivalsRuntimeBridge.RecordTargetValidityRejection('no_part_resolved', subject, player, targetType) return nil end if not isVisible and RivalsRuntimeBridge.Extras.ManipulateAllows(subject, part, worldPosition) then isVisible = true end if requireVisible and not isVisible then RivalsRuntimeBridge.RecordTargetValidityRejection('not_visible', subject, player, targetType) return nil elseif not isVisible and not allowHiddenTargets then RivalsRuntimeBridge.RecordTargetValidityRejection('not_visible', subject, player, targetType) return nil end local extrasAllowed, extrasReason = RivalsRuntimeBridge.Extras.TargetAllowed( subject, player, part, isVisible, worldPosition, mousePosition, fovRadius, ignoreFov, camera ) if not extrasAllowed then RivalsRuntimeBridge.RecordTargetValidityRejection(extrasReason or 'extras_rule', subject, player, targetType) return nil end local worldDistance = 0 if rootPosition then local rootOffset = worldPosition - rootPosition local worldDistanceSquared = rootOffset:Dot(rootOffset) if worldDistanceSquared > maxDistanceSquared then RivalsRuntimeBridge.RecordTargetValidityRejection('out_of_range', subject, player, targetType, { worldDistance = math.sqrt(worldDistanceSquared), maxDistance = maxDistance, }) return nil end worldDistance = math.sqrt(worldDistanceSquared) end local screenPosition, onScreen = camera:WorldToViewportPoint(worldPosition) local isOnScreen = onScreen and screenPosition.Z > 0 if not ignoreFov and not isOnScreen then RivalsRuntimeBridge.RecordTargetValidityRejection('off_screen', subject, player, targetType) return nil end local screenVector = Vector2.new(screenPosition.X, screenPosition.Y) local screenDistance = (screenVector - mousePosition).Magnitude if not ignoreFov and screenDistance > fovRadius then RivalsRuntimeBridge.RecordTargetValidityRejection('out_of_fov', subject, player, targetType, { screenDistance = screenDistance, fovRadius = fovRadius, }) return nil end local resolvedDisplayName = player and (player.DisplayName or player.Name) or subject.Name RivalsRuntimeBridge.RecordTargetValidityAcquired(resolvedDisplayName, { targetType = targetType, aimPart = effectiveAimPart, isVisible = isVisible, screenDistance = screenDistance, worldDistance = worldDistance, }) return { instance = subject, part = part, player = player, targetType = targetType, worldPosition = worldPosition, worldDistance = worldDistance, screenDistance = screenDistance, selectionDistance = RivalsRuntimeBridge.AimbotTargeting.ApplyPriority( isOnScreen and screenDistance or (AIMBOT_OFFSCREEN_SELECTION_PENALTY + worldDistance), subject, player, worldDistance ), displayName = resolvedDisplayName, isVisible = isVisible, } end local ConsiderBestAimbotTarget = function( bestTarget, subject, targetType, aimPart, mousePosition, maxDistance, fovRadius, ignoreFov, player, camera, cacheProfile, requireVisible ) local targetInfo = BuildAimbotTargetInfo( subject, targetType, aimPart, mousePosition, player, maxDistance, fovRadius, ignoreFov, camera, cacheProfile, nil, requireVisible ) if targetInfo and (not requireVisible or targetInfo.isVisible) and (not bestTarget or targetInfo.selectionDistance < bestTarget.selectionDistance) then return targetInfo end return bestTarget end local RefreshAimbotTargetInfo = function(targetInfo, aimPart, mousePosition, maxDistance, cacheProfile) if not targetInfo then return nil end if targetInfo.player and not ShouldShow(targetInfo.player, false) then return nil end if targetInfo.player and AimbotBridge.ShouldIgnoreAimbotKatanaDeflectTarget(targetInfo.player, AimbotBridge.ResolveAimbotEquippedItem()) then return nil end local subject = targetInfo.instance if targetInfo.player then local fighter = TrackedFightersByPlayer[targetInfo.player] local entity = fighter and fighter.Entity subject = entity and entity.Model if FighterDataCache.ReadEntity(entity) then return nil end end if ShouldIgnoreRivalsRiotShieldTarget( targetInfo.player, subject or (targetInfo.player and targetInfo.player.Character), AimbotBridge.ResolveAimbotEquippedItem() ) then return nil end return BuildAimbotTargetInfo( subject, targetInfo.targetType, aimPart, mousePosition, targetInfo.player, maxDistance, nil, nil, nil, cacheProfile ) end RivalsRuntimeBridge.PrimeAimbotAim = function(targetInfo, item) if not targetInfo then return false end if not RivalsRuntimeBridge.IsReadyToFight() then RivalsRuntimeBridge.LogDiagnosticEvent('PrimeAimbotAim', 'Refused: gate closed') return false end local targetPart = targetInfo.part local worldPosition = targetInfo.worldPosition local camera = Workspace.CurrentCamera if not targetPart or not targetPart.Parent or not worldPosition or not camera then return false end camera.CFrame = CFrame.lookAt(camera.CFrame.Position, worldPosition) return true end RivalsRuntimeBridge.BuildAimbotTargetInfo = BuildAimbotTargetInfo RivalsRuntimeBridge.ConsiderBestAimbotTarget = ConsiderBestAimbotTarget RivalsRuntimeBridge.ResolveVisibleAimbotTargetPart = ResolveVisibleAimbotTargetPart RivalsRuntimeBridge.RefreshAimbotTargetInfo = RefreshAimbotTargetInfo RivalsRuntimeBridge.DestroyAimbotStatusNotification = DestroyAimbotStatusNotification RivalsRuntimeBridge.NotifyAimbotStatus = NotifyAimbotStatus RivalsRuntimeBridge.BuildAimbotSilentStatus = BuildAimbotSilentStatus RivalsRuntimeBridge.EvaluateAimbotSilentRuntime = EvaluateAimbotSilentRuntime RivalsRuntimeBridge.ResolveAimbotSilentHookTarget = ResolveAimbotSilentHookTarget end -- ====================================================================== -- Cursor triggerbot -- ====================================================================== do local CursorTriggerbotRaycastFilter = {} local CursorTriggerbotRaycastParams = RaycastParams.new() CursorTriggerbotRaycastParams.FilterType = Enum.RaycastFilterType.Exclude CursorTriggerbotRaycastParams.FilterDescendantsInstances = CursorTriggerbotRaycastFilter local CursorTriggerbotRaycastFilterCharacter = nil local function ResolveCursorTriggerbotRaycastParams() local character = LP.Character if character ~= CursorTriggerbotRaycastFilterCharacter then CursorTriggerbotRaycastFilterCharacter = character CursorTriggerbotRaycastFilter[1] = character CursorTriggerbotRaycastFilter[2] = nil CursorTriggerbotRaycastParams.FilterDescendantsInstances = CursorTriggerbotRaycastFilter end return CursorTriggerbotRaycastParams end local function ResolveCursorTriggerbotTarget() EnsureAimbotTargetTracking() local camera = Workspace.CurrentCamera if not camera then return nil end local mousePosition = AimbotBridge.GetAimbotPointerPosition() local ray = camera:ViewportPointToRay(mousePosition.X, mousePosition.Y) local raycastParams = ResolveCursorTriggerbotRaycastParams() local result = Workspace:Raycast(ray.Origin, ray.Direction * DEFAULT_ESP_MAX_DISTANCE, raycastParams) local hitInstance = result and result.Instance if not hitInstance then return nil end local aimPart = Options.P2S1D2 and Options.P2S1D2.Value or 'Auto' local subject = hitInstance:FindFirstAncestorWhichIsA('Model') if not subject then return nil end local player = Players:GetPlayerFromCharacter(subject) if player then if not ShouldShow(player, false) or ShouldIgnoreRivalsRiotShieldTarget(player, subject, AimbotBridge.ResolveAimbotEquippedItem()) then return nil end return RivalsRuntimeBridge.BuildAimbotTargetInfo( subject, 'Players', aimPart, mousePosition, player, nil, nil, nil, nil, TRIGGERBOT_VISIBILITY_CACHE_PROFILE ) end if TrackedRangeTargets[subject] then return RivalsRuntimeBridge.BuildAimbotTargetInfo( subject, 'Range Targets', aimPart, mousePosition, nil, nil, nil, nil, nil, TRIGGERBOT_VISIBILITY_CACHE_PROFILE ) end if TrackedPracticeDummies[subject] then return RivalsRuntimeBridge.BuildAimbotTargetInfo( subject, 'Practice Dummies', aimPart, mousePosition, nil, nil, nil, nil, nil, TRIGGERBOT_VISIBILITY_CACHE_PROFILE ) end return nil end local SharedTryTriggerbotShot local SharedGetBestTriggerbotTarget local GetBestTriggerbotTarget do local function IsMeleeFastFireItem(item) local info = item and item.Info or nil return info and (info.Class == 'Melee' or info.Type == 'Melee') and true or false end local function ResolveTriggerbotShotInterval(item) if MeleeFastFire.Enabled and IsMeleeFastFireItem(item) then return MeleeFastFire.IntervalSec end local info = item and item.Info or nil local shotInterval = 0.05 if type(info and info.ShootCooldown) == 'number' then shotInterval = math.max(shotInterval, info.ShootCooldown) end if type(info and info.ShootBurstCooldown) == 'number' then shotInterval = math.max(shotInterval, info.ShootBurstCooldown) end if type(info and info.QuickShotCooldown) == 'number' then shotInterval = math.max(shotInterval, info.QuickShotCooldown) end if type(info and info.ChargeReleaseCooldown) == 'number' then shotInterval = math.max(shotInterval, info.ChargeReleaseCooldown) end if type(info and info.AttackCooldown) == 'number' then shotInterval = math.max(shotInterval, info.AttackCooldown) end if type(info and info.BladeCooldown) == 'number' then shotInterval = math.max(shotInterval, info.BladeCooldown) end local weaponSpeedScale = ResolveRivalsWeaponSpeedScale() return math.max(0.05, shotInterval * weaponSpeedScale) end AimbotBridge.IsShotImmediatelyReady = function(item) if not item then return false end local interval = ResolveTriggerbotShotInterval(item) local lastAt = (MeleeFastFire.Enabled and IsMeleeFastFireItem(item)) and MeleeFastFire.LastShotAt or AimbotTriggerbotLastShotAt if (tick() - lastAt) < interval then return false end if AimbotBridge.ReadAimbotReloadRemaining(item) > 0 then return false end if AimbotBridge.ReadAimbotShootRemaining(item) > 0 then return false end if AimbotBridge.ReadAimbotIsCharging(item) then return false end return true end local function IsTriggerbotHoldItem(item) return item and item.Name == 'Flamethrower' end local function FireTriggerbotShot() if not RivalsRuntimeBridge.IsReadyToFight() then RivalsRuntimeBridge.LogDiagnosticEvent('Triggerbot', 'Refused FireTriggerbotShot: gate closed') return false end if AimbotInputState.SyntheticInputBlocked() then return false end local now = tick() local item = AimbotBridge.ResolveAimbotEquippedItem() local isMelee = MeleeFastFire.Enabled and IsMeleeFastFireItem(item) local floor = isMelee and MeleeFastFire.IntervalSec or 0.05 local lastAt = isMelee and MeleeFastFire.LastShotAt or AimbotTriggerbotLastShotAt if (now - lastAt) < floor then return false end local pointerPosition = AimbotBridge.GetAimbotPointerPosition() local virtualInputManager = Instance.new('VirtualInputManager') virtualInputManager:SendMouseButtonEvent(pointerPosition.X, pointerPosition.Y, 0, true, game, 1) if IsTriggerbotHoldItem(item) then AimbotSilentConnections.TriggerbotHoldingMouse = true AimbotSilentConnections.TriggerbotHoldX = pointerPosition.X AimbotSilentConnections.TriggerbotHoldY = pointerPosition.Y else task.defer( function() virtualInputManager:SendMouseButtonEvent(pointerPosition.X, pointerPosition.Y, 0, false, game, 1) end ) end if isMelee then MeleeFastFire.LastShotAt = now else AimbotTriggerbotLastShotAt = now end return true end local function TryTriggerbotShot(targetInfo, item, shouldPrime) if not targetInfo or not item or AimbotInputState.LeftMouse then AimbotBridge.ReleaseTriggerbotMouseHold() AimbotBridge.ResetTriggerbotReactionState() return false end if not RivalsRuntimeBridge.IsReadyToFight() then RivalsRuntimeBridge.LogDiagnosticEvent('Triggerbot', 'Refused: gate closed') AimbotBridge.ReleaseTriggerbotMouseHold() AimbotBridge.ResetTriggerbotReactionState() return false end if targetInfo.player and IsRivalsSpawnShieldActive(targetInfo.player.Character) then AimbotBridge.ReleaseTriggerbotMouseHold() AimbotBridge.ResetTriggerbotReactionState() return false end if not RivalsRuntimeBridge.Extras.TriggerAllowed(targetInfo, item) then AimbotBridge.ReleaseTriggerbotMouseHold() AimbotBridge.ResetTriggerbotReactionState() return false end local now = tick() local targetIdentity = targetInfo.player or targetInfo.instance or targetInfo.part local reactionTime = Options.P2S1S7 and Options.P2S1S7.Value or 0 if AimbotSilentConnections.TriggerbotTarget ~= targetIdentity then AimbotSilentConnections.TriggerbotTarget = targetIdentity AimbotSilentConnections.TriggerbotTargetAcquiredAt = now if reactionTime > 0 then AimbotBridge.ReleaseTriggerbotMouseHold() return false end elseif (now - AimbotSilentConnections.TriggerbotTargetAcquiredAt) < reactionTime then AimbotBridge.ReleaseTriggerbotMouseHold() return false end local holdTriggerbot = IsTriggerbotHoldItem(item) if holdTriggerbot and AimbotSilentConnections.TriggerbotHoldingMouse then if shouldPrime and RivalsRuntimeBridge.AimbotHitChance.Hold() then RivalsRuntimeBridge.PrimeAimbotAim(targetInfo, item) end return true end if AimbotBridge.ReadAimbotReloadRemaining(item) > 0 or AimbotBridge.ReadAimbotShootRemaining(item) > 0 or AimbotBridge.ReadAimbotIsCharging(item) then AimbotBridge.ReleaseTriggerbotMouseHold() return false end local triggerbotInterval = ResolveTriggerbotShotInterval(item) local isMelee = MeleeFastFire.Enabled and IsMeleeFastFireItem(item) local gateLastAt = isMelee and MeleeFastFire.LastShotAt or AimbotTriggerbotLastShotAt if (now - gateLastAt) < triggerbotInterval then return false end if shouldPrime and RivalsRuntimeBridge.AimbotHitChance.Arm() then RivalsRuntimeBridge.PrimeAimbotAim(targetInfo, item) end if not holdTriggerbot then AimbotBridge.ReleaseTriggerbotMouseHold() end return FireTriggerbotShot() end local CollectBestAimbotTarget = function(requireVisible, ignoreFovOverride) EnsureAimbotTargetTracking() local aimPart = Options.P2S1D2 and Options.P2S1D2.Value or 'Auto' local fovRadius = Options.P2S1S2 and Options.P2S1S2.Value or 170 local maxDistance = DEFAULT_ESP_MAX_DISTANCE local ignoreFov = ignoreFovOverride if ignoreFov == nil then ignoreFov = AimbotBridge.IsAimbotIgnoreFovEnabled() end local mousePosition = AimbotBridge.GetAimbotPointerPosition() local camera = Workspace.CurrentCamera if not camera then return nil end local bestTarget = nil for _, fighter in ipairs(LocalFighterController and LocalFighterController.Objects or {}) do local player = fighter and fighter.Player if player and player ~= LP and player.Parent == Players then local entity = fighter.Entity local subject = entity and entity.Model if not ShouldShow(player, false) then RivalsRuntimeBridge.RecordTargetValidityRejection('team_or_self', subject, player, 'Players') elseif FighterDataCache.ReadEntity(entity) then RivalsRuntimeBridge.RecordTargetValidityRejection('invincible', subject, player, 'Players') elseif ShouldIgnoreRivalsRiotShieldTarget( player, subject or player.Character, AimbotBridge.ResolveAimbotEquippedItem() ) then RivalsRuntimeBridge.RecordTargetValidityRejection('riot_shield', subject, player, 'Players') else bestTarget = RivalsRuntimeBridge.ConsiderBestAimbotTarget( bestTarget, subject, 'Players', aimPart, mousePosition, maxDistance, fovRadius, ignoreFov, player, camera, AIMBOT_VISIBILITY_CACHE_PROFILE, requireVisible ) end end end for model in pairs(TrackedRangeTargets) do if not model.Parent then TrackedRangeTargets[model] = nil else bestTarget = RivalsRuntimeBridge.ConsiderBestAimbotTarget( bestTarget, model, 'Range Targets', aimPart, mousePosition, maxDistance, fovRadius, ignoreFov, nil, camera, AIMBOT_VISIBILITY_CACHE_PROFILE, requireVisible ) end end for model in pairs(TrackedPracticeDummies) do if not model.Parent then TrackedPracticeDummies[model] = nil else bestTarget = RivalsRuntimeBridge.ConsiderBestAimbotTarget( bestTarget, model, 'Practice Dummies', aimPart, mousePosition, maxDistance, fovRadius, ignoreFov, nil, camera, AIMBOT_VISIBILITY_CACHE_PROFILE, requireVisible ) end end RivalsRuntimeBridge.LastAimbotSweepTarget = bestTarget RivalsRuntimeBridge.LastAimbotSweepAt = os.clock() return bestTarget end local GetBestAimbotTarget = CollectBestAimbotTarget local _rawGetBestAimbotTarget = GetBestAimbotTarget local function FindBestZombieAimbotTarget(requireVisible, ignoreFovOverride) local toggle = Toggles and Toggles.EventZombieIncludeInAimbot if not (toggle and toggle.Value == true) then return nil end local ec = ResolveEnemyController() if not ec then return nil end local camera = Workspace.CurrentCamera if not camera then return nil end local aimPart = 'Auto' local aimPartOption = Options and Options.EventZombieAimPart if aimPartOption and type(aimPartOption.Value) == 'string' then aimPart = aimPartOption.Value end local fovRadius = Options.P2S1S2 and Options.P2S1S2.Value or 170 local maxDistance = DEFAULT_ESP_MAX_DISTANCE local ignoreFov = ignoreFovOverride if ignoreFov == nil then ignoreFov = AimbotBridge.IsAimbotIgnoreFovEnabled() end local mousePosition = AimbotBridge.GetAimbotPointerPosition() local best = nil for _, enemy in ipairs(ec.Objects or {}) do local model = enemy.Model or (enemy.Entity and rawget(enemy.Entity, 'Model')) if model and model.Parent then local name = model.Name local isDummy = name == 'Target' or name == 'Dummy' or name == 'DPS Dummy' if not isDummy and (model:FindFirstChildOfClass('Humanoid') or model:FindFirstChild('HitboxHead')) then local towerKit = RivalsRuntimeBridge.TowerKit if towerKit and towerKit.Aim then local info = RivalsRuntimeBridge.ConsiderBestAimbotTarget( nil, model, 'Practice Dummies', towerKit.Aim.SilentPart(model), mousePosition, maxDistance, fovRadius, ignoreFov, nil, camera, AIMBOT_VISIBILITY_CACHE_PROFILE, requireVisible ) if info then local selection = towerKit.Aim.SilentSelection(model, info.selectionDistance) if selection ~= nil then info.selectionDistance = selection if not best or selection < best.selectionDistance then best = info end end end else best = RivalsRuntimeBridge.ConsiderBestAimbotTarget( best, model, 'Practice Dummies', aimPart, mousePosition, maxDistance, fovRadius, ignoreFov, nil, camera, AIMBOT_VISIBILITY_CACHE_PROFILE, requireVisible ) end end end end return best end GetBestAimbotTarget = function(requireVisible, ignoreFovOverride) local humanBest = _rawGetBestAimbotTarget(requireVisible, ignoreFovOverride) local zombieBest = FindBestZombieAimbotTarget(requireVisible, ignoreFovOverride) if not zombieBest then return humanBest end if not humanBest then return zombieBest end if zombieBest.selectionDistance < humanBest.selectionDistance then return zombieBest end return humanBest end RivalsRuntimeBridge.GetBestAimbotTarget = GetBestAimbotTarget -- ====================================================================== -- Aimbot targeting rules (priority, delays, lock) -- ====================================================================== RivalsRuntimeBridge.AimbotTargeting = { Committed = nil, Pending = nil, PendingSince = 0, Challenger = nil, ChallengerSince = 0, LastCallAt = 0, LastTarget = nil, LastTargetAt = 0, SpringPitch = 0, SpringYaw = 0, Circle = nil, CircleAngle = 0, } function RivalsRuntimeBridge.AimbotTargeting.ReadToggle(optionId) return Toggles and Toggles[optionId] and Toggles[optionId].Value == true end function RivalsRuntimeBridge.AimbotTargeting.ReadNumber(optionId, fallback) local option = Options and Options[optionId] local value = option and tonumber(option.Value) if value == nil then return fallback end return value end -- Re-ranks candidates by the chosen priority. The result replaces selectionDistance, and the lowest value wins. function RivalsRuntimeBridge.AimbotTargeting.ApplyPriority(base, subject, player, worldDistance) local option = Options and Options.PX6D2 local mode = option and option.Value if mode == nil or mode == 'Crosshair' then return base end if mode == 'Nearest' then return worldDistance elseif mode == 'Lowest Health' then local humanoid = subject and subject:FindFirstChildOfClass('Humanoid') local health = humanoid and humanoid.Health or 100 return health + math.min(base, 9999) / 10000 elseif mode == 'Cheaters First' then local detector = RivalsRuntimeBridge.CheaterDetector if player and detector and detector.IsFlagged(player) then return base - 1e7 end end return base end function RivalsRuntimeBridge.AimbotTargeting.ResetSpring() RivalsRuntimeBridge.AimbotTargeting.SpringPitch = 0 RivalsRuntimeBridge.AimbotTargeting.SpringYaw = 0 end function RivalsRuntimeBridge.AimbotTargeting.Reset() local targeting = RivalsRuntimeBridge.AimbotTargeting targeting.Committed = nil targeting.Pending = nil targeting.Challenger = nil targeting.LastTarget = nil targeting.ResetSpring() end function RivalsRuntimeBridge.AimbotTargeting.Hold(info, keepChallenger) local targeting = RivalsRuntimeBridge.AimbotTargeting targeting.Committed = info targeting.Pending = nil if not keepChallenger then targeting.Challenger = nil end targeting.LastTarget = info targeting.LastTargetAt = os.clock() return info end -- Wraps the raw best target with reaction delay, switch delay and target lock. -- With every option at its default this returns the raw target unchanged. function RivalsRuntimeBridge.AimbotTargeting.Filter(best) local targeting = RivalsRuntimeBridge.AimbotTargeting local now = os.clock() local reaction = targeting.ReadNumber('PX6S1', 0) local switchDelay = targeting.ReadNumber('PX6S2', 0) local lock = targeting.ReadToggle('PX6T1') if reaction <= 0 and switchDelay <= 0 and not lock then targeting.Committed = nil targeting.Pending = nil targeting.Challenger = nil targeting.LastCallAt = now if best then targeting.LastTarget = best targeting.LastTargetAt = now end return best end local sticky = lock and Options.PX6D1 and Options.PX6D1.Value == 'Until Invalid' if not sticky and now - targeting.LastCallAt > 0.3 then targeting.Committed = nil targeting.Pending = nil targeting.Challenger = nil end targeting.LastCallAt = now local current = nil if targeting.Committed then current = RivalsRuntimeBridge.RefreshAimbotTargetInfo( targeting.Committed, Options.P2S1D2 and Options.P2S1D2.Value or 'Auto', AimbotBridge.GetAimbotPointerPosition(), DEFAULT_ESP_MAX_DISTANCE ) if not current then targeting.Committed = nil targeting.Challenger = nil end end if not current then if not best then targeting.Pending = nil return nil end if reaction <= 0 then return targeting.Hold(best) end if targeting.Pending ~= best.instance then targeting.Pending = best.instance targeting.PendingSince = now end if now - targeting.PendingSince >= reaction then return targeting.Hold(best) end return nil end if lock or not best or best.instance == current.instance then return targeting.Hold(current) end if switchDelay <= 0 then return targeting.Hold(best) end if targeting.Challenger ~= best.instance then targeting.Challenger = best.instance targeting.ChallengerSince = now end if now - targeting.ChallengerSince >= switchDelay then return targeting.Hold(best) end return targeting.Hold(current, true) end RivalsRuntimeBridge.GetFilteredAimbotTarget = function(requireVisible, ignoreFovOverride) return RivalsRuntimeBridge.AimbotTargeting.Filter(GetBestAimbotTarget(requireVisible, ignoreFovOverride)) end -- ====================================================================== -- Combat Bot -- ====================================================================== -- Combat Bot: hands-free fighting that uses the normal silent aim + triggerbot pipeline -- (via AimbotBridge.IsRagebotForcingCombat) plus ordinary Humanoid movement input. -- It never touches shot packets or positions, so the server only ever sees normal play. RivalsRuntimeBridge.CombatBot = { Strafe = 1, NextStrafeAt = 0, NextHopAt = 0, LastReloadAt = 0, LastSwapAt = 0, Moving = false, Bound = false, } function RivalsRuntimeBridge.CombatBot.IsActive() if not (Toggles and Toggles.PX14T1 and Toggles.PX14T1.Value == true) then return false end local picker = Options and Options.PX14T1K if picker and type(picker.GetState) == 'function' then return picker:GetState() == true end return true end function RivalsRuntimeBridge.CombatBot.ReadAmmo(item) local ok, ammo = pcall(function() return item:Get('Ammo') end) if ok and type(ammo) == 'number' then return ammo end return nil end function RivalsRuntimeBridge.CombatBot.IsMelee(item) local info = type(item) == 'table' and item.Info or nil return type(info) == 'table' and (info.Class == 'Melee' or info.Type == 'Melee') end function RivalsRuntimeBridge.CombatBot.TapKey(keyCode) local virtualInputManager = Instance.new('VirtualInputManager') virtualInputManager:SendKeyEvent(true, keyCode, false, game) virtualInputManager:SendKeyEvent(false, keyCode, false, game) end function RivalsRuntimeBridge.CombatBot.FindSlotItem(items, slot) local direct = items[slot] or items[tostring(slot)] if type(direct) == 'table' then return direct end for key, item in pairs(items) do if type(item) == 'table' then local ok, value = pcall( function() return tonumber(item:Get('Slot') or item:Get('Index') or item:Get('ItemSlot') or key) end ) if ok and value == slot then return item end end end return nil end function RivalsRuntimeBridge.CombatBot.TrySwap(now, current) local bot = RivalsRuntimeBridge.CombatBot if now - bot.LastSwapAt < 0.4 then return false end local fighter = ResolveLocalFighter() local items = fighter and type(fighter.Items) == 'table' and fighter.Items or nil if not items then return false end for slot = 1, 3 do local candidate = bot.FindSlotItem(items, slot) if candidate and candidate ~= current and not bot.IsMelee(candidate) then local ammo = bot.ReadAmmo(candidate) if ammo == nil or ammo > 0 then bot.LastSwapAt = now local ok = pcall(function() fighter:EquipItem(slot) end) if not ok then bot.TapKey(({ Enum.KeyCode.One, Enum.KeyCode.Two, Enum.KeyCode.Three })[slot]) end return true end end end return false end -- Reloads or swaps away from an empty gun. function RivalsRuntimeBridge.CombatBot.ManageWeapon() local bot = RivalsRuntimeBridge.CombatBot if not (Toggles.PX14T3 and Toggles.PX14T3.Value == true) then return end local item = AimbotBridge.ResolveAimbotEquippedItem() if type(item) ~= 'table' or bot.IsMelee(item) then return end local ammo = bot.ReadAmmo(item) if ammo == nil or ammo > 0 or AimbotBridge.ReadAimbotReloadRemaining(item) > 0 then return end local now = os.clock() local mode = Options.PX14D2 and Options.PX14D2.Value or 'Reload' if mode == 'Swap' and bot.TrySwap(now, item) then return end if now - bot.LastReloadAt >= 1 then bot.LastReloadAt = now bot.TapKey(Enum.KeyCode.R) end end -- Ordinary strafing / hopping through Humanoid input, only while a target is engaged and you are not steering. function RivalsRuntimeBridge.CombatBot.UpdateMovement() local bot = RivalsRuntimeBridge.CombatBot local mode = Options and Options.PX14D3 and Options.PX14D3.Value or 'Off' local targeting = RivalsRuntimeBridge.AimbotTargeting local target = targeting.LastTarget local engaged = mode ~= 'Off' and bot.IsActive() and target ~= nil and os.clock() - targeting.LastTargetAt < 0.5 local character = LP.Character local humanoid = character and character:FindFirstChildOfClass('Humanoid') if not engaged or not humanoid or humanoid.Health <= 0 or not RivalsRuntimeBridge.IsReadyToFight() then bot.Moving = false return end if not bot.Moving and humanoid.MoveDirection.Magnitude > 0.1 then return end bot.Moving = true local now = os.clock() local lead = 0 if Toggles.PX14T2 and Toggles.PX14T2.Value == true then local theirs = typeof(target.instance) == 'Instance' and target.instance:FindFirstChildOfClass('Humanoid') or nil if theirs then lead = humanoid.Health / math.max(humanoid.MaxHealth, 1) - theirs.Health / math.max(theirs.MaxHealth, 1) end end -- behind on health: change direction faster and hop more; well ahead: stay steadier local pace = lead < -0.05 and 0.65 or (lead > 0.25 and 1.4 or 1) if now >= bot.NextStrafeAt then local interval = math.clamp(tonumber(Options.PX14S1 and Options.PX14S1.Value) or 0.45, 0.1, 3) bot.NextStrafeAt = now + interval * pace * (0.6 + math.random() * 0.8) bot.Strafe = (math.random() < 0.5) and -1 or 1 if mode == 'Random' then bot.Forward = math.random(-1, 1) else bot.Forward = 0 end end if now >= bot.NextHopAt then bot.NextHopAt = now + (0.8 + math.random() * 1.2) * pace local chance = math.clamp(tonumber(Options.PX14S2 and Options.PX14S2.Value) or 30, 0, 100) / 100 if math.random() < chance and humanoid.FloorMaterial ~= Enum.Material.Air then humanoid.Jump = true end end humanoid:Move(Vector3.new(bot.Strafe, 0, bot.Forward or 0), true) end function RivalsRuntimeBridge.CombatBot.Step() local bot = RivalsRuntimeBridge.CombatBot if not bot.IsActive() then bot.Moving = false return end bot.UpdateMovement() end function RivalsRuntimeBridge.CombatBot.Start() local bot = RivalsRuntimeBridge.CombatBot if bot.Bound then return end bot.Bound = true -- after the default controls (Input priority) so the strafe input wins for that frame RunService:BindToRenderStep('ZkxHubCombatBotMove', Enum.RenderPriority.Input.Value + 1, bot.Step) task.spawn(function() while bot.Bound do task.wait(0.2) if bot.IsActive() then pcall(bot.ManageWeapon) end end end) end function RivalsRuntimeBridge.CombatBot.Destroy() local bot = RivalsRuntimeBridge.CombatBot if bot.Bound then bot.Bound = false pcall(function() RunService:UnbindFromRenderStep('ZkxHubCombatBotMove') end) end bot.Moving = false end -- Snapline. The target record the aimbot keeps is only refreshed when the aimbot looks: every frame for camera aim, but for hitscan guns -- only at the moment of a shot. So the line is drawn from (a) that record while it is fresh, else (b) a preview of the target the -- aimbot would take if you fired now, and its end is re-projected from the part's CURRENT position every frame, so it follows without lag. -- Preview is only allowed when the aimbot could act: with the Silent Aim rules on and 'scoped in' required it shows the moment the scope is -- up; otherwise while aim / fire is held. Smoothing only shapes the appearance (slide between targets, fade); 0 = hard snap. RivalsRuntimeBridge.AimbotTargeting.Preview = { Info = nil, At = 0 } RivalsRuntimeBridge.AimbotTargeting.Line = { Info = nil, Subject = nil, Offset = Vector3.zero, Glide = Vector2.zero, Alpha = 0, Shown = false, Switched = false, DrawnEnd = nil } function RivalsRuntimeBridge.AimbotTargeting.PreviewAllowed() local extras = RivalsRuntimeBridge.Extras if not AimbotBridge.IsAimbotSelected() or not IsAimbotGameReady() then return false end local item = AimbotBridge.ResolveAimbotEquippedItem() if not AimbotBridge.CanUseAimbotWithItem(item) then return false end local held = AimbotBridge.IsAimbotShootInputHeld() if not held and not AimbotBridge.IsAimbotEnabled() then return false end if type(extras) ~= 'table' then return held end if type(extras.ActivationOk) == 'function' and extras.ActivationOk(item) == false then return false end if type(extras.RulesOn) == 'function' and extras.RulesOn() and extras.Tog('KPA2', false) then return true end return held end -- the target the aimbot would take right now, without touching the reaction / switch delay state function RivalsRuntimeBridge.AimbotTargeting.PeekTarget() local targeting = RivalsRuntimeBridge.AimbotTargeting local committed = targeting.Committed if committed and targeting.ReadToggle('PX6T1') then local refreshed = RivalsRuntimeBridge.RefreshAimbotTargetInfo( committed, Options.P2S1D2 and Options.P2S1D2.Value or 'Auto', AimbotBridge.GetAimbotPointerPosition(), DEFAULT_ESP_MAX_DISTANCE ) if refreshed then return refreshed end end return RivalsRuntimeBridge.GetBestAimbotTarget() end function RivalsRuntimeBridge.AimbotTargeting.PreviewTarget() local targeting = RivalsRuntimeBridge.AimbotTargeting local preview = targeting.Preview local okAllowed, allowed = pcall(targeting.PreviewAllowed) if not okAllowed or not allowed then preview.Info, preview.At = nil, 0 return nil end local now = os.clock() local info = preview.Info if info and info.part and info.part.Parent and now - preview.At < 0.05 then return info end preview.At = now local ok, found = pcall(targeting.PeekTarget) preview.Info = ok and found or nil return preview.Info end function RivalsRuntimeBridge.AimbotTargeting.UpdateSnapline(info, camera, deltaTime) local targeting = RivalsRuntimeBridge.AimbotTargeting local state = targeting.Line local line = targeting.Snapline local dt = math.clamp(type(deltaTime) == 'number' and deltaTime or 1 / 60, 0, 0.1) local smoothing = math.clamp(targeting.ReadNumber('PX8S6', 35), 0, 100) / 100 local instant = smoothing <= 0 local viewport = camera and camera.ViewportSize or Vector2.new(0, 0) local mode = Options and Options.PX8D1 and Options.PX8D1.Value or 'Crosshair' local origin if mode == 'Mouse' then origin = UserInputService:GetMouseLocation() elseif mode == 'Bottom' then origin = Vector2.new(viewport.X / 2, viewport.Y) elseif mode == 'Top' then origin = Vector2.new(viewport.X / 2, 0) else origin = Vector2.new(viewport.X / 2, viewport.Y / 2) end local screenEnd = nil if info and camera then local part = info.part if part and part.Parent then if state.Info ~= info then -- a new record: remember where its aim point sits relative to the part, and re-apply that to the part's live position local world = info.worldPosition local offset = typeof(world) == 'Vector3' and (world - part.Position) or Vector3.zero state.Offset = offset.Magnitude < 8 and offset or Vector3.zero state.Info = info local subject = info.player or info.instance or part if state.Subject ~= subject then state.Subject = subject state.Switched = true end end local screen, onScreen = camera:WorldToViewportPoint(part.Position + state.Offset) if onScreen and screen.Z > 0 then screenEnd = Vector2.new(screen.X, screen.Y) end end end if screenEnd and (state.Switched or not state.Shown) then -- first frame on a target: slide in from where the line was (or out of the origin) instead of jumping if instant then state.Glide = Vector2.zero else state.Glide = (state.Shown and state.DrawnEnd or origin) - screenEnd end state.Switched = false end if instant then state.Glide = Vector2.zero else state.Glide = state.Glide * math.exp(-dt * (80 - 72 * smoothing)) if state.Glide.Magnitude < 0.3 then state.Glide = Vector2.zero end end local goal = screenEnd and 1 or 0 if instant then state.Alpha = goal else state.Alpha = state.Alpha + (goal - state.Alpha) * (1 - math.exp(-dt * (goal == 1 and 40 or 18))) if math.abs(goal - state.Alpha) < 0.02 then state.Alpha = goal end end if state.Alpha <= 0 or (not screenEnd and not state.DrawnEnd) then if line then line.Visible = false end state.Shown, state.Subject, state.Info, state.DrawnEnd, state.Switched = false, nil, nil, nil, false return end if not line then line = Drawing.new('Line') targeting.Snapline = line end -- fading out (no target any more): hold the last place local finalEnd = screenEnd and (screenEnd + state.Glide) or state.DrawnEnd state.DrawnEnd = finalEnd state.Shown = true local color if targeting.ReadToggle('PX8T5') then color = Color3.fromHSV((os.clock() * targeting.ReadNumber('PX8S4', 3) * 0.08) % 1, 0.9, 1) else local colorOption = Options and Options.PX8C2 color = colorOption and colorOption.Value or Color3.new(1, 1, 1) end line.From = origin line.To = finalEnd line.Thickness = targeting.ReadNumber('PX8S5', 1) line.Color = color line.Transparency = state.Alpha line.Visible = true end function RivalsRuntimeBridge.AimbotTargeting.UpdateCircle(deltaTime) local targeting = RivalsRuntimeBridge.AimbotTargeting local circle = targeting.Circle local target = targeting.LastTarget local camera = Workspace.CurrentCamera local wantCircle, wantLine = targeting.ReadToggle('PX8T1'), targeting.ReadToggle('PX8T4') local fresh = target ~= nil and os.clock() - targeting.LastTargetAt < 0.25 local position = nil if wantCircle and fresh and camera then local part = target.part if part and part.Parent then local screen, onScreen = camera:WorldToViewportPoint(target.worldPosition or part.Position) if onScreen and screen.Z > 0 then position = Vector2.new(screen.X, screen.Y) end end end local lineTarget = nil if wantLine and camera then lineTarget = fresh and target or targeting.PreviewTarget() end targeting.UpdateSnapline(lineTarget, camera, deltaTime) if not position or not wantCircle then if circle then circle.Visible = false end return end if not circle then circle = Drawing.new('Circle') circle.NumSides = 48 targeting.Circle = circle end local radius = targeting.ReadNumber('PX8S1', 18) if targeting.ReadToggle('PX8T3') then targeting.CircleAngle = ( targeting.CircleAngle + (type(deltaTime) == 'number' and deltaTime or 1 / 60) * targeting.ReadNumber('PX8S3', 3) ) % (math.pi * 2) radius = radius * (1 + 0.18 * math.sin(targeting.CircleAngle)) end local filled = targeting.ReadToggle('PX8T2') local colorOption = Options and Options.PX8C1 circle.Position = position circle.Radius = radius circle.Thickness = targeting.ReadNumber('PX8S2', 2) circle.Filled = filled circle.Color = colorOption and colorOption.Value or Color3.fromRGB(255, 70, 70) circle.Transparency = filled and 0.35 or 0.9 circle.Visible = true end function RivalsRuntimeBridge.AimbotTargeting.Destroy() local targeting = RivalsRuntimeBridge.AimbotTargeting if targeting.Circle then pcall(function() targeting.Circle.Visible = false targeting.Circle:Remove() end) end if targeting.Snapline then pcall(function() targeting.Snapline.Visible = false targeting.Snapline:Remove() end) end targeting.Snapline = nil targeting.Line = { Info = nil, Subject = nil, Offset = Vector3.zero, Glide = Vector2.zero, Alpha = 0, Shown = false, Switched = false, DrawnEnd = nil } targeting.Preview = { Info = nil, At = 0 } targeting.Circle = nil targeting.Reset() end function RivalsRuntimeBridge.FlickbotNormal(rng, mean, standardDeviation) local first = math.max(rng:NextNumber(), 1e-15) local second = rng:NextNumber() return mean + standardDeviation * (math.sqrt(-2 * math.log(first)) * math.cos(2 * math.pi * second)) end function RivalsRuntimeBridge.FlickbotGamma(rng, shape, scale) local factor = 1 if shape < 1 then factor = rng:NextNumber() ^ (1 / shape) shape = shape + 1 end local adjustedShape = shape - (1 / 3) local root = math.sqrt(9 * adjustedShape) while true do local sample = RivalsRuntimeBridge.FlickbotNormal(rng, 0, 1) local candidate = (1 + (sample / root)) ^ 3 if candidate > 0 then local uniform = math.max(rng:NextNumber(), 1e-15) if uniform < 1 - (0.0331 * sample ^ 4) or math.log(uniform) < (0.5 * sample * sample) + adjustedShape * (1 - candidate + math.log(candidate)) then return adjustedShape * candidate * scale * factor end end end end function RivalsRuntimeBridge.FlickbotErf(value) local sign = value < 0 and -1 or 1 local magnitude = math.abs(value) local factor = 1 / (1 + 0.3275911 * magnitude) local polynomial = 0.254829592 * factor - 0.284496736 * factor ^ 2 + 1.421413741 * factor ^ 3 - 1.453152027 * factor ^ 4 + 1.061405429 * factor ^ 5 return sign * (1 - polynomial * math.exp(-magnitude * magnitude)) end function RivalsRuntimeBridge.FlickbotLognormalCdf(timeMs, startMs, mean, sigma) if timeMs <= startMs then return 0 end return 0.5 * (1 + RivalsRuntimeBridge.FlickbotErf((math.log(timeMs - startMs) - mean) / (sigma * math.sqrt(2)))) end function RivalsRuntimeBridge.FlickbotLognormalPdf(timeMs, startMs, mean, sigma) if timeMs <= startMs then return 0 end local elapsed = timeMs - startMs local standardized = (math.log(elapsed) - mean) / sigma return math.exp(-0.5 * standardized * standardized) / (sigma * math.sqrt(2 * math.pi) * elapsed) end function RivalsRuntimeBridge.FlickbotBump(value) if value <= 0 or value >= 1 then return 0 end return value * value * (1 - value) ^ 3 / 0.03456 end function RivalsRuntimeBridge.ApplyFlickbotProfile() local flickbot = RivalsRuntimeBridge.Flickbot local durationMs = math.max(Options.P2S1S10 and Options.P2S1S10.Value or 110, 20) flickbot.FittsA = durationMs / 4 flickbot.FittsB = durationMs / 4 flickbot.TargetWidth = 80 flickbot.PeakTimeRatio = 0.32 flickbot.PrimarySigmaMin = 0.2 flickbot.PrimarySigmaMax = 0.26 flickbot.UndershootMin = 0.97 flickbot.UndershootMax = 1 flickbot.OvershootProbability = 0.08 flickbot.OvershootMin = 1.01 flickbot.OvershootMax = 1.04 flickbot.SecondCorrectionProbability = 0 flickbot.CorrectionSigmaMin = 0.1 flickbot.CorrectionSigmaMax = 0.14 flickbot.CurvatureScale = math.clamp(Options.P2S1S11 and Options.P2S1S11.Value or 12, 0, 50) / 1000 local humanness = math.clamp(Options.P2S1S12 and Options.P2S1S12.Value or 30, 0, 100) / 30 flickbot.OuSigma = 0.5 * humanness flickbot.TremorAmplitudeMin = 0.05 * humanness flickbot.TremorAmplitudeMax = 0.18 * humanness flickbot.SignalDependentNoise = 0.02 end function RivalsRuntimeBridge.GenerateFlickbotTrajectory(startX, startY, targetX, targetY) RivalsRuntimeBridge.ApplyFlickbotProfile() local flickbot = RivalsRuntimeBridge.Flickbot local rng = flickbot.Rng local function randomBetween(minimum, maximum) return minimum + rng:NextNumber() * (maximum - minimum) end local function normal(mean, standardDeviation) return RivalsRuntimeBridge.FlickbotNormal(rng, mean, standardDeviation) end local deltaX = targetX - startX local deltaY = targetY - startY local distance = math.sqrt(deltaX * deltaX + deltaY * deltaY) if distance < 1 then return {} end local directionX = deltaX / distance local directionY = deltaY / distance local directionAngle = math.atan2(deltaY, deltaX) local indexOfDifficulty = math.log(distance / flickbot.TargetWidth + 1) / math.log(2) local duration = math.max((flickbot.FittsA + flickbot.FittsB * indexOfDifficulty) * math.exp(normal(0, 0.08)), 80) local distanceFactor if randomBetween(0, 1) < flickbot.OvershootProbability then distanceFactor = randomBetween(flickbot.OvershootMin, flickbot.OvershootMax) else distanceFactor = randomBetween(flickbot.UndershootMin, flickbot.UndershootMax) end local primaryDistance = distance * distanceFactor local primarySigma = randomBetween(flickbot.PrimarySigmaMin, flickbot.PrimarySigmaMax) local peakRatio = randomBetween(flickbot.PeakTimeRatio - 0.03, flickbot.PeakTimeRatio + 0.03) local primaryMean = math.log(duration * peakRatio) + primarySigma * primarySigma local corrections = {} local remaining = distance - primaryDistance if math.abs(remaining) > 0.5 then local sign = remaining > 0 and 1 or -1 local correctionDistance = math.abs(remaining) * randomBetween(0.88, 1.02) local correctionSigma = randomBetween(flickbot.CorrectionSigmaMin, flickbot.CorrectionSigmaMax) local correctionPeak = randomBetween(0.12, 0.18) corrections[#corrections + 1] = { distance = correctionDistance, startMs = duration * randomBetween(0.55, 0.68), mean = math.log(duration * correctionPeak) + correctionSigma * correctionSigma, sigma = correctionSigma, directionX = directionX * sign, directionY = directionY * sign, } local secondRemaining = remaining - correctionDistance * sign if math.abs(secondRemaining) > 0.3 and randomBetween(0, 1) < flickbot.SecondCorrectionProbability then local secondSign = secondRemaining > 0 and 1 or -1 local secondSigma = randomBetween(0.1, 0.16) local secondPeak = randomBetween(0.08, 0.12) corrections[#corrections + 1] = { distance = math.abs(secondRemaining) * randomBetween(0.85, 1.05), startMs = duration * randomBetween(0.78, 0.88), mean = math.log(duration * secondPeak) + secondSigma * secondSigma, sigma = secondSigma, directionX = directionX * secondSign, directionY = directionY * secondSign, } end end local angleScale = 0.5 + 0.8 * math.abs(math.sin(directionAngle)) - 0.15 * math.abs(math.cos(directionAngle)) local curvature = distance * flickbot.CurvatureScale * angleScale * normal(0, 1) local tremorFrequency = randomBetween(flickbot.TremorFrequencyMin, flickbot.TremorFrequencyMax) local tremorAmplitude = randomBetween(flickbot.TremorAmplitudeMin, flickbot.TremorAmplitudeMax) local tremorPhaseX = randomBetween(0, 2 * math.pi) local tremorPhaseY = randomBetween(0, 2 * math.pi) local noiseX = 0 local noiseY = 0 local endTime = duration * 1.15 local sampleTimes = {} local sampleTime = 0 while sampleTime < endTime do sampleTime = sampleTime + math.clamp( RivalsRuntimeBridge.FlickbotGamma( rng, flickbot.GammaShape, flickbot.SampleDeltaMean / flickbot.GammaShape ), 2, 25 ) if sampleTime <= endTime + 15 then sampleTimes[#sampleTimes + 1] = sampleTime end end local trajectory = {} for index, timeMs in ipairs(sampleTimes) do local deltaMs = index > 1 and (timeMs - sampleTimes[index - 1]) or flickbot.SampleDeltaMean local deltaSeconds = deltaMs / 1000 local primaryProgress = RivalsRuntimeBridge.FlickbotLognormalCdf(timeMs, 0, primaryMean, primarySigma) local x = startX + directionX * primaryDistance * primaryProgress - directionY * curvature * RivalsRuntimeBridge.FlickbotBump(primaryProgress) local y = startY + directionY * primaryDistance * primaryProgress + directionX * curvature * RivalsRuntimeBridge.FlickbotBump(primaryProgress) for _, correction in ipairs(corrections) do local correctionProgress = RivalsRuntimeBridge.FlickbotLognormalCdf(timeMs, correction.startMs, correction.mean, correction.sigma) x = x + correction.directionX * correction.distance * correctionProgress y = y + correction.directionY * correction.distance * correctionProgress end local velocity = primaryDistance * RivalsRuntimeBridge.FlickbotLognormalPdf(timeMs, 0, primaryMean, primarySigma) for _, correction in ipairs(corrections) do velocity = velocity + correction.distance * RivalsRuntimeBridge.FlickbotLognormalPdf( timeMs, correction.startMs, correction.mean, correction.sigma ) end noiseX = noiseX + (-flickbot.OuTheta * noiseX * deltaSeconds + flickbot.OuSigma * math.sqrt(deltaSeconds) * normal(0, 1)) noiseY = noiseY + (-flickbot.OuTheta * noiseY * deltaSeconds + flickbot.OuSigma * math.sqrt(deltaSeconds) * normal(0, 1)) local timeSeconds = timeMs / 1000 local tremorScale = 1 / (1 + velocity * 0.3) local tremorX = math.sin(2 * math.pi * tremorFrequency * timeSeconds + tremorPhaseX) local tremorY = math.sin(2 * math.pi * tremorFrequency * timeSeconds + tremorPhaseY) trajectory[#trajectory + 1] = { x = x + noiseX + tremorAmplitude * tremorScale * tremorX + flickbot.SignalDependentNoise * velocity * normal(0, 1), y = y + noiseY + tremorAmplitude * tremorScale * tremorY + flickbot.SignalDependentNoise * velocity * normal(0, 1), t = timeMs, } end return trajectory end function RivalsRuntimeBridge.StartFlickbot() local flickbot = RivalsRuntimeBridge.Flickbot local camera = Workspace.CurrentCamera if RivalsRuntimeBridge.MovementRecorder.IsCameraClaimed() or not camera or not RivalsRuntimeBridge.IsReadyToFight() then return false end local targetInfo = GetBestAimbotTarget(true) local targetPart = targetInfo and targetInfo.part or nil if not targetPart or not targetPart.Parent then return false end local targetPoint = camera:WorldToViewportPoint(targetPart.Position) if targetPoint.Z <= 0 then return false end local viewport = camera.ViewportSize local generated = RivalsRuntimeBridge.GenerateFlickbotTrajectory(viewport.X * 0.5, viewport.Y * 0.5, targetPoint.X, targetPoint.Y) if #generated < 2 then return false end local trajectory = table.create(#generated) for index, point in ipairs(generated) do trajectory[index] = { direction = camera:ViewportPointToRay(point.x, point.y).Direction, t = point.t, } end flickbot.Trajectory = trajectory flickbot.TrajectoryIndex = 1 flickbot.ElapsedMs = 0 flickbot.TotalMs = trajectory[#trajectory].t flickbot.BakedEndDirection = trajectory[#trajectory].direction flickbot.Selected = targetInfo flickbot.State = 'Flicking' return true end function RivalsRuntimeBridge.SampleFlickbotTrajectory(deltaTime) local flickbot = RivalsRuntimeBridge.Flickbot local trajectory = flickbot.Trajectory flickbot.ElapsedMs = flickbot.ElapsedMs + deltaTime * 1000 local index = flickbot.TrajectoryIndex while index < #trajectory and trajectory[index + 1].t <= flickbot.ElapsedMs do index = index + 1 end flickbot.TrajectoryIndex = index local current = trajectory[index] local following = trajectory[index + 1] if not following then return trajectory[#trajectory].direction, true end local interval = following.t - current.t local alpha = interval > 0 and math.clamp((flickbot.ElapsedMs - current.t) / interval, 0, 1) or 1 return current.direction:Lerp(following.direction, alpha), false end function RivalsRuntimeBridge.FinishFlickbotToCooldown() local flickbot = RivalsRuntimeBridge.Flickbot flickbot.Selected = nil flickbot.Trajectory = nil local cooldownMs = math.clamp(Options.P2S1S9 and Options.P2S1S9.Value or 250, 0, 2000) if cooldownMs > 0 then flickbot.State = 'Cooldown' flickbot.Timer = cooldownMs / 1000 else flickbot.State = 'Idle' flickbot.Timer = 0 end end function RivalsRuntimeBridge.IsFlickbotCameraClaimed() local state = RivalsRuntimeBridge.Flickbot.State return state == 'Flicking' or state == 'PostShot' end function RivalsRuntimeBridge.UpdateFlickbot(deltaTime) local flickbot = RivalsRuntimeBridge.Flickbot if RivalsRuntimeBridge.MovementRecorder.IsCameraClaimed() then if flickbot.State ~= 'Idle' or flickbot.WasHeld then RivalsRuntimeBridge.ResetFlickbot() end return end local enabled = Toggles.P2S1T12 and Toggles.P2S1T12.Value == true local keypicker = Options.P2S1T12K local held = enabled and keypicker and type(keypicker.GetState) == 'function' and keypicker:GetState() == true if not enabled then if flickbot.State ~= 'Idle' or flickbot.WasHeld then RivalsRuntimeBridge.ResetFlickbot() end return end if held and not flickbot.WasHeld and flickbot.State == 'Idle' and not AimbotInputState.SyntheticInputBlocked() then RivalsRuntimeBridge.StartFlickbot() end flickbot.WasHeld = held local frameDelta = math.clamp(type(deltaTime) == 'number' and deltaTime or (1 / 60), 0, 0.1) if flickbot.State == 'Flicking' then local selected = flickbot.Selected local targetPart = selected and selected.part or nil local camera = Workspace.CurrentCamera if not targetPart or not targetPart.Parent or not camera or not flickbot.Trajectory then RivalsRuntimeBridge.FinishFlickbotToCooldown() return end local direction, finished = RivalsRuntimeBridge.SampleFlickbotTrajectory(frameDelta) if flickbot.TotalMs > 0 then local targetOffset = targetPart.Position - camera.CFrame.Position if targetOffset.Magnitude > 0.001 then local progress = math.clamp(flickbot.ElapsedMs / flickbot.TotalMs, 0, 1) local corrected = direction + (targetOffset.Unit - flickbot.BakedEndDirection) * progress if corrected.Magnitude > 0.001 then direction = corrected.Unit end end end local cameraController = RivalsModsState.ResolveCameraController() if cameraController and type(cameraController.SetRotation) == 'function' then local pitch, yaw = CFrame.lookAt(camera.CFrame.Position, camera.CFrame.Position + direction):ToOrientation() cameraController:SetRotation(Vector2.new(pitch, yaw)) end if finished then if Toggles.P2S1T13 and Toggles.P2S1T13.Value == true then flickbot.State = 'PostShot' flickbot.Timer = math.clamp(Options.P2S1S8 and Options.P2S1S8.Value or 0, 0, 250) / 1000 else RivalsRuntimeBridge.FinishFlickbotToCooldown() end end elseif flickbot.State == 'PostShot' then flickbot.Timer = flickbot.Timer - frameDelta if flickbot.Timer <= 0 then FireTriggerbotShot() RivalsRuntimeBridge.FinishFlickbotToCooldown() end elseif flickbot.State == 'Cooldown' then flickbot.Timer = flickbot.Timer - frameDelta if flickbot.Timer <= 0 then flickbot.State = 'Idle' flickbot.Timer = 0 end end end RivalsRuntimeBridge.UpdateCameraAim = function(deltaTime) if RivalsRuntimeBridge.MovementRecorder.IsCameraClaimed() or RivalsRuntimeBridge.IsFlickbotCameraClaimed() or not AimbotBridge.IsCameraAimEnabled() or not IsAimbotGameReady() or not IsAimbotCameraReady() or not RivalsRuntimeBridge.IsReadyToFight() then AimbotBridge.ResetCameraAimRandomState() return end local camera = Workspace.CurrentCamera local ignoreFov = AimbotBridge.IsAimbotIgnoreFovEnabled() local targetInfo = RivalsRuntimeBridge.GetFilteredAimbotTarget(true, ignoreFov) local configuredAimPart = Options.P2S1D2 and Options.P2S1D2.Value or 'Auto' if configuredAimPart == 'Random' and targetInfo then local targetIdentity = targetInfo.player or targetInfo.instance local stablePart = AimbotSilentConnections.CameraAimRandomPart if AimbotSilentConnections.CameraAimRandomTarget == targetIdentity and stablePart and stablePart.Parent then local stableAimPart = RivalsRuntimeBridge.IsHeadAimbotTargetPart(stablePart) and 'Head' or 'HumanoidRootPart' local stableTargetInfo = RivalsRuntimeBridge.RefreshAimbotTargetInfo( targetInfo, stableAimPart, AimbotBridge.GetAimbotPointerPosition(), DEFAULT_ESP_MAX_DISTANCE, AIMBOT_VISIBILITY_CACHE_PROFILE ) if stableTargetInfo then targetInfo = stableTargetInfo end end AimbotSilentConnections.CameraAimRandomTarget = targetIdentity AimbotSilentConnections.CameraAimRandomPart = targetInfo.part else AimbotBridge.ResetCameraAimRandomState() end local targetPart = targetInfo and targetInfo.part or nil local worldPosition = targetInfo and targetInfo.worldPosition or nil if not camera or not targetPart or not targetPart.Parent or not worldPosition then RivalsRuntimeBridge.AimbotTargeting.ResetSpring() return end if targetInfo.player and IsRivalsSpawnShieldActive(targetInfo.player.Character) then return end local cameraController = RivalsModsState.ResolveCameraController() if not cameraController or type(cameraController.SetRotation) ~= 'function' then return end local currentRotation = rawget(cameraController, 'Rotation') if typeof(currentRotation) ~= 'Vector2' then return end local aimSpeed = Options.P2S2S1 and Options.P2S2S1.Value or 35 local frameDelta = math.clamp(type(deltaTime) == 'number' and deltaTime or (1 / 60), 0, 0.1) local blendAlpha = math.clamp(1 - math.exp(-frameDelta * (2 + (aimSpeed * 0.58))), 0, 1) local pitch, yaw = CFrame.lookAt(camera.CFrame.Position, worldPosition):ToOrientation() local deltaPitch = pitch - currentRotation.X local deltaYaw = (yaw - currentRotation.Y + math.pi) % (2 * math.pi) - math.pi local stepPitch = deltaPitch * blendAlpha local stepYaw = deltaYaw * blendAlpha local smoothing = Options.PX7D1 and Options.PX7D1.Value or 'Smooth' local targeting = RivalsRuntimeBridge.AimbotTargeting if smoothing == 'Linear' then -- constant turn rate: 12 degrees per second for every point of Aim Speed local remaining = math.sqrt(deltaPitch * deltaPitch + deltaYaw * deltaYaw) if remaining > 1e-5 then local scale = math.min(1, math.rad(aimSpeed * 12) * frameDelta / remaining) stepPitch = deltaPitch * scale stepYaw = deltaYaw * scale else stepPitch, stepYaw = 0, 0 end elseif smoothing == 'Spring' then -- slightly underdamped spring, so the view eases in and settles with a tiny overshoot local omega = 2 + aimSpeed * 0.58 local zeta = 0.8 local substeps = 4 local h = frameDelta / substeps local velocityPitch, velocityYaw = targeting.SpringPitch, targeting.SpringYaw local movedPitch, movedYaw = 0, 0 for _ = 1, substeps do velocityPitch += (omega * omega * (deltaPitch - movedPitch) - 2 * zeta * omega * velocityPitch) * h velocityYaw += (omega * omega * (deltaYaw - movedYaw) - 2 * zeta * omega * velocityYaw) * h movedPitch += velocityPitch * h movedYaw += velocityYaw * h end targeting.SpringPitch, targeting.SpringYaw = velocityPitch, velocityYaw stepPitch, stepYaw = movedPitch, movedYaw elseif smoothing == 'Adaptive' then -- fast while far from the target, easing in as the crosshair closes the gap local remaining = math.sqrt(deltaPitch * deltaPitch + deltaYaw * deltaYaw) local rate = (2 + aimSpeed * 0.58) * (0.45 + 1.55 * math.clamp(remaining / math.rad(25), 0, 1)) local alpha = math.clamp(1 - math.exp(-frameDelta * rate), 0, 1) stepPitch = deltaPitch * alpha stepYaw = deltaYaw * alpha end if smoothing ~= 'Spring' then targeting.ResetSpring() end cameraController:SetRotation(Vector2.new(currentRotation.X + stepPitch, currentRotation.Y + stepYaw)) end local GetBestTriggerbotTarget = function() EnsureAimbotTargetTracking() local aimPart = Options.P2S1D2 and Options.P2S1D2.Value or 'Auto' local fovRadius = Options.P2S1S2 and Options.P2S1S2.Value or 170 local maxDistance = DEFAULT_ESP_MAX_DISTANCE local ignoreFov = AimbotBridge.IsAimbotIgnoreFovEnabled() local mousePosition = AimbotBridge.GetAimbotPointerPosition() local camera = Workspace.CurrentCamera if not camera then return nil end local bestTarget = nil for _, fighter in ipairs(LocalFighterController and LocalFighterController.Objects or {}) do local player = fighter and fighter.Player if player and player ~= LP and player.Parent == Players then local entity = fighter.Entity local subject = entity and entity.Model if not ShouldShow(player, false) then RivalsRuntimeBridge.RecordTargetValidityRejection('team_or_self', subject, player, 'Players') elseif FighterDataCache.ReadEntity(entity) then RivalsRuntimeBridge.RecordTargetValidityRejection('invincible', subject, player, 'Players') elseif ShouldIgnoreRivalsRiotShieldTarget( player, subject or player.Character, AimbotBridge.ResolveAimbotEquippedItem() ) then RivalsRuntimeBridge.RecordTargetValidityRejection('riot_shield', subject, player, 'Players') else bestTarget = RivalsRuntimeBridge.ConsiderBestAimbotTarget( bestTarget, subject, 'Players', aimPart, mousePosition, maxDistance, fovRadius, ignoreFov, player, camera, TRIGGERBOT_VISIBILITY_CACHE_PROFILE ) end end end for model in pairs(TrackedRangeTargets) do if not model.Parent then TrackedRangeTargets[model] = nil else bestTarget = RivalsRuntimeBridge.ConsiderBestAimbotTarget( bestTarget, model, 'Range Targets', aimPart, mousePosition, maxDistance, fovRadius, ignoreFov, nil, camera, TRIGGERBOT_VISIBILITY_CACHE_PROFILE ) end end for model in pairs(TrackedPracticeDummies) do if not model.Parent then TrackedPracticeDummies[model] = nil else bestTarget = RivalsRuntimeBridge.ConsiderBestAimbotTarget( bestTarget, model, 'Practice Dummies', aimPart, mousePosition, maxDistance, fovRadius, ignoreFov, nil, camera, TRIGGERBOT_VISIBILITY_CACHE_PROFILE ) end end RivalsRuntimeBridge.LastAimbotSweepTarget = bestTarget RivalsRuntimeBridge.LastAimbotSweepAt = os.clock() return bestTarget end SharedTryTriggerbotShot = TryTriggerbotShot SharedGetBestTriggerbotTarget = GetBestTriggerbotTarget end local function ResolveTriggerbotTarget(aimbotEnabled, currentTargetInfo) local aimPart = Options.P2S1D2 and Options.P2S1D2.Value or 'Auto' local maxDistance = DEFAULT_ESP_MAX_DISTANCE local mousePosition = AimbotBridge.GetAimbotPointerPosition() local refreshedTarget = currentTargetInfo and RivalsRuntimeBridge.RefreshAimbotTargetInfo( currentTargetInfo, aimPart, mousePosition, maxDistance, TRIGGERBOT_VISIBILITY_CACHE_PROFILE ) if refreshedTarget then return refreshedTarget end local cursorTargetInfo = ResolveCursorTriggerbotTarget() if cursorTargetInfo then return cursorTargetInfo end return GetBestTriggerbotTarget() end local TryTriggerbotShot = SharedTryTriggerbotShot GetBestTriggerbotTarget = SharedGetBestTriggerbotTarget do -- Hit chance (P2S1S13): one roll per shot. Take() is the shot hook's roll, Arm() lets the triggerbot roll -- before it pre-primes so the hook reuses that same verdict, Hold() is a short re-roll window for held weapons. local hitChance = { Rng = Random.new(), PendingVerdict = nil, PendingAt = 0, PendingTtl = 0.35, HoldVerdict = true, HoldAt = 0, HoldWindow = 0.2, } hitChance.ReadPercent = function() local option = Options.P2S1S13 local value = option and option.Value if type(value) ~= 'number' then return 100 end return math.clamp(value, 1, 100) end hitChance.Roll = function() local percent = hitChance.ReadPercent() return percent >= 100 or hitChance.Rng:NextNumber(0, 100) < percent end hitChance.Arm = function() hitChance.PendingVerdict = hitChance.Roll() hitChance.PendingAt = tick() return hitChance.PendingVerdict end hitChance.Take = function() local verdict = hitChance.PendingVerdict hitChance.PendingVerdict = nil if hitChance.ReadPercent() >= 100 then return true end if verdict ~= nil and (tick() - hitChance.PendingAt) <= hitChance.PendingTtl then return verdict end return hitChance.Roll() end hitChance.Hold = function() if hitChance.ReadPercent() >= 100 then return true end local now = tick() if (now - hitChance.HoldAt) >= hitChance.HoldWindow then hitChance.HoldAt = now hitChance.HoldVerdict = hitChance.Roll() end return hitChance.HoldVerdict end RivalsRuntimeBridge.AimbotHitChance = hitChance end RivalsRuntimeBridge.BeginAimbotSilentShot = function(item) if (AimbotSilentState.ShotPrimeDepth or 0) > 0 then return false, nil, nil end local enabled = AimbotBridge.IsAimbotSelected() local keybindActive = AimbotBridge.IsAimbotEnabled() local shootInputHeld = AimbotBridge.IsAimbotShootInputHeld() local aimAssistActive = keybindActive or shootInputHeld if not RivalsRuntimeBridge.Extras.ActivationOk(item) then return false, nil, nil end local readyToFight = RivalsRuntimeBridge.IsReadyToFight() RivalsRuntimeBridge.RecordCharacterLoadingCheckpoint('Silent Aim', readyToFight, enabled and aimAssistActive) if not (enabled and aimAssistActive and readyToFight) then return false, nil, nil end local targetInfo = RivalsRuntimeBridge.ResolveAimbotSilentHookTarget(item) if not targetInfo then return false, nil, nil end if targetInfo.player and IsRivalsSpawnShieldActive(targetInfo.player.Character) then return false, nil, nil end if not RivalsRuntimeBridge.AimbotHitChance.Take() then -- Failed roll: the camera stays untouched so this shot goes where the player is really aiming. The prime -- depth is still held for the whole StartShooting call, so the nested wrapper can't roll a second time. AimbotSilentState.ShotPrimeDepth = (AimbotSilentState.ShotPrimeDepth or 0) + 1 if AimbotBridge.ResolveAimbotSilentDebugEnabled() then RivalsRuntimeBridge.NotifyAimbotStatus( 'silent_prime', string.format( 'Silent Camera shot-hook: %s skipped (hit chance %.0f%%)', targetInfo.displayName, RivalsRuntimeBridge.AimbotHitChance.ReadPercent() ), true ) end return true, nil, nil end local camera = Workspace.CurrentCamera local originalCFrame = camera and camera.CFrame or nil if not camera or not originalCFrame or not RivalsRuntimeBridge.PrimeAimbotAim(targetInfo, item) then return false, nil, nil end AimbotSilentState.ShotPrimeDepth = (AimbotSilentState.ShotPrimeDepth or 0) + 1 if AimbotBridge.ResolveAimbotSilentDebugEnabled() then RivalsRuntimeBridge.NotifyAimbotStatus( 'silent_prime', string.format('Silent Camera shot-hook: %s', targetInfo.displayName), true ) end RivalsRuntimeBridge.Extras.ShotTarget = targetInfo return true, camera, originalCFrame end RivalsRuntimeBridge.FinishAimbotSilentShot = function(didPrime, camera, originalCFrame) if not didPrime then return end RivalsRuntimeBridge.Extras.ShotTarget = nil AimbotSilentState.ShotPrimeDepth = math.max((AimbotSilentState.ShotPrimeDepth or 1) - 1, 0) if camera and originalCFrame and Workspace.CurrentCamera == camera and RivalsRuntimeBridge.IsReadyToFight() then camera.CFrame = originalCFrame end end RivalsModsState.BeginAimbotSilentShot = RivalsRuntimeBridge.BeginAimbotSilentShot RivalsModsState.FinishAimbotSilentShot = RivalsRuntimeBridge.FinishAimbotSilentShot local function EnsureAimbotSilentStartShootingHook(item) if AimbotSilentConnections.HookedItem == item then return end if not AimbotBridge.IsAimbotSilentShotHookItem(item) then return end local originalStartShooting = item.StartShooting local wrappedStartShooting wrappedStartShooting = function(self, ...) local didPrime, camera, originalCFrame = RivalsRuntimeBridge.BeginAimbotSilentShot(self) local baseStartShooting = RivalsModsState.ResolveGunStartShootingBase(originalStartShooting) local results = CallGameAtIdentity2(baseStartShooting, self, ...) RivalsRuntimeBridge.FinishAimbotSilentShot(didPrime, camera, originalCFrame) if not results[1] then ReportRivalsRuntimeIssue('silent_start_shooting', results[2]) return nil end return table.unpack(results, 2, results.n) end AimbotSilentConnections.HookedItem = item AimbotSilentConnections.OriginalStartShooting = originalStartShooting AimbotSilentConnections.WrappedStartShooting = wrappedStartShooting item.StartShooting = wrappedStartShooting end local function UpdateAimbot() if not (Toggles.P2S1T6 and Toggles.P2S1T6.Value) then RivalsRuntimeBridge.DestroyAimbotStatusNotification() end local enabled = AimbotBridge.IsAimbotSelected() local triggerbotEnabled = AimbotBridge.IsTriggerbotEnabled() local keybindActive = enabled and AimbotBridge.IsAimbotEnabled() local shootInputHeld = enabled and AimbotBridge.IsAimbotShootInputHeld() local aimAssistActive = enabled and (keybindActive or shootInputHeld) RivalsRuntimeBridge.RecordCharacterLoadingCheckpoint( 'Silent Aim', RivalsRuntimeBridge.IsReadyToFight(), enabled == true ) local aimbotGameReady = enabled and IsAimbotGameReady() local triggerbotGameReady = triggerbotEnabled and IsAimbotGameReady() local cameraAimActive = AimbotBridge.IsCameraAimEnabled() local cameraAimGameReady = cameraAimActive and IsAimbotGameReady() local gameReady = aimbotGameReady or triggerbotGameReady local silentAimUsesFov = enabled and aimAssistActive and not AimbotBridge.IsAimbotIgnoreFovEnabled() local cameraAimUsesFov = cameraAimActive and not AimbotBridge.IsAimbotIgnoreFovEnabled() AimbotBridge.UpdateAimbotFovCircle( (enabled and aimAssistActive) or cameraAimActive, aimbotGameReady or cameraAimGameReady, silentAimUsesFov or cameraAimUsesFov ) if not enabled and not triggerbotEnabled then AimbotBridge.ResetAimbotRuntimeState() AimbotBridge.ResetTriggerbotReactionState() RivalsRuntimeBridge.DestroyAimbotStatusNotification() return end if not gameReady then RivalsRuntimeBridge.RecordTargetValidityUpstreamGate('game_not_ready') AimbotBridge.ResetAimbotRuntimeState() AimbotBridge.ResetTriggerbotReactionState() RivalsRuntimeBridge.DestroyAimbotStatusNotification() return end local item = AimbotBridge.ResolveAimbotEquippedItem() if item and enabled then EnsureAimbotSilentStartShootingHook(item) end local cameraReady = IsAimbotCameraReady() if not cameraReady then RivalsRuntimeBridge.RecordTargetValidityUpstreamGate('camera_not_ready') AimbotBridge.ResetAimbotRuntimeState() AimbotBridge.ResetTriggerbotReactionState() RivalsRuntimeBridge.NotifyAimbotStatus('camera_wait', 'Waiting for camera.') return end local targetInfo = nil if not enabled or not aimAssistActive then if enabled then RivalsRuntimeBridge.RecordTargetValidityUpstreamGate('mouse_not_held') end AimbotBridge.ResetAimbotRuntimeState() if enabled then RivalsRuntimeBridge.DestroyAimbotStatusNotification() end else local evaluation = RivalsRuntimeBridge.EvaluateAimbotSilentRuntime(item, shootInputHeld) local statusForce = AimbotBridge.ResolveAimbotSilentDebugEnabled() local shouldContinuousAim = evaluation.shouldAim and AimbotBridge.ShouldUseAimbotSilentContinuousAim(item) if evaluation.blockedReason then RivalsRuntimeBridge.RecordTargetValidityUpstreamGate('silent_blocked:' .. tostring(evaluation.blockedReason)) RivalsRuntimeBridge.NotifyAimbotStatus( 'silent_blocked', RivalsRuntimeBridge.BuildAimbotSilentStatus(nil, evaluation), statusForce ) else if shouldContinuousAim then targetInfo = RivalsRuntimeBridge.GetFilteredAimbotTarget() if not targetInfo then RivalsRuntimeBridge.NotifyAimbotStatus('no_target', 'No target visible.') elseif RivalsRuntimeBridge.AimbotHitChance.Hold() then RivalsRuntimeBridge.PrimeAimbotAim(targetInfo, item) end end RivalsRuntimeBridge.NotifyAimbotStatus( 'tracking', RivalsRuntimeBridge.BuildAimbotSilentStatus(targetInfo, evaluation), statusForce ) end end if not triggerbotEnabled then AimbotBridge.ReleaseTriggerbotMouseHold() AimbotBridge.ResetTriggerbotReactionState() return end local triggerTargetInfo = ResolveTriggerbotTarget(enabled, targetInfo) if RivalsRuntimeBridge.Extras.TriggerReleaseHold(triggerTargetInfo ~= nil) then return end if not triggerTargetInfo then AimbotBridge.ReleaseTriggerbotMouseHold() AimbotBridge.ResetTriggerbotReactionState() if not enabled then RivalsRuntimeBridge.DestroyAimbotStatusNotification() end return end if TryTriggerbotShot(triggerTargetInfo, item, enabled) and not enabled then RivalsRuntimeBridge.NotifyAimbotStatus('tracking', string.format('Triggerbot: %s', triggerTargetInfo.displayName)) end end RivalsRuntimeBridge.UpdateAimbot = UpdateAimbot end -- ====================================================================== -- ESP render loop and cheater detector -- ====================================================================== do -- ESP Render Loop RivalsRuntimeBridge.CheaterDetector = { LastPosition = {}, LastRoot = {}, Streak = {}, Flagged = {}, } function RivalsRuntimeBridge.CheaterDetector.IsFlagged(player) return RivalsRuntimeBridge.CheaterDetector.Flagged[player] ~= nil end function RivalsRuntimeBridge.CheaterDetector.Reset() local detector = RivalsRuntimeBridge.CheaterDetector detector.LastPosition = {} detector.LastRoot = {} detector.Streak = {} detector.Flagged = {} end -- Flags a player who keeps moving faster than any legitimate movement for a short while. -- Single huge jumps (respawns, map teleports) are ignored on purpose. function RivalsRuntimeBridge.CheaterDetector.Step(deltaTime) local detector = RivalsRuntimeBridge.CheaterDetector if not (Toggles and Toggles.PX10T1 and Toggles.PX10T1.Value == true) then if next(detector.LastPosition) ~= nil or next(detector.Flagged) ~= nil then detector.Reset() end return end if type(deltaTime) ~= 'number' or deltaTime <= 0 or deltaTime > 0.5 then return end local thresholdOption = Options and Options.PX10S1 local threshold = thresholdOption and tonumber(thresholdOption.Value) or 120 for _, player in ipairs(Players:GetPlayers()) do if player ~= LP then local character = player.Character local root = character and character:FindFirstChild('HumanoidRootPart') local humanoid = character and character:FindFirstChildOfClass('Humanoid') if root and humanoid and humanoid.Health > 0 then local position = root.Position local lastPosition = detector.LastPosition[player] local sameBody = detector.LastRoot[player] == root detector.LastPosition[player] = position detector.LastRoot[player] = root if lastPosition and sameBody then local offset = position - lastPosition local speed = Vector3.new(offset.X, 0, offset.Z).Magnitude / deltaTime if speed > threshold and speed < 5000 then local streak = (detector.Streak[player] or 0) + deltaTime detector.Streak[player] = streak if streak >= 0.3 and not detector.Flagged[player] then detector.Flagged[player] = true if Toggles.PX10T2 and Toggles.PX10T2.Value == true then Library:Notify({ Title = 'Possible cheater', Description = string.format( '%s is moving at %d studs/s', player.DisplayName ~= '' and player.DisplayName or player.Name, math.floor(speed) ), Time = 5, }) end end else detector.Streak[player] = 0 end else detector.Streak[player] = 0 end else detector.LastPosition[player] = nil detector.LastRoot[player] = nil detector.Streak[player] = 0 end end end for player in pairs(detector.Flagged) do if player.Parent == nil then detector.Flagged[player] = nil detector.LastPosition[player] = nil detector.LastRoot[player] = nil detector.Streak[player] = nil end end end -- ===== ZkxHub extras :: core ===== -- Safe stubs first: every hook the patched code calls exists and behaves like the original game logic. -- If the real module below fails to load, these stay in place and nothing in the base script changes. RivalsRuntimeBridge.Extras = { Version = 1, Loaded = false, FilterCandidates = function(candidates) return candidates end, SamplePoints = function() return nil end, RandomBucketRoll = function() return math.random(1, 2) end, OverrideAimPart = function(aimPart) return aimPart end, ManipulateAllows = function() return false end, TargetAllowed = function() return true end, ActivationOk = function() return true end, TriggerAllowed = function() return true end, TriggerReleaseHold = function() return false end, FeedbackText = function(_, _, _, _, default) return default end, HealthColor = function(_, default) return default end, FlagFont = function(default) return default end, FlagSize = function(default) return default end, FlagText = function(text) return text end, SuppressCornerBox = function() return false end, StatusXOffset = function() return 0 end, DataField = function() return nil end, NeedsInputHook = function() return false end, SyncControllers = function() end, NewController = function() return nil end, DestroyController = function() end, EspStep = function() end, BuildCombatUi = function() end, BuildEspUi = function() end, BuildVisualsUi = function() end, BuildMiscUi = function() end, BuildConfigUi = function() end, CollapseLongGroups = function() end, Destroy = function() end, Reconcile = function() end, } xpcall(function() local extras = RivalsRuntimeBridge.Extras local reported = {} local rng = Random.new() extras.Cleanups = {} extras.Destroy = function() for _, cleanup in ipairs(extras.Cleanups) do pcall(cleanup) end end function extras.Report(tag, err) if reported[tag] then return end reported[tag] = true ReportRivalsRuntimeIssue('extras_' .. tostring(tag), err) end -- Wraps an implementation so an error can never leak into the base script: it reports once and -- returns the default (the value the original code would have used). local function guard(tag, impl, default) return function(...) local ok, a, b = pcall(impl, ...) if ok then return a, b end extras.Report(tag, a) if type(default) == 'function' then return default(...) end return default end end function extras.Tog(id, default) local toggle = Toggles[id] if toggle == nil then return default end return toggle.Value == true end function extras.Opt(id, default) local option = Options[id] if option == nil or option.Value == nil then return default end return option.Value end function extras.Num(id, default) local value = tonumber(extras.Opt(id, default)) if value == nil then return default end return value end function extras.Has(id, name, default) local option = Options[id] if option == nil then return default end local value = option.Value if type(value) ~= 'table' then return default end if value[name] == true then return true end for _, entry in ipairs(value) do if entry == name then return true end end return false end -- ---------------------------------------------------------------- indicator list extras.IndicatorEntries = {} function extras.Pulse(key, text, seconds) extras.IndicatorEntries[key] = { Text = text, Until = os.clock() + (seconds or 1) } end -- ---------------------------------------------------------------- silent aim rules function extras.RulesOn() return extras.Tog('KPA0', false) and AimbotBridge.ResolveAimbotMode() == 'Silent' end extras.FilterCandidates = guard('filter_candidates', function(candidates) if not extras.RulesOn() then return candidates end local allowHead = extras.Has('KPA8', 'Head', true) local allowBody = extras.Has('KPA8', 'Body', true) local filtered = {} for _, candidate in ipairs(candidates) do local isHead = candidate.Name == 'HitboxHead' if (isHead and allowHead) or (not isHead and allowBody) then filtered[#filtered + 1] = candidate end end return filtered end, function(candidates) return candidates end) extras.RandomBucketRoll = guard('random_bucket', function() if extras.RulesOn() and extras.Opt('KPA9', 'Closest To Crosshair') == 'Random' then local headChance = math.clamp(extras.Num('KPAA', 50), 0, 100) / 100 return rng:NextNumber() < headChance and 1 or 2 end return math.random(1, 2) end, function() return math.random(1, 2) end) extras.OverrideAimPart = guard('override_aim_part', function(aimPart) if aimPart == 'Closest' or not extras.RulesOn() then return aimPart end if extras.Opt('KPA9', 'Closest To Crosshair') == 'Random' then return 'Random' end return 'CrosshairClosest' end, function(aimPart) return aimPart end) -- Point on the (shrunken) hitbox box that lies closest to the crosshair ray. local function surfacePoint(part) local camera = Workspace.CurrentCamera if not camera then return nil end local pointer = AimbotBridge.GetAimbotPointerPosition() local ray = camera:ViewportPointToRay(pointer.X, pointer.Y) local cf = part.CFrame local half = part.Size * 0.5 local shrink = 1 - math.clamp(extras.Num('KPAC', 50), 0, 95) / 100 local hx, hy, hz = half.X * shrink, half.Y * shrink, half.Z * shrink local origin = cf:PointToObjectSpace(ray.Origin) local direction = cf:VectorToObjectSpace(ray.Direction) if extras.Tog('KPAD', false) then local ax, ay, az = math.abs(direction.X), math.abs(direction.Y), math.abs(direction.Z) if ax >= ay and ax >= az then hx = half.X elseif ay >= az then hy = half.Y else hz = half.Z end end local current = Vector3.zero for _ = 1, 3 do local t = (current - origin):Dot(direction) local q = origin + direction * t current = Vector3.new(math.clamp(q.X, -hx, hx), math.clamp(q.Y, -hy, hy), math.clamp(q.Z, -hz, hz)) end return cf:PointToWorldSpace(current) end extras.SamplePoints = guard('sample_points', function(part) if not part or not extras.RulesOn() or extras.Opt('KPAB', 'Center') ~= 'Surface' then return nil end if part.Name ~= 'HitboxHead' and part.Name ~= 'HitboxBody' then return nil end local point = surfacePoint(part) if not point then return nil end return { point, part.Position } end, nil) -- shield / deflect / invincibility checks shared by the silent rules and the triggerbot rules local function isDeflecting(player) local fighter = TrackedFightersByPlayer[player] return fighter ~= nil and RivalsRuntimeBridge.IsRivalsKatanaDeflectActiveItem(fighter.EquippedItem) == true end local function isShielded(player, character, observer) local state = EvaluateRivalsRiotShieldChecker(player, character, observer) return state ~= nil and state.IgnoreTarget == true end local function isInvulnerable(player, character) return IsRivalsSpawnShieldActive(character or player.Character) == true end extras.TargetAllowed = guard( 'target_allowed', function(subject, player, part, isVisible, worldPosition, mousePosition, fovRadius, ignoreFov, camera) if not extras.RulesOn() then return true end if extras.Tog('KPA3', true) and not isVisible then return false, 'rule_visible' end if player then local character = subject or player.Character if extras.Tog('KPA4', true) and isInvulnerable(player, character) then return false, 'rule_invulnerable' end if extras.Tog('KPA5', true) and isDeflecting(player) then return false, 'rule_deflecting' end if extras.Tog('KPA6', true) and isShielded(player, character, camera and camera.CFrame.Position or nil) then return false, 'rule_shielded' end end if extras.Tog('KPA7', true) and ignoreFov == true and camera and worldPosition and mousePosition then local screenPoint, onScreen = camera:WorldToViewportPoint(worldPosition) if not onScreen or screenPoint.Z <= 0 then return false, 'rule_fov' end local radius = extras.Num('P2S1S2', 170) if (Vector2.new(screenPoint.X, screenPoint.Y) - mousePosition).Magnitude > radius then return false, 'rule_fov' end end return true end, true ) -- Best-effort "is the scope fully up". Returns nil when this build exposes no way to tell, which the -- callers treat as "do not block". function extras.IsFullyAiming(item) if type(item) ~= 'table' then return nil end local ok, method = pcall(function() return item.IsFullyAiming end) if ok and type(method) == 'function' then local okCall, result = pcall(method, item) if okCall and type(result) == 'boolean' then return result end end local data = rawget(item, '_data') local isAiming = nil if type(data) == 'table' then isAiming = rawget(data, 'IsAiming') end local viewModel = rawget(item, 'ViewModel') local current = nil if type(viewModel) == 'table' then current = rawget(viewModel, 'CurrentAimValue') end if isAiming == nil and current == nil then return nil end if isAiming ~= true then return false end local info = item.Info local percent = type(info) == 'table' and info.AimScopePercent or nil if type(percent) == 'number' and type(current) == 'number' then return current >= percent end return true end extras.ActivationOk = guard('activation', function(item) if not extras.RulesOn() then return true end if extras.Tog('KPA1', true) and AimbotBridge.ReadAimbotReloadRemaining(item) > 0 then return false end if extras.Tog('KPA2', false) and extras.IsFullyAiming(item) == false then return false end return true end, true) -- ---------------------------------------------------------------- triggerbot rules local trigger = { RollAt = 0, Verdict = true, LastTargetAt = 0 } extras.TriggerAllowed = guard('trigger_allowed', function(targetInfo, item) if not extras.Tog('KPB0', false) then return true end if extras.Tog('KPB1', true) and AimbotBridge.ReadAimbotReloadRemaining(item) > 0 then return false end if extras.Tog('KPB2', false) and extras.IsFullyAiming(item) == false then return false end if extras.Has('KPB3', 'Visible', true) and not targetInfo.isVisible then return false end local player = targetInfo.player if player then local character = targetInfo.instance or player.Character if extras.Has('KPB3', 'Vulnerable', true) and isInvulnerable(player, character) then return false end if extras.Has('KPB3', 'Not Deflecting', true) and isDeflecting(player) then return false end if extras.Has('KPB3', 'Not Shielded', true) then local camera = Workspace.CurrentCamera if isShielded(player, character, camera and camera.CFrame.Position or nil) then return false end end end local chance = math.clamp(extras.Num('KPB4', 100), 1, 100) if chance < 100 then local now = os.clock() if now - trigger.RollAt > 0.25 then trigger.RollAt = now trigger.Verdict = rng:NextNumber(0, 100) < chance end return trigger.Verdict end return true end, true) -- Keeps a held trigger (flamethrower style) down for a moment after the target is lost. extras.TriggerReleaseHold = guard('trigger_release', function(hasTarget) if not extras.Tog('KPB0', false) then return false end local now = os.clock() if hasTarget then trigger.LastTargetAt = now return false end return (now - trigger.LastTargetAt) < math.clamp(extras.Num('KPB5', 0), 0, 1) end, false) -- ---------------------------------------------------------------- shot packet hook (anti katana / manipulate) local function decodeCFrame(value) if type(value) ~= 'table' then return nil end local x, y, z = value['\0'], value['\1'], value['\2'] local rx, ry, rz = value['\3'], value['\4'], value['\5'] if type(x) ~= 'number' or type(y) ~= 'number' or type(z) ~= 'number' or type(rx) ~= 'number' or type(ry) ~= 'number' or type(rz) ~= 'number' then return nil end return CFrame.new(x, y, z) * CFrame.fromOrientation(rx, ry, rz) end local function encodeCFrame(cf) local rx, ry, rz = cf:ToOrientation() return { ['\0'] = cf.X, ['\1'] = cf.Y, ['\2'] = cf.Z, ['\3'] = rx, ['\4'] = ry, ['\5'] = rz } end -- Looks for a shot origin (within `reach` studs of the real one) that has a clear line to the point. -- Returns visibleAlready, origin: (true, origin) when nothing blocks, (false, newOrigin) when a -- workable alternative exists, (false, nil) when none does. local MANIPULATE_REACH = 9.9 local function findVisibleOrigin(origin, part, point) local character = part.Parent local params = ResolveAimbotVisibilityRaycastParams(character) local function blockedBy(from) return Workspace:Raycast(from, point - from, params) end local firstHit = blockedBy(origin) if firstHit == nil then return true, origin end local offset = point - origin local distance = offset.Magnitude if distance < 0.01 then return false, nil end local direction = offset / distance local reach = math.min(MANIPULATE_REACH, distance - part.Size.Magnitude / 2) local pushed = false if reach > 0 and firstHit.Distance < reach then local forward = origin + direction * reach if blockedBy(forward) == nil then return false, forward end pushed = true end local up = Vector3.yAxis local side = direction:Cross(math.abs(direction:Dot(up)) > 0.99 and Vector3.xAxis or up).Unit local other = side:Cross(direction).Unit local samples = pushed and 6 or 7 for index = 0, samples - 1 do local angle = math.tau * index / samples local candidate = origin + (side * math.cos(angle) + other * math.sin(angle)) * MANIPULATE_REACH if blockedBy(candidate) == nil then return false, candidate end end return false, nil end local manipulateCache = setmetatable({}, { __mode = 'k' }) extras.ManipulateAllows = guard('manipulate_allows', function(subject, part, worldPosition) if not extras.Tog('KPD1', false) or AimbotBridge.ResolveAimbotMode() ~= 'Silent' then return false end if not part or typeof(worldPosition) ~= 'Vector3' then return false end if extras.Opt('KPD2', 'On Best Target') == 'On Best Target' then local committed = RivalsRuntimeBridge.AimbotTargeting and RivalsRuntimeBridge.AimbotTargeting.Committed local committedInstance = committed and committed.instance if committedInstance ~= nil and committedInstance ~= subject then return false end end local camera = Workspace.CurrentCamera if not camera then return false end local now = os.clock() local cached = manipulateCache[subject] if cached and now - cached.At < 0.1 then return cached.Result end local _, origin = findVisibleOrigin(camera.CFrame.Position, part, worldPosition) local result = origin ~= nil manipulateCache[subject] = { At = now, Result = result } return result end, false) local function localHit(origin, part, point) local params = RaycastParams.new() params.FilterType = Enum.RaycastFilterType.Include params.FilterDescendantsInstances = { part } local hit = Workspace:Raycast(origin, point - origin, params) local worldPoint = hit and hit.Position or point local objectPoint = part.CFrame:PointToObjectSpace(worldPoint) if objectPoint == Vector3.zero then return nil end return objectPoint end local function manipulatePacket(shot) local target = extras.ShotTarget if not target or not target.part or not target.part.Parent or typeof(target.worldPosition) ~= 'Vector3' then return end local aim = decodeCFrame(shot['\0']) if not aim then return end local part, point = target.part, target.worldPosition local visible, newOrigin = findVisibleOrigin(aim.Position, part, point) if visible or newOrigin == nil then return end local hitLocal = localHit(newOrigin, part, point) if not hitLocal then return end local look = encodeCFrame(CFrame.lookAt(newOrigin, point)) shot['\0'] = look shot['\1'] = encodeCFrame(CFrame.lookAt(newOrigin, point)) shot['\2'] = part shot['\3'] = encodeCFrame(CFrame.new(hitLocal)) extras.Pulse('manipulated', 'Manipulated', 0.8) end local function handlePacket(controller, packet) if packet.type ~= 'StartShooting' then return end local args = packet.args local shot = type(args) == 'table' and args['\1'] or nil if type(shot) ~= 'table' then return end local inner = controller._playerContext and controller._playerContext.inner local item = inner and inner.fighterState:GetItemById(packet.objectId) if item == nil then return end local info = rawget(item, 'Info') if type(info) ~= 'table' or rawget(info, 'Type') ~= 'Gun' then return end if extras.Tog('KPD0', false) then local hit = shot['\2'] if typeof(hit) == 'Instance' then local model = hit:FindFirstAncestorOfClass('Model') local player = model and Players:GetPlayerFromCharacter(model) if player then local origin = decodeCFrame(shot['\0']) if isDeflecting(player) or isShielded(player, model, origin and origin.Position or nil) then packet.block = true extras.Pulse('antikatana', 'Anti Katana: shot blocked', 1.2) return end end end end if extras.Tog('KPD1', false) then manipulatePacket(shot) end end function extras.NeedsInputHook() return extras.Tog('KPD0', false) or extras.Tog('KPD1', false) end extras.NewController = guard('new_controller', function(binding, playerContext) local controller = { _inputBinding = binding, _playerContext = playerContext } binding:SetHandler(function(packet) local ok, err = pcall(handlePacket, controller, packet) if not ok then extras.Report('packet_handler', err) end end) return controller end, nil) function extras.DestroyController(controller) if controller and controller._inputBinding then pcall(function() controller._inputBinding:Destroy() end) end end extras.SyncControllers = guard('sync_controllers', function() local controller = RivalsModsState.ExtrasController if not controller then return end local enabled = extras.NeedsInputHook() if controller._inputBinding.enabled ~= enabled then controller._inputBinding:SetEnabled(enabled) end end, nil) -- ---------------------------------------------------------------- feedback text templates local function applyTemplate(template, character, damage, partName) local player = typeof(character) == 'Instance' and Players:GetPlayerFromCharacter(character) or nil local name = player and player.Name or (typeof(character) == 'Instance' and character.Name or 'player') local display = player and (player.DisplayName ~= '' and player.DisplayName or player.Name) or name local amount = tonumber(damage) local values = { NAME = name, DNAME = display, DMG = amount and tostring(math.floor(amount + 0.5)) or '?', PART = tostring(partName or 'Body'), } return (string.gsub(template, '%%(%u+)%%', function(token) return values[token] end)) end extras.ApplyTemplate = applyTemplate extras.FeedbackText = guard('feedback_text', function(kind, character, damage, partName, default) local template = extras.Opt(kind == 'Hit' and 'KPF2' or 'KPF4', '') if type(template) ~= 'string' or template:match('^%s*$') then return default end return applyTemplate(template, character, damage, partName) end, function(_, _, _, _, default) return default end) -- ---------------------------------------------------------------- health bar colour modes extras.HealthColor = guard('health_color', function(ratio, default) local mode = extras.Opt('KPH0', 'Reactive') if mode == 'Solid' then return extras.Opt('KPH1', default) end if mode == 'Gradient' then local low = extras.Opt('KPH2', Color3.fromRGB(255, 60, 60)) local high = extras.Opt('KPH3', Color3.fromRGB(60, 255, 60)) return low:Lerp(high, math.clamp(ratio or 0, 0, 1)) end return default end, function(_, default) return default end) -- ---------------------------------------------------------------- flag text style (rank / win streak / deflecting) local flagFonts = { UI = 0, System = 1, Plex = 2, Monospace = 3 } extras.FlagFont = guard( 'flag_font', function(default) return flagFonts[extras.Opt('KPG1', 'Same As Text')] or default end, function(default) return default end ) extras.FlagSize = guard('flag_size', function(default) local size = extras.Num('KPG2', 0) if size > 0 then return size end return default end, function(default) return default end) extras.FlagText = guard('flag_text', function(text) local case = extras.Opt('KPG3', 'Normal') local surround = extras.Opt('KPG4', 'None') if (case == 'Normal' and surround == 'None') or text == '' then return text end local pair = RivalsRuntimeBridge.EspExtras.Surrounds[surround] local lines = string.split(text, '\n') for index, line in ipairs(lines) do if case == 'UPPER' then line = string.upper(line) elseif case == 'lower' then line = string.lower(line) end if pair and line ~= '' then line = pair[1] .. line .. pair[2] end lines[index] = line end return table.concat(lines, '\n') end, function(text) return text end) extras.CoreLoaded = true end, function(err) RivalsRuntimeBridge.Extras.CoreError = tostring(err) task.defer(function() pcall(ReportRivalsRuntimeIssue, 'extras_core_load', tostring(err)) end) return err end) -- ===== ZkxHub extras :: end core ===== -- ===== ZkxHub extras :: ESP ===== -- Independent Drawing-based layer. It never touches the base ESP objects; the only coupling is two tiny -- hooks (corner box suppression, status text offset) that return the original behaviour when off. xpcall(function() local extras = RivalsRuntimeBridge.Extras local entries = {} local frameId = 0 local stamp = setmetatable({}, { __mode = 'k' }) local indicatorTexts = {} local BONE_NAMES = { Head = true, Torso = true, UpperTorso = true, LowerTorso = true, LeftUpperLeg = true, LeftLowerLeg = true, LeftFoot = true, RightUpperLeg = true, RightLowerLeg = true, RightFoot = true, LeftUpperArm = true, LeftLowerArm = true, LeftHand = true, RightUpperArm = true, RightLowerArm = true, RightHand = true, LeftLeg = true, RightLeg = true, LeftArm = true, RightArm = true, } local BLACK = Color3.new(0, 0, 0) local function newDrawing(kind, props) local ok, drawable = pcall(Drawing.new, kind) if not ok or drawable == nil then return nil end for key, value in pairs(props or {}) do pcall(function() drawable[key] = value end) end return drawable end local function slot(entry, name, kind, props) local cached = entry.drawables[name] if cached ~= nil then return cached or nil end local drawable = newDrawing(kind, props) entry.drawables[name] = drawable or false return drawable end local function mark(drawable) if drawable then stamp[drawable] = frameId if drawable.Visible ~= true then drawable.Visible = true end end end local function sweep(entry) for _, drawable in pairs(entry.drawables) do if drawable and stamp[drawable] ~= frameId and drawable.Visible then drawable.Visible = false end end end local function destroyEntry(entry) for _, drawable in pairs(entry.drawables) do if drawable then pcall(function() drawable:Remove() end) end end entry.drawables = {} end local function clearAll() for player, entry in pairs(entries) do destroyEntry(entry) entries[player] = nil end for index, drawable in pairs(indicatorTexts) do pcall(function() drawable:Remove() end) indicatorTexts[index] = nil end end table.insert(extras.Cleanups, clearAll) local function readScope(scope) local prefix = 'KPE' .. scope local function tog(id, default) return extras.Tog(prefix .. id, default) end local function opt(id, default) return extras.Opt(prefix .. id, default) end local function num(id, default) return extras.Num(prefix .. id, default) end return { On = tog('0', false), Skeleton = tog('SK', false), SkeletonColor = opt('SKC', Color3.new(1, 1, 1)), SkeletonGradient = tog('SKG', false), SkeletonColor2 = opt('SKH', Color3.fromRGB(255, 70, 70)), SkeletonThickness = num('SKT', 1), HeadMarker = tog('HM', false), HeadMarkerColor = opt('HMC', Color3.fromRGB(255, 255, 255)), HeadMarkerShape = opt('HMS', 'Circle'), HeadMarkerFilled = tog('HMF', false), HeadMarkerOutline = tog('HMO', true), HeadMarkerSize = num('HMR', 1), Tracer = tog('TR', false), TracerColor = opt('TRC', Color3.fromRGB(255, 255, 255)), TracerGradient = tog('TRG', false), TracerColor2 = opt('TRH', Color3.fromRGB(255, 70, 70)), TracerThickness = num('TRT', 1), TracerFrom = opt('TRF', 'Bottom'), TracerTo = opt('TRW', 'Feet'), TracerOutline = tog('TRO', true), FullBox = tog('FB', false), FullBoxColor = opt('FBC', Color3.fromRGB(255, 255, 255)), FilledBox = tog('FF', false), FilledBoxColor = opt('FFC', Color3.fromRGB(255, 60, 60)), FilledBoxOpacity = num('FFA', 0.25), FilledBoxGradient = tog('FFG', false), FilledBoxColor2 = opt('FFH', Color3.fromRGB(60, 60, 255)), AmmoBar = tog('AB', false), AmmoBarColor = opt('ABC', Color3.fromRGB(80, 160, 255)), AmmoBarColor2 = opt('ABH', Color3.fromRGB(255, 70, 70)), AmmoBarMode = opt('ABM', 'Reactive'), } end -- Rivals rigs hang each limb off an AnimationConstraint (torso/head use a Motor6D), so read both and -- join the two parts each joint connects. If the part names are not the usual limb names, fall back to -- any joint between body parts so a differently named rig still gets a skeleton. local function addBone(bones, seen, a, b) if a and b and a ~= b and a:IsA('BasePart') and b:IsA('BasePart') then local key = a.Name .. '|' .. b.Name if not seen[key] then seen[key] = true bones[#bones + 1] = { a, b } end end end local function jointParts(descendant) if descendant:IsA('AnimationConstraint') then local a0, a1 = descendant.Attachment0, descendant.Attachment1 return a0 and a0.Parent, a1 and a1.Parent end if descendant:IsA('Motor6D') then return descendant.Part0, descendant.Part1 end return nil, nil end local function isBodyPart(part) local name = part.Name return name ~= 'HumanoidRootPart' and name:sub(1, 6) ~= 'Hitbox' end local function collectBones(character) local descendants = character:GetDescendants() local bones, seen = {}, {} for _, descendant in ipairs(descendants) do local a, b = jointParts(descendant) if a and b and BONE_NAMES[a.Name] and BONE_NAMES[b.Name] then addBone(bones, seen, a, b) end end if #bones <= 2 then for _, descendant in ipairs(descendants) do local a, b = jointParts(descendant) if a and b and isBodyPart(a) and isBodyPart(b) then addBone(bones, seen, a, b) end end end if #bones == 0 then local head = character:FindFirstChild('HitboxHead') local body = character:FindFirstChild('HitboxBody') if head and body then bones[1] = { head, body } end end return bones end local function reactiveColor(ratio) return Color3.fromRGB(255, 60, 60):Lerp(Color3.fromRGB(60, 255, 60), math.clamp(ratio, 0, 1)) end local function drawPlayer(player, entry, scope, cam, vp, uiScale, myPos, maxDistance, fitStatic, mouse) local character = player.Character if not character then return end local parts = ReadCharacterEspParts(character, entry.parts) entry.parts = parts local root = parts and parts.HumanoidRootPart if not root then return end if myPos and (root.Position - myPos).Magnitude > maxDistance then return end local rootScreen = cam:WorldToViewportPoint(root.Position) if rootScreen.Z <= 0 then return end local bounds = nil if not fitStatic then bounds = GetHitboxScreenBounds(parts, character, cam) end if bounds == nil then local height = (5.5 / rootScreen.Z) * vp.Y * 0.5 local width = height * 0.55 bounds = { MinX = rootScreen.X - width / 2, MaxX = rootScreen.X + width / 2, MinY = rootScreen.Y - height / 2, MaxY = rootScreen.Y + height / 2, Width = width, Height = height, CenterX = rootScreen.X, CenterY = rootScreen.Y, } end local minX, maxX, minY, maxY = bounds.MinX, bounds.MaxX, bounds.MinY, bounds.MaxY local width, height = math.max(maxX - minX, 1), math.max(maxY - minY, 1) local boxPos, boxSize = Vector2.new(minX, minY), Vector2.new(width, height) if scope.FilledBox then local strips = scope.FilledBoxGradient and 10 or 1 local stripHeight = height / strips for index = 1, strips do local strip = slot(entry, 'fill' .. index, 'Square', { Filled = true, Thickness = 1 }) if strip then strip.Position = Vector2.new(minX, minY + stripHeight * (index - 1)) strip.Size = Vector2.new(width, math.ceil(stripHeight) + (index < strips and 1 or 0)) strip.Color = strips > 1 and scope.FilledBoxColor:Lerp(scope.FilledBoxColor2, (index - 0.5) / strips) or scope.FilledBoxColor strip.Transparency = math.clamp(scope.FilledBoxOpacity, 0, 1) mark(strip) end end end if scope.FullBox then local outline = slot(entry, 'boxOutline', 'Square', { Filled = false }) if outline then outline.Position, outline.Size, outline.Color = boxPos, boxSize, BLACK outline.Thickness = 3 * uiScale + 1 outline.Transparency = 1 mark(outline) end local box = slot(entry, 'box', 'Square', { Filled = false }) if box then box.Position, box.Size, box.Color = boxPos, boxSize, scope.FullBoxColor box.Thickness = math.max(1, uiScale) box.Transparency = 1 mark(box) end end if scope.Skeleton then if entry.bonesCharacter ~= character or os.clock() - entry.bonesAt > 3 then entry.bones = collectBones(character) entry.bonesCharacter = character entry.bonesAt = os.clock() end for index, bone in ipairs(entry.bones) do local a, b = bone[1], bone[2] if a.Parent and b.Parent then local pa, pb = cam:WorldToViewportPoint(a.Position), cam:WorldToViewportPoint(b.Position) if pa.Z > 0 and pb.Z > 0 then local line = slot(entry, 'bone' .. index, 'Line') if line then line.From, line.To = Vector2.new(pa.X, pa.Y), Vector2.new(pb.X, pb.Y) line.Thickness = scope.SkeletonThickness line.Transparency = 1 if scope.SkeletonGradient then local t = math.clamp((((pa.Y + pb.Y) / 2) - minY) / height, 0, 1) line.Color = scope.SkeletonColor:Lerp(scope.SkeletonColor2, t) else line.Color = scope.SkeletonColor end mark(line) end end end end end if scope.HeadMarker then local head = character:FindFirstChild('HitboxHead') or character:FindFirstChild('Head') if head then local headScreen = cam:WorldToViewportPoint(head.Position) if headScreen.Z > 0 then local center = Vector2.new(headScreen.X, headScreen.Y) local radius = math.max(3, height * 0.06) * math.clamp(scope.HeadMarkerSize, 0.2, 4) local shape = scope.HeadMarkerShape if shape == 'Circle' then if scope.HeadMarkerOutline then local outline = slot(entry, 'headOutline', 'Circle', { NumSides = 24, Filled = false }) if outline then outline.Position, outline.Radius, outline.Color = center, radius + 1, BLACK outline.Thickness = 3 outline.Transparency = 1 mark(outline) end end local circle = slot(entry, 'head', 'Circle', { NumSides = 24 }) if circle then circle.Position, circle.Radius, circle.Color = center, radius, scope.HeadMarkerColor circle.Filled = scope.HeadMarkerFilled circle.Thickness = 1 circle.Transparency = 1 mark(circle) end elseif shape == 'Diamond' then local function diamond(name, color, size, filled, thickness) local quad = slot(entry, name, 'Quad') if quad then quad.PointA = center + Vector2.new(0, -size) quad.PointB = center + Vector2.new(size, 0) quad.PointC = center + Vector2.new(0, size) quad.PointD = center + Vector2.new(-size, 0) quad.Color, quad.Filled, quad.Thickness, quad.Transparency = color, filled, thickness, 1 mark(quad) end end if scope.HeadMarkerOutline then diamond('headOutline', BLACK, radius + 1.5, false, 3) end diamond('head', scope.HeadMarkerColor, radius, scope.HeadMarkerFilled, 1) else local function arm(name, from, to, color, thickness) local line = slot(entry, name, 'Line') if line then line.From, line.To, line.Color, line.Thickness, line.Transparency = from, to, color, thickness, 1 mark(line) end end local h = Vector2.new(radius, 0) local v = Vector2.new(0, radius) if scope.HeadMarkerOutline then arm('headOutlineH', center - h, center + h, BLACK, 3) arm('headOutlineV', center - v, center + v, BLACK, 3) end arm('headH', center - h, center + h, scope.HeadMarkerColor, 1) arm('headV', center - v, center + v, scope.HeadMarkerColor, 1) end end end end if scope.Tracer then local origin local from = scope.TracerFrom if from == 'Center' then origin = Vector2.new(vp.X / 2, vp.Y / 2) elseif from == 'Top' then origin = Vector2.new(vp.X / 2, 0) elseif from == 'Mouse' then origin = mouse else origin = Vector2.new(vp.X / 2, vp.Y) end local target = Vector2.new(bounds.CenterX, maxY) if scope.TracerTo == 'Head' then local head = character:FindFirstChild('HitboxHead') or character:FindFirstChild('Head') local headScreen = head and cam:WorldToViewportPoint(head.Position) if headScreen and headScreen.Z > 0 then target = Vector2.new(headScreen.X, headScreen.Y) else target = Vector2.new(bounds.CenterX, minY) end end if scope.TracerOutline then local outline = slot(entry, 'tracerOutline', 'Line') if outline then outline.From, outline.To, outline.Color = origin, target, BLACK outline.Thickness = scope.TracerThickness + 2 outline.Transparency = 1 mark(outline) end end local segments = scope.TracerGradient and 8 or 1 for index = 1, segments do local line = slot(entry, 'tracer' .. index, 'Line') if line then local t0, t1 = (index - 1) / segments, index / segments line.From, line.To = origin:Lerp(target, t0), origin:Lerp(target, t1) line.Color = segments > 1 and scope.TracerColor:Lerp(scope.TracerColor2, (index - 0.5) / segments) or scope.TracerColor line.Thickness = scope.TracerThickness line.Transparency = 1 mark(line) end end end if scope.AmmoBar then local info = GetCachedWeaponInfo(player) local ammo, maxAmmo = info and info.ammo, info and info.maxAmmo if type(ammo) == 'number' and type(maxAmmo) == 'number' and maxAmmo > 0 and maxAmmo < math.huge then local ratio = math.clamp(ammo / maxAmmo, 0, 1) local barWidth = math.max(3 * uiScale, 2) local x = maxX + 3 * uiScale local background = slot(entry, 'ammoBg', 'Square', { Filled = true }) if background then background.Position, background.Size, background.Color = Vector2.new(x, minY), Vector2.new(barWidth, height), BLACK background.Transparency = 0.6 mark(background) end local fillHeight = math.max(height * ratio, ratio > 0 and 1 or 0) local fill = slot(entry, 'ammoFill', 'Square', { Filled = true }) if fill and fillHeight > 0 then fill.Position = Vector2.new(x, maxY - fillHeight) fill.Size = Vector2.new(barWidth, fillHeight) local mode = scope.AmmoBarMode if mode == 'Solid' then fill.Color = scope.AmmoBarColor elseif mode == 'Gradient' then fill.Color = scope.AmmoBarColor2:Lerp(scope.AmmoBarColor, ratio) else fill.Color = reactiveColor(ratio) end fill.Transparency = 1 mark(fill) end end end end local function stepIndicators(cam) local list = {} if extras.Tog('KPI0', true) then local now = os.clock() for key, value in pairs(extras.IndicatorEntries) do if value.Until > now then list[#list + 1] = value.Text else extras.IndicatorEntries[key] = nil end end table.sort(list) end local corner = extras.Opt('KPI1', 'Top Left') local vp = cam.ViewportSize for index, text in ipairs(list) do local drawable = indicatorTexts[index] if drawable == nil then drawable = newDrawing('Text', { Outline = true, Size = 14, Font = 3, Color = Color3.new(1, 1, 1), Center = false }) indicatorTexts[index] = drawable end if drawable then drawable.Text = text local y = 140 + (index - 1) * 16 if corner == 'Bottom Left' or corner == 'Bottom Right' then y = vp.Y - 160 - (index - 1) * 16 end local x = (corner == 'Top Right' or corner == 'Bottom Right') and (vp.X - 180) or 12 drawable.Position = Vector2.new(x, y) drawable.Visible = true end end for index = #list + 1, #indicatorTexts do local drawable = indicatorTexts[index] if drawable and drawable.Visible then drawable.Visible = false end end end extras.EspStep = function(deltaTime) local ok, err = pcall(function() frameId = frameId + 1 local cam = Workspace.CurrentCamera if cam == nil then return end local enemy, team = readScope('E'), readScope('T') if not EspRenderSettings.Enabled or (not enemy.On and not team.On) then if next(entries) ~= nil then for player, entry in pairs(entries) do destroyEntry(entry) entries[player] = nil end end stepIndicators(cam) return end local vp = cam.ViewportSize local uiScale = (vp.Y > 0 and vp.Y or 1080) / 1080 local character = LP.Character local myRoot = character and character:FindFirstChild('HumanoidRootPart') local myPos = myRoot and myRoot.Position or nil local maxDistance = extras.Num('KPEMD', 1000) local fitStatic = extras.Opt('KPEBF', 'Dynamic') == 'Static' local mouse = UserInputService:GetMouseLocation() local myTeamId = LP:GetAttribute('TeamID') for _, player in ipairs(Players:GetPlayers()) do if player ~= LP then local entry = entries[player] local active = IsPlayerInLocalEnvironment(player) local isTeam = false if active then local theirTeam = player:GetAttribute('TeamID') if myTeamId and theirTeam then isTeam = myTeamId == theirTeam elseif player.Team and LP.Team then isTeam = player.Team == LP.Team end end local scope = isTeam and team or enemy if active and scope.On then if entry == nil then entry = { drawables = {}, bones = {}, bonesAt = 0 } entries[player] = entry end local drawOk, drawErr = pcall(drawPlayer, player, entry, scope, cam, vp, uiScale, myPos, maxDistance, fitStatic, mouse) if not drawOk then extras.Report('esp_draw', drawErr) end sweep(entry) elseif entry then sweep(entry) end end end for player, entry in pairs(entries) do if player.Parent == nil then destroyEntry(entry) entries[player] = nil end end stepIndicators(cam) end) if not ok then extras.Report('esp_step', err) end end extras.SuppressCornerBox = function(isTeammate) local ok, result = pcall(function() local prefix = isTeammate and 'KPET' or 'KPEE' return extras.Tog(prefix .. '0', false) and extras.Tog(prefix .. 'FB', false) end) return ok and result == true end extras.StatusXOffset = function(isTeammate) local ok, result = pcall(function() local prefix = isTeammate and 'KPET' or 'KPEE' if extras.Tog(prefix .. '0', false) and extras.Tog(prefix .. 'AB', false) then return 7 * ((Workspace.CurrentCamera and Workspace.CurrentCamera.ViewportSize.Y or 1080) / 1080) end return 0 end) return ok and result or 0 end end, function(err) RivalsRuntimeBridge.Extras.EspError = tostring(err) task.defer(function() pcall(ReportRivalsRuntimeIssue, 'extras_esp_load', tostring(err)) end) return err end) -- ===== ZkxHub extras :: end ESP ===== -- ===== ZkxHub extras :: misc (images, config transfer, spoofer) ===== xpcall(function() local extras = RivalsRuntimeBridge.Extras -- ---------------------------------------------------------------- crosshair image library extras.Images = { Loaded = false, List = {}, Path = 'ZkxHub/Crosshair Images.json' } local function trim(value) if type(value) ~= 'string' then return '' end return value:match('^%s*(.-)%s*$') or '' end function extras.Images.Load() local images = extras.Images if images.Loaded then return end images.Loaded = true images.List = {} if type(isfile) ~= 'function' or not isfile(images.Path) then return end local ok, decoded = pcall(function() return HttpService:JSONDecode(readfile(images.Path)) end) local records = ok and type(decoded) == 'table' and (decoded.images or decoded) or nil if type(records) ~= 'table' then return end local seen = {} for _, record in ipairs(records) do if type(record) == 'table' then local name, source = trim(record.name), trim(record.source) if name ~= '' and source ~= '' and not seen[name] then seen[name] = true table.insert(images.List, { name = name, source = source }) end end end table.sort(images.List, function(left, right) return left.name < right.name end) end function extras.Images.Save() local images = extras.Images if type(writefile) ~= 'function' then return false end return ( pcall(function() if type(makefolder) == 'function' and type(isfolder) == 'function' and not isfolder('ZkxHub') then makefolder('ZkxHub') end writefile(images.Path, HttpService:JSONEncode({ version = 1, images = images.List })) end) ) end function extras.Images.Names() extras.Images.Load() local names = {} for _, record in ipairs(extras.Images.List) do table.insert(names, record.name) end return names end function extras.Images.Find(name) extras.Images.Load() for _, record in ipairs(extras.Images.List) do if record.name == name then return record end end return nil end function extras.Images.Add(nameValue, sourceValue) local name, source = trim(nameValue), trim(sourceValue) if name == '' or source == '' or extras.Images.Find(name) then return false end if source:match('^%d+$') then source = 'rbxassetid://' .. source end table.insert(extras.Images.List, { name = name, source = source }) table.sort(extras.Images.List, function(left, right) return left.name < right.name end) return extras.Images.Save() end function extras.Images.Remove(name) extras.Images.Load() for index, record in ipairs(extras.Images.List) do if record.name == name then table.remove(extras.Images.List, index) return extras.Images.Save() end end return false end function extras.Images.Refresh() local option = Options.KPX3 if option and type(option.SetValues) == 'function' then option:SetValues(extras.Images.Names()) end end function extras.Images.UseSelected() local option = Options.KPX3 local record = option and extras.Images.Find(option.Value) if not record then Library:Notify('Select a saved image first.') return false end if Options.P1S26I1 then Options.P1S26I1:SetValue(record.source) end if Options.P1S26D1 then Options.P1S26D1:SetValue('Image') end if Toggles.P1S26T1 and not Toggles.P1S26T1.Value then Toggles.P1S26T1:SetValue(true) end RivalsRuntimeBridge.ViewmodelVisuals.RefreshAll() return true end -- ---------------------------------------------------------------- config transfer (clipboard) local function configPath(name) local folder = tostring(SaveManager.Folder) .. '/settings' local sub = SaveManager.SubFolder if type(sub) == 'string' and sub ~= '' then folder = folder .. '/' .. sub end return folder .. '/' .. name .. '.json' end function extras.ExportConfig() local clip = setclipboard or toclipboard if type(clip) ~= 'function' then Library:Notify('Export needs setclipboard, which this executor lacks.') return false end local name = '__extras_export' local ok, err = SaveManager:Save(name) if not ok then Library:Notify('Export failed: ' .. tostring(err)) return false end local path = configPath(name) local readOk, data = pcall(readfile, path) pcall(delfile, path) if not readOk or type(data) ~= 'string' then Library:Notify('Export failed: could not read the temporary file.') return false end clip(data) Library:Notify('Config copied to clipboard (' .. #data .. ' chars).') return true end function extras.ImportConfig(text) text = trim(text) if text == '' then Library:Notify('Paste a config into the box first.') return false end local decodeOk, decoded = pcall(function() return HttpService:JSONDecode(text) end) if not decodeOk or type(decoded) ~= 'table' or type(decoded.objects) ~= 'table' then Library:Notify('That does not look like an exported config.') return false end local name = '__extras_import' SaveManager:CheckFolderTree() local path = configPath(name) local writeOk, writeErr = pcall(writefile, path, text) if not writeOk then Library:Notify('Import failed: ' .. tostring(writeErr)) return false end local loaded, loadErr = SaveManager:Load(name) pcall(delfile, path) if not loaded then Library:Notify('Import failed: ' .. tostring(loadErr)) return false end task.defer(function() pcall(extras.Reconcile) end) Library:Notify('Config imported.') return true end -- ---------------------------------------------------------------- spoofer extras: names / avatar text, own profile data local Names = { Objects = setmetatable({}, { __mode = 'k' }), Records = setmetatable({}, { __mode = 'k' }), Connections = {}, Started = false, Accum = 0, } extras.Names = Names local function isTextObject(instance) return instance:IsA('TextLabel') or instance:IsA('TextButton') end local function isImageObject(instance) return instance:IsA('ImageLabel') or instance:IsA('ImageButton') end function Names.Collect(root) for _, descendant in ipairs(root:GetDescendants()) do if isTextObject(descendant) or isImageObject(descendant) then Names.Objects[descendant] = true end end end function Names.Start() if Names.Started then return end local playerGui = LP:FindFirstChildOfClass('PlayerGui') if not playerGui then return end Names.Started = true Names.Collect(playerGui) Names.Connections[1] = playerGui.DescendantAdded:Connect(function(descendant) if isTextObject(descendant) or isImageObject(descendant) then Names.Objects[descendant] = true end end) Names.Connections[2] = playerGui.DescendantRemoving:Connect(function(descendant) Names.Objects[descendant] = nil Names.Records[descendant] = nil end) end function Names.Stop() Names.Restore() for _, connection in ipairs(Names.Connections) do pcall(function() connection:Disconnect() end) end Names.Connections = {} Names.Started = false Names.Objects = setmetatable({}, { __mode = 'k' }) end function Names.Restore() for object, record in pairs(Names.Records) do pcall(function() if isTextObject(object) then if object.Text == record.spoofed then object.Text = record.orig end elseif object.Image == record.spoofed then object.Image = record.orig end end) end Names.Records = setmetatable({}, { __mode = 'k' }) end local function escapePattern(text) return (text:gsub('([%^%$%(%)%%%.%[%]%*%+%-%?])', '%%%1')) end local function escapeReplacement(text) return (text:gsub('%%', '%%%%')) end function Names.Targets() local targets = {} local any = false for _, player in ipairs(Players:GetPlayers()) do local isLocal = player == LP local nameOn = extras.Tog(isLocal and 'KPN1' or 'KPN5', false) local displayOn = extras.Tog(isLocal and 'KPN3' or 'KPN7', false) local avatarOn = extras.Tog(isLocal and 'KPN9' or 'KPNB', false) if nameOn or displayOn or avatarOn then any = true targets[#targets + 1] = { Player = player, Name = nameOn and trim(extras.Opt(isLocal and 'KPN2' or 'KPN6', '')) or '', Display = displayOn and trim(extras.Opt(isLocal and 'KPN4' or 'KPN8', '')) or '', Avatar = avatarOn and trim(tostring(extras.Opt(isLocal and 'KPNA' or 'KPNC', ''))) or '', } end end return targets, any end function Names.Step() local targets, any = Names.Targets() if not any then if next(Names.Records) ~= nil then Names.Restore() end return end Names.Start() for object in pairs(Names.Objects) do if object.Parent ~= nil then local isText = isTextObject(object) local current = isText and object.Text or object.Image local record = Names.Records[object] local base = current if record and current == record.spoofed then base = record.orig end local result = base for _, target in ipairs(targets) do local player = target.Player if isText then if target.Display ~= '' and player.DisplayName ~= '' and result:find(player.DisplayName, 1, true) then result = result:gsub(escapePattern(player.DisplayName), escapeReplacement(target.Display)) end if target.Name ~= '' and result:find(player.Name, 1, true) then result = result:gsub(escapePattern(player.Name), escapeReplacement(target.Name)) end elseif target.Avatar ~= '' then local lowered = result:lower() if ( lowered:find('thumb', 1, true) or lowered:find('headshot', 1, true) or lowered:find('avatar', 1, true) ) and result:find(tostring(player.UserId), 1, true) then result = result:gsub(tostring(player.UserId), escapeReplacement(target.Avatar)) end end end if result ~= base then Names.Records[object] = { orig = base, spoofed = result } if isText then object.Text = result else object.Image = result end elseif record then if isText then object.Text = base else object.Image = base end Names.Records[object] = nil end end end end -- own profile fields served through the game's player-data proxy (the cosmetics module owns the proxy) extras.DataFields = { EventCurrency = { 'KPS1', 'KPS2', 1 }, WeaponKeys = { 'KPS3', 'KPS4', 1 }, Level = { 'KPS5', 'KPS6', 1 }, CasualWins = { 'KPS7', 'KPS8', 1 }, RankedWins = { 'KPS9', 'KPSA', 1 }, CasualWinPercent = { 'KPSB', 'KPSC', 0.01 }, RankedWinPercent = { 'KPSD', 'KPSE', 0.01 }, RankedCurrentELO = { 'KPSF', 'KPSG', 1 }, FavoriteMap = { 'KPSH', 'KPSI', 'text' }, } local function dataFieldImpl(fieldName) local spec = extras.DataFields[fieldName] if spec == nil or not extras.Tog(spec[1], false) then return nil end local raw = extras.Opt(spec[2], '') if spec[3] == 'text' then raw = trim(tostring(raw)) return raw ~= '' and raw or nil end local number = tonumber(raw) if number == nil then return nil end return number * spec[3] end -- sits on the game's player-data read path, so it must never throw extras.DataField = function(fieldName) local ok, value = pcall(dataFieldImpl, fieldName) if ok then return value end return nil end -- The profile page never reads the local player data: ViewProfile.Fetch asks the server (Remotes.Misc.RequestProfile) and builds -- every number from that answer, so the data proxy below only reaches screens that read local data (shop keys, event currency). -- Fetch uses its ReplicatedStorage upvalue for that one call only, so a table that answers the same path stands in for it and -- patches the answer when it is about the local player. extras.ProfileFields = { 'Level', 'CasualWins', 'CasualWinPercent', 'RankedWins', 'RankedWinPercent', 'RankedCurrentELO', 'FavoriteMap' } extras.ProfileHook = { Installed = false } local function profileOverlay(real, overrides) return setmetatable({}, { __index = function(_, key) local override = overrides[key] if override ~= nil then return override end local value = real[key] if type(value) == 'function' then return function(_, ...) return value(real, ...) end end return value end, }) end local function findUpvalueIndex(fn, wanted) if type(debug.getupvalues) == 'function' then local ok, upvalues = pcall(debug.getupvalues, fn) if ok and type(upvalues) == 'table' then for index, value in pairs(upvalues) do if typeof(value) == 'Instance' and value == wanted then return index end end end end if type(debug.getupvalue) == 'function' then for index = 1, 80 do local ok, value = pcall(debug.getupvalue, fn, index) if not ok then break end if typeof(value) == 'Instance' and value == wanted then return index end end end return nil end function extras.ProfilePatch(reply) if type(reply) ~= 'table' then return reply end local patched = nil for _, field in ipairs(extras.ProfileFields) do local value = extras.DataField(field) if value ~= nil then patched = patched or table.clone(reply) patched[field] = value end end return patched or reply end function extras.InstallProfileHook() local hook = extras.ProfileHook if hook.Installed then return true end local wanted = false for _, field in ipairs(extras.ProfileFields) do local spec = extras.DataFields[field] if spec and extras.Tog(spec[1], false) then wanted = true break end end if not wanted then return false end local ok, result = pcall(function() local playerScripts = LP:FindFirstChild('PlayerScripts') local modules = playerScripts and playerScripts:FindFirstChild('Modules') local pages = modules and modules:FindFirstChild('Pages') local pageModule = pages and pages:FindFirstChild('ViewProfile') local service = game:GetService('ReplicatedStorage') local remotes = service:FindFirstChild('Remotes') local misc = remotes and remotes:FindFirstChild('Misc') local remote = misc and misc:FindFirstChild('RequestProfile') if not pageModule or not remote then return false end local loaded, page = pcall(require, pageModule) if not loaded then error(page, 0) end local fetch = type(page) == 'table' and page.Fetch or nil if type(fetch) ~= 'function' or type(debug.setupvalue) ~= 'function' then return false end local index = findUpvalueIndex(fetch, service) if not index then return false end local standIn = profileOverlay(service, { Remotes = profileOverlay(remotes, { Misc = profileOverlay(misc, { RequestProfile = profileOverlay(remote, { InvokeServer = function(_, target, ...) local reply = remote:InvokeServer(target, ...) if target == LP then return extras.ProfilePatch(reply) end return reply end, }), }), }), }) debug.setupvalue(fetch, index, standIn) hook.Installed = true hook.Page, hook.Fetch, hook.Index, hook.Service = page, fetch, index, service return true end) if not ok then extras.Report('profile_hook', result) return false end return result == true end function extras.RefreshProfilePage() local hook = extras.ProfileHook local page = hook.Installed and hook.Page or nil if not page then return end -- the server answers a profile request with nothing when the previous one is under a second old, and Fetch closes the page on -- nothing, so rapid row changes are coalesced and the fetch waits until the last change is 1.5 s old hook.RefreshToken = (hook.RefreshToken or 0) + 1 local token = hook.RefreshToken task.delay(1.5, function() if token ~= hook.RefreshToken or not hook.Installed then return end -- the game's own threads run at identity 2; Fetch lazily requires modules, which an executor thread may not do local previousIdentity = type(getthreadidentity) == 'function' and getthreadidentity() or nil if type(setthreadidentity) == 'function' then pcall(setthreadidentity, 2) end pcall(function() if page:IsOpen() and page._last_player_object == LP then page:Fetch(LP) end end) if previousIdentity ~= nil and type(setthreadidentity) == 'function' then pcall(setthreadidentity, previousIdentity) end end) end function extras.RestoreProfileHook() local hook = extras.ProfileHook if not hook.Installed then return end hook.Installed = false pcall(debug.setupvalue, hook.Fetch, hook.Index, hook.Service) end table.insert(extras.Cleanups, extras.RestoreProfileHook) function extras.RefreshData() extras.InstallProfileHook() extras.RefreshProfilePage() local cosmetics = extras.CosmeticsRef if not cosmetics then return false end local anyOn = false for _, spec in pairs(extras.DataFields) do if extras.Tog(spec[1], false) then anyOn = true break end end if anyOn then local controller = cosmetics.ResolvePlayerDataController() local currentData = controller and rawget(controller, 'CurrentData') or nil if type(currentData) == 'table' then pcall(cosmetics.LoadPlayerDataProxy, currentData) end end for fieldName in pairs(extras.DataFields) do pcall(cosmetics.TriggerNativeDataChanged, fieldName) end return true end -- ---------------------------------------------------------------- avatar changer -- Local only. The client may not call Humanoid:ApplyDescription ("can only be called by the backend server"), so another user's look is -- built the way the game builds its own preview models (a model made from that user's HumanoidDescription) and its cosmetic pieces are -- copied onto the local character: accessories, clothes, body colors, head. Nothing replicates, other players keep seeing the real avatar. -- Everything it changes is recorded per character and put back by Revert (the game's Visibility module rewrites part transparency on -- its own, so original accessories are taken off the character instead of being hidden, and put back on restore). -- Keeping the look: a respawn gives a new character (CharacterAdded), but the server can also add its own accessories or swap the -- shirt after spawn, and the fighter's model is not always LP.Character. So once a second every body is compared with what was -- applied to it: a foreign accessory is taken off, a body that lost one of our pieces (or never had them) is dressed again. local Avatar = { Records = setmetatable({}, { __mode = 'k' }), Dressing = setmetatable({}, { __mode = 'k' }), Tries = setmetatable({}, { __mode = 'k' }), Cache = {}, Busy = false, Active = nil, Preview = nil, Connection = nil, GuardToken = 0, Controller = nil, } extras.Avatar = Avatar -- the game's own recipe for its preview models: its limb meshes, everything else from the account local AVATAR_LIMBS = { Torso = 15365012259, LeftArm = 15365010034, RightArm = 15365012263, LeftLeg = 15365010038, RightLeg = 15365010030, } local AVATAR_PARTS = { 'Accessories', 'Clothing', 'Body Colors', 'Head & Face' } local AVATAR_COLORS = { 'HeadColor3', 'LeftArmColor3', 'LeftLegColor3', 'RightArmColor3', 'RightLegColor3', 'TorsoColor3' } function Avatar.PartsWanted() local wanted = {} for _, name in ipairs(AVATAR_PARTS) do wanted[name] = extras.Has('KPV3', name, true) end return wanted end function Avatar.Resolve(text) text = trim(tostring(text or '')) if text == '' then return nil, 'Enter a Roblox user id or name first.' end if text:match('^%d+$') then return tonumber(text) end local ok, userId = pcall(Players.GetUserIdFromNameAsync, Players, text) if ok and type(userId) == 'number' then return userId end return nil, 'No Roblox user is called ' .. text .. '.' end function Avatar.NameOf(userId) local ok, name = pcall(Players.GetNameFromUserIdAsync, Players, userId) if ok and type(name) == 'string' and name ~= '' then return name end return tostring(userId) end -- one model per user, built once; only the active one and the newest are kept function Avatar.Build(userId) local cached = Avatar.Cache[userId] if cached and cached:FindFirstChildOfClass('Humanoid') then return cached end local ok, description = pcall(Players.GetHumanoidDescriptionFromUserIdAsync, Players, userId) if not ok then return nil, tostring(description) end for property, assetId in pairs(AVATAR_LIMBS) do description[property] = assetId end local built, model = pcall(Players.CreateHumanoidModelFromDescriptionAsync, Players, description, Enum.HumanoidRigType.R15) if not built then return nil, tostring(model) end local keep = Avatar.Active and Avatar.Active.UserId or nil for id, old in pairs(Avatar.Cache) do if id ~= keep then pcall(old.Destroy, old) Avatar.Cache[id] = nil end end Avatar.Cache[userId] = model return model end -- MeshId can only be written from a thread with the executor's capabilities, and a menu callback may run at the game's identity 2: -- the two functions that touch the character run at identity 8 and hand the caller's identity back afterwards local function elevated(fn) return function(...) local previous = type(getthreadidentity) == 'function' and getthreadidentity() or nil if previous ~= nil and type(setthreadidentity) == 'function' then pcall(setthreadidentity, 8) end local packed = table.pack(pcall(fn, ...)) if previous ~= nil and type(setthreadidentity) == 'function' then pcall(setthreadidentity, previous) end if not packed[1] then error(packed[2], 0) end return table.unpack(packed, 2, packed.n) end end -- Undo keeps { instance, property, old value, the value that was set }: the first three put things back, the last one lets the -- check below notice that the game changed it again local function setProp(record, instance, property, value) record.Undo[#record.Undo + 1] = { instance, property, instance[property], value } instance[property] = value end -- an instance made by us is registered before it is parented, so a deferred ChildAdded can never mistake it for the game's local function created(record, instance) record.Created[#record.Created + 1] = instance record.CreatedSet[instance] = true return instance end -- the attachment of the same name on one of the character's body parts (not inside another accessory) local function bodyAttachment(character, name) for _, part in ipairs(character:GetChildren()) do if part:IsA('BasePart') then local found = part:FindFirstChild(name) if found and found:IsA('Attachment') then return found end end end return nil end -- A client-side AddAccessory only parents the accessory: it makes no weld and the old one still points at the preview model. -- So the weld is rebuilt the way the engine builds it (Handle -> body part, C0/C1 from the two attachments), before it is added. function Avatar.PrepareAccessory(accessory, character) local clone = accessory:Clone() local handle = clone:FindFirstChild('Handle') if not handle or not handle:IsA('BasePart') then clone:Destroy() return nil end for _, descendant in ipairs(clone:GetDescendants()) do if descendant:IsA('JointInstance') or descendant:IsA('WeldConstraint') then descendant:Destroy() elseif descendant:IsA('BasePart') then descendant.CanCollide = false descendant.CanTouch = false descendant.CanQuery = false descendant.Massless = true end end local handleAttachment = handle:FindFirstChildOfClass('Attachment') local target = handleAttachment and bodyAttachment(character, handleAttachment.Name) if not target then -- layered clothing carries no attachment: the engine fits it to the body, which a client cannot ask for clone:Destroy() return nil end local weld = Instance.new('Weld') weld.Name = 'AccessoryWeld' weld.Part0 = handle weld.Part1 = target.Parent weld.C0 = handleAttachment.CFrame weld.C1 = target.CFrame weld.Parent = handle return clone end Avatar.Revert = elevated(function(character) local record = Avatar.Records[character] if not record then return end Avatar.Records[character] = nil for _, made in ipairs(record.Created) do pcall(made.Destroy, made) end for _, original in ipairs(record.Removed) do pcall(function() original.Parent = character end) end for index = #record.Undo, 1, -1 do local entry = record.Undo[index] pcall(function() entry[1][entry[2]] = entry[3] end) end end) Avatar.ApplyTo = elevated(function(character, model, wanted) local humanoid = character and character:FindFirstChildOfClass('Humanoid') local head = character and character:FindFirstChild('Head') if not humanoid or not head then return false, 'There is no character to dress yet.' end Avatar.Revert(character) local record = { Created = {}, CreatedSet = {}, Removed = {}, Undo = {}, Skipped = {}, Wanted = wanted, Complete = false } Avatar.Records[character] = record local ok, err = pcall(function() if wanted['Accessories'] then local originals = {} for _, child in ipairs(character:GetChildren()) do if child:IsA('Accessory') then originals[#originals + 1] = child end end for _, original in ipairs(originals) do record.Removed[#record.Removed + 1] = original original.Parent = nil end for _, accessory in ipairs(model:GetChildren()) do if accessory:IsA('Accessory') then local clone = Avatar.PrepareAccessory(accessory, character) if clone then created(record, clone) humanoid:AddAccessory(clone) else record.Skipped[#record.Skipped + 1] = accessory.Name end end end end if wanted['Clothing'] then local function swap(className, property, source) local current = character:FindFirstChildOfClass(className) if current then setProp(record, current, property, source and source[property] or '') elseif source then local clone = created(record, source:Clone()) clone.Parent = character end end swap('Shirt', 'ShirtTemplate', model:FindFirstChildOfClass('Shirt')) swap('Pants', 'PantsTemplate', model:FindFirstChildOfClass('Pants')) swap('ShirtGraphic', 'Graphic', model:FindFirstChildOfClass('ShirtGraphic')) end if wanted['Body Colors'] then local mine = character:FindFirstChildOfClass('BodyColors') local theirs = model:FindFirstChildOfClass('BodyColors') if mine and theirs then for _, property in ipairs(AVATAR_COLORS) do setProp(record, mine, property, theirs[property]) end end end if wanted['Head & Face'] then local theirs = model:FindFirstChild('Head') if theirs and theirs:IsA('MeshPart') and head:IsA('MeshPart') then -- the game's own rule for a head that is too big is 25 %; the size is kept so the neck and the camera do not move local fits = true for _, axis in ipairs({ 'X', 'Y', 'Z' }) do if math.abs(theirs.Size[axis] / head.Size[axis] - 1) > 0.25 then fits = false end end if fits then setProp(record, head, 'MeshId', theirs.MeshId) setProp(record, head, 'TextureID', theirs.TextureID) setProp(record, head, 'Size', theirs.Size) local theirFace = theirs:FindFirstChildOfClass('Decal') local myFace = head:FindFirstChildOfClass('Decal') if theirFace and myFace then setProp(record, myFace, 'Texture', theirFace.Texture) elseif theirFace then local face = created(record, theirFace:Clone()) face.Parent = head end else record.Skipped[#record.Skipped + 1] = 'head (size)' end end end end) if not ok then Avatar.Revert(character) return false, tostring(err) end record.Complete = true return true, nil, record.Skipped end) local function sameValue(a, b) if a == b then return true end local kind = typeof(a) if kind == 'Vector3' then return (a - b).Magnitude < 0.001 end if kind == 'Color3' then return math.abs(a.R - b.R) + math.abs(a.G - b.G) + math.abs(a.B - b.B) < 0.01 end return false end -- 'none': nothing was applied to this body; 'broken': one of our pieces is gone or was changed back; 'foreign': the game added -- accessories (returned as the second value); 'ok' function Avatar.Status(character) local record = Avatar.Records[character] if not record or not record.Complete then return 'none' end for _, made in ipairs(record.Created) do if made.Parent == nil then return 'broken' end end for _, entry in ipairs(record.Undo) do local instance = entry[1] if instance.Parent == nil or not sameValue(instance[entry[2]], entry[4]) then return 'broken' end end if record.Wanted['Accessories'] then local foreign = nil for _, child in ipairs(character:GetChildren()) do if child:IsA('Accessory') and not record.CreatedSet[child] then foreign = foreign or {} foreign[#foreign + 1] = child end end if foreign then return 'foreign', foreign end end return 'ok' end -- the game's accessories that showed up after the look was applied are taken off the same way the originals were function Avatar.Absorb(character, foreign) local record = Avatar.Records[character] if not record then return end for _, accessory in ipairs(foreign) do if #record.Removed < 40 then record.Removed[#record.Removed + 1] = accessory accessory.Parent = nil else pcall(accessory.Destroy, accessory) end end end -- the bodies the game shows for the local fighter: the character, and the fighter entity's model when that is another one function Avatar.Targets() local list = {} local character = LP.Character if character then list[#list + 1] = character end if Avatar.Controller == nil then local ok, controller = pcall(require, LP.PlayerScripts.Controllers.FighterController) if ok and type(controller) == 'table' then Avatar.Controller = controller end end local ok, model = pcall(function() local fighter = Avatar.Controller and Avatar.Controller.LocalFighter local entity = fighter and fighter.Entity return entity and entity.Model end) if ok and model and model ~= character and typeof(model) == 'Instance' then list[#list + 1] = model end return list end -- the Locker / equipment scene asks FighterController:GenerateCharacterModel(userId).Template:Clone(); answering that for the -- local user id with the model built above makes the character there follow function Avatar.SeedPreview(model) local ok, controller = pcall(require, LP.PlayerScripts.Controllers.FighterController) local cache = ok and type(controller) == 'table' and controller._character_model_cache or nil if type(cache) ~= 'table' then return false end local key = tostring(LP.UserId) if Avatar.Preview == nil then Avatar.Preview = { Cache = cache, Key = key, Original = cache[key] } end cache[key] = { Template = model } return true end function Avatar.RestorePreview() local preview = Avatar.Preview if not preview then return end Avatar.Preview = nil preview.Cache[preview.Key] = preview.Original end -- a new body (respawn, round start) is dressed once the server has finished its own appearance function Avatar.Dress(body) if Avatar.Dressing[body] then return end Avatar.Dressing[body] = true task.spawn(function() local ok, err = pcall(function() local deadline = os.clock() + 12 while os.clock() < deadline and body.Parent ~= nil do if body:FindFirstChildOfClass('Humanoid') and body:FindFirstChild('Head') and (body ~= LP.Character or LP:HasAppearanceLoaded()) then break end task.wait(0.2) end task.wait(0.3) local active = Avatar.Active if active and body.Parent ~= nil then -- only a failure makes the next attempt wait longer if Avatar.ApplyTo(body, active.Model, Avatar.PartsWanted()) then Avatar.Tries[body] = nil end end end) Avatar.Dressing[body] = nil if not ok then extras.Report('avatar_dress', err) end end) end -- one pass of the once-a-second check function Avatar.Keep(body) if body.Parent == nil or Avatar.Dressing[body] then return end local status, foreign = Avatar.Status(body) if status == 'ok' then Avatar.Tries[body] = nil elseif status == 'foreign' then Avatar.Absorb(body, foreign) else -- a body that cannot be dressed (no humanoid yet, an error) is retried less and less often local tries = Avatar.Tries[body] if tries and os.clock() - tries.at < math.min(1.5 * 2 ^ tries.count, 30) then return end Avatar.Tries[body] = { count = (tries and tries.count or 0) + 1, at = os.clock() } Avatar.Dress(body) end end function Avatar.GuardStep() if Avatar.Active and extras.Tog('KPV2', true) and not Avatar.Busy then for _, body in ipairs(Avatar.Targets()) do Avatar.Keep(body) end end end function Avatar.StartGuard() Avatar.GuardToken += 1 local token = Avatar.GuardToken local function pass() if Avatar.GuardToken ~= token then return end local ok, err = pcall(Avatar.GuardStep) if not ok then extras.Report('avatar_guard', err) end task.delay(1, pass) end task.delay(1, pass) end function Avatar.Watch() if Avatar.Connection then return end Avatar.Connection = LP.CharacterAdded:Connect(function(character) if Avatar.Active and extras.Tog('KPV2', true) then Avatar.Dress(character) end end) Avatar.StartGuard() end -- the copy list changed while an avatar is on: dress the current bodies again function Avatar.Reapply() local active = Avatar.Active if active then for _, body in ipairs(Avatar.Targets()) do pcall(Avatar.ApplyTo, body, active.Model, Avatar.PartsWanted()) end end end function Avatar.Run(text) if Avatar.Busy then Library:Notify('Still loading the last avatar.') return end Avatar.Busy = true task.spawn(function() local ok, err = pcall(function() local userId, reason = Avatar.Resolve(text) if not userId then Library:Notify(reason) return end local model, why = Avatar.Build(userId) if not model then Library:Notify('Could not load that avatar: ' .. tostring(why)) return end Avatar.Active = { UserId = userId, Model = model } Avatar.Watch() local applied, problem, skipped = false, 'There is no character to dress yet.', nil for index, body in ipairs(Avatar.Targets()) do local done, why2, skipped2 = Avatar.ApplyTo(body, model, Avatar.PartsWanted()) if index == 1 then applied, problem, skipped = done, why2, skipped2 end end if extras.Tog('KPV4', true) then Avatar.SeedPreview(model) else Avatar.RestorePreview() end local name = Avatar.NameOf(userId) if not applied then Library:Notify('Avatar not applied: ' .. tostring(problem)) elseif skipped and #skipped > 0 then Library:Notify('Avatar applied: ' .. name .. '. Skipped: ' .. table.concat(skipped, ', ') .. '.') else Library:Notify('Avatar applied: ' .. name .. '.') end end) Avatar.Busy = false if not ok then extras.Report('avatar_run', err) Library:Notify('The avatar changer failed, see the console.') end end) end function Avatar.RestoreAll() Avatar.Active = nil local bodies = {} for body in pairs(Avatar.Records) do bodies[#bodies + 1] = body end for _, body in ipairs(bodies) do Avatar.Revert(body) end Avatar.RestorePreview() end function Avatar.Restore() Avatar.RestoreAll() Library:Notify('Your own avatar is back.') end function Avatar.Destroy() Avatar.RestoreAll() Avatar.GuardToken += 1 if Avatar.Connection then pcall(function() Avatar.Connection:Disconnect() end) Avatar.Connection = nil end for id, model in pairs(Avatar.Cache) do pcall(model.Destroy, model) Avatar.Cache[id] = nil end end table.insert(extras.Cleanups, Avatar.Destroy) -- spoof text scan rides on the ESP step so there is a single patched call site local baseEspStep = extras.EspStep extras.EspStep = function(deltaTime) baseEspStep(deltaTime) Names.Accum = Names.Accum + (type(deltaTime) == 'number' and deltaTime or 0.016) if Names.Accum < 0.5 then return end Names.Accum = 0 local ok, err = pcall(Names.Step) if not ok then extras.Report('spoof_text', err) end end table.insert(extras.Cleanups, function() Names.Stop() end) end, function(err) RivalsRuntimeBridge.Extras.MiscError = tostring(err) task.defer(function() pcall(ReportRivalsRuntimeIssue, 'extras_misc_load', tostring(err)) end) return err end) -- ===== ZkxHub extras :: end misc ===== -- ===== ZkxHub extras :: UI builders (each one adds groupboxes to an existing tab) ===== xpcall(function() local extras = RivalsRuntimeBridge.Extras local function safeGroup(tab, side, title, builder) local ok, err = pcall(function() local box = side == 'L' and tab:AddLeftGroupbox(title) or tab:AddRightGroupbox(title) builder(box) end) if not ok then extras.Report('ui_' .. title, err) end end -- A "Sections" multi-select: each section's controls sit in a hidden block that appears when its name is -- ticked, so a long box stays one short row until you need a part of it. local function sections(box, id, defs, default) local names = {} for _, definition in ipairs(defs) do names[#names + 1] = definition[1] end box:AddDropdown(id, { Text = 'Sections', Values = names, Default = default or {}, Multi = true, Tooltip = 'Tick the sections you want to see and edit.', }) for _, definition in ipairs(defs) do local block = box:AddDependencyBox() definition[2](block) block:SetupDependencies({ { Options[id], definition[1] } }) end end local function toggle(box, id, text, default, tip) return box:AddToggle(id, { Text = text, Default = default, Tooltip = tip }) end local function slider(box, id, text, default, min, max, rounding, suffix, tip) return box:AddSlider(id, { Text = text, Default = default, Min = min, Max = max, Rounding = rounding or 0, Suffix = suffix or '', Compact = true, Tooltip = tip, }) end local function dropdown(box, id, text, values, default, tip) return box:AddDropdown(id, { Text = text, Values = values, Default = default, Multi = false, Tooltip = tip }) end local function multi(box, id, text, values, default, tip) return box:AddDropdown(id, { Text = text, Values = values, Default = default, Multi = true, Tooltip = tip }) end local function color(box, id, text, default) return box:AddLabel(text):AddColorPicker(id, { Default = default, Title = text }) end local function input(box, id, text, default, placeholder, tip) return box:AddInput(id, { Text = text, Default = default or '', Placeholder = placeholder or '', AllowEmpty = true, EmptyReset = '', Tooltip = tip, }) end -- ---------------------------------------------------------------- Combat tab extras.BuildCombatUi = function(tab) safeGroup(tab, 'L', 'Silent Aim Rules', function(box) toggle( box, 'KPA0', 'Enable Rules', false, 'Applies while Aimbot Mode is Silent. Off keeps the original behaviour.' ) sections(box, 'KPASEC', { { 'Activation', function(t) toggle(t, 'KPA1', 'Not Reloading', true, 'Silent shots are not redirected while reloading.') toggle(t, 'KPA2', 'Scoped In', false, 'Only redirect shots while fully scoped in.') end, }, { 'Targets', function(t) toggle(t, 'KPA3', 'Target Visible', true, 'Target must be visible.') toggle(t, 'KPA4', 'Target Vulnerable', true, 'Skips targets with spawn protection.') toggle(t, 'KPA5', 'Target Not Deflecting', true, 'Skips targets with an active Katana deflect.') toggle(t, 'KPA6', 'Target Not Shielded', true, 'Skips targets whose Riot Shield blocks the shot.') toggle( t, 'KPA7', 'Target Within FOV', true, 'Keeps the FOV circle binding even when Ignore FOV is on.' ) end, }, { 'Hitbox', function(t) multi( t, 'KPA8', 'Allowed Hitboxes', { 'Head', 'Body' }, { 'Head', 'Body' }, 'Hitboxes silent aim may use.' ) dropdown( t, 'KPA9', 'Hitbox Selection', { 'Closest To Crosshair', 'Random' }, 'Closest To Crosshair', 'Replaces Aim Part while the rules are on.' ) slider( t, 'KPAA', 'Head %', 50, 0, 100, 0, '%', 'Random selection only: chance of picking the head first.' ) dropdown( t, 'KPAB', 'Aim Point', { 'Center', 'Surface' }, 'Center', 'Surface aims at the point of the hitbox nearest your crosshair.' ) slider( t, 'KPAC', 'Surface Reduction', 50, 0, 95, 0, '%', 'Shrinks the hitbox used for the Surface point.' ) toggle( t, 'KPAD', 'Preserve Depth', false, 'Keeps full size along the viewing direction when shrinking.' ) end, }, }) end) safeGroup(tab, 'R', 'Triggerbot Rules', function(box) toggle(box, 'KPB0', 'Enable Rules', false, 'Extra conditions for the triggerbot.') toggle(box, 'KPB1', 'Not Reloading', true) toggle(box, 'KPB2', 'Scoped In', false) multi( box, 'KPB3', 'Target Filters', { 'Visible', 'Vulnerable', 'Not Deflecting', 'Not Shielded' }, { 'Visible', 'Vulnerable', 'Not Deflecting', 'Not Shielded' }, 'Conditions a target must meet before the trigger fires.' ) slider(box, 'KPB4', 'Activation Chance', 100, 1, 100, 0, '%', 'Re-rolled about four times a second.') slider( box, 'KPB5', 'Release Delay', 0, 0, 1, 2, ' s', 'Keeps a held trigger down this long after the target is lost.' ) end) safeGroup(tab, 'R', 'Shot Hooks', function(box) local antiKatana = toggle( box, 'KPD0', 'Anti Katana', false, 'Cancels your own gun shots at a target that is deflecting or shielded.' ) local manipulate = toggle( box, 'KPD1', 'Manipulate', false, 'Silent aim can use targets that are only reachable by moving the shot origin up to about 10 studs. The server may reject it.' ) dropdown( box, 'KPD2', 'Manipulation Check', { 'On Best Target', 'On All Targets' }, 'On Best Target', 'On Best Target only widens the committed target.' ) toggle(box, 'KPI0', 'Show Indicator List', true, 'Small on-screen list for Manipulated and Anti Katana blocks.') dropdown( box, 'KPI1', 'Indicator Corner', { 'Top Left', 'Top Right', 'Bottom Left', 'Bottom Right' }, 'Top Left' ) local function sync() pcall(RivalsModsState.EnsureHooks) extras.SyncControllers() end antiKatana:OnChanged(GuardRivalsCallback('Extras_AntiKatana', sync)) manipulate:OnChanged(GuardRivalsCallback('Extras_Manipulate', sync)) end) end -- ---------------------------------------------------------------- ESP tab extras.BuildEspUi = function(tab) local function scope(letter, title, side) local p = 'KPE' .. letter safeGroup(tab, side, title, function(box) toggle(box, p .. '0', 'Enable ' .. title, false, 'Master switch for the extras below.') sections(box, p .. 'SEC', { { 'Skeleton', function(t) toggle(t, p .. 'SK', 'Skeleton', false) color(t, p .. 'SKC', 'Skeleton Color', Color3.fromRGB(255, 255, 255)) toggle( t, p .. 'SKG', 'Skeleton Gradient', false, 'Blends to the second color from top to bottom.' ) color(t, p .. 'SKH', 'Skeleton Color 2', Color3.fromRGB(255, 70, 70)) slider(t, p .. 'SKT', 'Skeleton Thickness', 1, 1, 6, 0) end, }, { 'Head', function(t) toggle(t, p .. 'HM', 'Head Marker', false) dropdown(t, p .. 'HMS', 'Head Shape', { 'Circle', 'Cross', 'Diamond' }, 'Circle') color(t, p .. 'HMC', 'Head Color', Color3.fromRGB(255, 255, 255)) toggle(t, p .. 'HMF', 'Head Filled', false) toggle(t, p .. 'HMO', 'Head Outline', true) slider(t, p .. 'HMR', 'Head Size', 1, 0.2, 4, 1, 'x') end, }, { 'Tracer', function(t) toggle(t, p .. 'TR', 'Tracer', false) color(t, p .. 'TRC', 'Tracer Color', Color3.fromRGB(255, 255, 255)) toggle(t, p .. 'TRG', 'Tracer Gradient', false) color(t, p .. 'TRH', 'Tracer Color 2', Color3.fromRGB(255, 70, 70)) slider(t, p .. 'TRT', 'Tracer Thickness', 1, 1, 6, 0) dropdown(t, p .. 'TRF', 'Tracer From', { 'Bottom', 'Center', 'Top', 'Mouse' }, 'Bottom') dropdown(t, p .. 'TRW', 'Tracer To', { 'Feet', 'Head' }, 'Feet') toggle(t, p .. 'TRO', 'Tracer Outline', true) end, }, { 'Box', function(t) toggle(t, p .. 'FB', 'Full Box', false, 'Replaces the corner box with a full rectangle.') color(t, p .. 'FBC', 'Full Box Color', Color3.fromRGB(255, 255, 255)) toggle(t, p .. 'FF', 'Filled Box', false) color(t, p .. 'FFC', 'Fill Color', Color3.fromRGB(255, 60, 60)) slider(t, p .. 'FFA', 'Fill Opacity', 0.25, 0, 1, 2) toggle(t, p .. 'FFG', 'Fill Gradient', false, 'Blends top to bottom.') color(t, p .. 'FFH', 'Fill Color 2', Color3.fromRGB(60, 60, 255)) end, }, { 'Ammo', function(t) toggle(t, p .. 'AB', 'Ammo Bar', false) color(t, p .. 'ABC', 'Ammo Color', Color3.fromRGB(80, 160, 255)) color(t, p .. 'ABH', 'Ammo Color 2', Color3.fromRGB(255, 70, 70)) dropdown(t, p .. 'ABM', 'Ammo Color Mode', { 'Reactive', 'Solid', 'Gradient' }, 'Reactive') end, }, }) end) end scope('E', 'Enemy Extras', 'L') scope('T', 'Teammate Extras', 'R') safeGroup(tab, 'L', 'ESP Style', function(box) sections(box, 'KPSTY', { { 'Layout', function(t) dropdown( t, 'KPEBF', 'Box Fit', { 'Dynamic', 'Static' }, 'Dynamic', 'Dynamic follows the hitboxes, Static is a fixed ratio.' ) slider(t, 'KPEMD', 'Max Distance', 1000, 50, 1500, 0, ' studs') end, }, { 'Flags', function(t) dropdown( t, 'KPG1', 'Flag Font', { 'Same As Text', 'UI', 'System', 'Plex', 'Monospace' }, 'Same As Text', 'Font of the rank / win streak / deflecting text.' ) slider(t, 'KPG2', 'Flag Size', 0, 0, 32, 0, ' px', '0 keeps the original size.') dropdown(t, 'KPG3', 'Flag Case', { 'Normal', 'UPPER', 'lower' }, 'Normal') dropdown(t, 'KPG4', 'Flag Surround', { 'None', '[ ]', '( )', '< >', '- -', '* *' }, 'None') end, }, { 'Health', function(t) dropdown( t, 'KPH0', 'Color Mode', { 'Reactive', 'Solid', 'Gradient' }, 'Reactive', 'Reactive is the original behaviour.' ) color(t, 'KPH1', 'Solid Color', Color3.fromRGB(60, 255, 60)) color(t, 'KPH2', 'Gradient Low', Color3.fromRGB(255, 60, 60)) color(t, 'KPH3', 'Gradient High', Color3.fromRGB(60, 255, 60)) end, }, }) end) end -- ---------------------------------------------------------------- Crosshair tab extras.BuildVisualsUi = function(tab) safeGroup(tab, 'R', 'Saved Images', function(box) input(box, 'KPX1', 'Name', '', 'my-crosshair') input(box, 'KPX2', 'Source', '', 'asset id, file path or URL') box:AddButton({ Text = 'Save Image', Func = GuardRivalsCallback('Extras_ImageAdd', function() if extras.Images.Add(Options.KPX1.Value, Options.KPX2.Value) then Options.KPX1:SetValue('') Options.KPX2:SetValue('') extras.Images.Refresh() Library:Notify('Saved image.') else Library:Notify('Use a unique name and a source.') end end), }) box:AddDropdown( 'KPX3', { Values = extras.Images.Names(), Multi = false, Text = 'Saved Images', Searchable = true, AllowNull = true } ) box:AddButton({ Text = 'Use Selected As Crosshair', Func = GuardRivalsCallback('Extras_ImageUse', extras.Images.UseSelected), }) box:AddButton({ Text = 'Remove Selected', Func = GuardRivalsCallback('Extras_ImageRemove', function() local option = Options.KPX3 if option and option.Value and extras.Images.Remove(option.Value) then option:SetValue(nil) extras.Images.Refresh() Library:Notify('Removed image.') else Library:Notify('Select a saved image first.') end end), }) end) end -- ---------------------------------------------------------------- Misc tab (spoofer extras) extras.BuildMiscUi = function(tab) extras.MiscTab = tab local function nameTab(t, label, ids) toggle(t, ids[1], label .. ': Name', false, 'Rewrites this name on your screen only.') input(t, ids[2], label .. ' Name Value', 'Nosniy') toggle(t, ids[3], label .. ': Display Name', false) input(t, ids[4], label .. ' Display Value', 'Nosniy') toggle(t, ids[5], label .. ': Avatar', false, 'Swaps thumbnails of the matching user id for this user id.') input(t, ids[6], label .. ' Avatar User ID', '20349956') end safeGroup(tab, 'L', 'Spoofer: Names & Avatar', function(box) box:AddLabel('Rewrites text and avatar thumbnails on your screen only.', true) sections(box, 'KPNSEC', { { 'You', function(t) nameTab(t, 'You', { 'KPN1', 'KPN2', 'KPN3', 'KPN4', 'KPN9', 'KPNA' }) end }, { 'Others', function(t) nameTab(t, 'Others', { 'KPN5', 'KPN6', 'KPN7', 'KPN8', 'KPNB', 'KPNC' }) end }, }) end) safeGroup(tab, 'L', 'Avatar Changer', function(box) input(box, 'KPV1', 'User ID Or Name', '', 'for example 156') local copy = multi( box, 'KPV3', 'Copy', { 'Accessories', 'Clothing', 'Body Colors', 'Head & Face' }, { 'Accessories', 'Clothing', 'Body Colors', 'Head & Face' }, 'Which parts of that avatar to copy.' ) copy:OnChanged(GuardRivalsCallback('Extras_AvatarParts', extras.Avatar.Reapply)) toggle( box, 'KPV2', 'Keep After Respawn', true, 'Dresses every new character again after a respawn or a new round, and puts the look back if the game changes your outfit.' ) toggle( box, 'KPV4', 'Locker Preview', true, 'The character in the Locker follows too. Close and reopen the Locker to see it.' ) box:AddButton({ Text = 'Apply Avatar', Func = GuardRivalsCallback( 'Extras_AvatarApply', function() extras.Avatar.Run(Options.KPV1 and Options.KPV1.Value) end ), }) box:AddButton({ Text = 'Restore My Avatar', Func = GuardRivalsCallback('Extras_AvatarRestore', extras.Avatar.Restore), }) end) local function profileRows(t, rows) for _, row in ipairs(rows) do local control = toggle(t, row[1], row[3], false) local value = input(t, row[2], row[3] .. ' Value', row[4], nil, row[5]) local refresh = GuardRivalsCallback('Extras_DataRefresh', extras.RefreshData) control:OnChanged(refresh) value:OnChanged(refresh) end end safeGroup(tab, 'R', 'Spoofer: Own Profile', function(box) box:AddLabel( 'Shop numbers come through the game player-data proxy; the profile page numbers are patched in the answer the server sends. Reopen your profile or shop to see them.', true ) sections(box, 'KPSSEC', { { 'Economy', function(t) profileRows(t, { { 'KPS1', 'KPS2', 'Event Currency', '9999' }, { 'KPS3', 'KPS4', 'Weapon Keys', '999' }, { 'KPS5', 'KPS6', 'Level', '999' }, }) end, }, { 'Record', function(t) profileRows(t, { { 'KPS7', 'KPS8', 'Casual Wins', '99999' }, { 'KPS9', 'KPSA', 'Ranked Wins', '9999' }, { 'KPSB', 'KPSC', 'Casual Win %', '100', 'Percent, 0 to 100.' }, { 'KPSD', 'KPSE', 'Ranked Win %', '100', 'Percent, 0 to 100.' }, { 'KPSF', 'KPSG', 'Ranked ELO', '3600', 'The rank name and icon on the profile follow this number.', }, { 'KPSH', 'KPSI', 'Favorite Map', 'Arena', 'A map name spelled like the game does: Arena, Crossroads, Dimension, Gate, Onyx, Station, Splash, Docks...', }, }) end, }, }) end) end -- ---------------------------------------------------------------- UI Settings tab (config transfer) extras.BuildConfigUi = function(tab) tab = tab or extras.MiscTab if not tab then return end safeGroup(tab, 'R', 'Config Transfer', function(box) box:AddButton({ Text = 'Export Config To Clipboard', Func = GuardRivalsCallback('Extras_Export', extras.ExportConfig), }) input(box, 'KPZ1', 'Paste Config', '', 'paste an exported config here') box:AddButton({ Text = 'Import Pasted Config', Func = GuardRivalsCallback( 'Extras_Import', function() extras.ImportConfig(Options.KPZ1 and Options.KPZ1.Value) end ), }) end) end -- The longest original groups start collapsed so a tab is easy to scan; the open/closed state is saved with -- the config like any other group, so a saved config keeps whatever you left open. local LONG_GROUPS = { ['Lightning'] = true, ['Weather'] = true, ['Lighting'] = true, ['Shadows & Environment'] = true, ['Custom Crosshair'] = true, ['Crosshair Text'] = true, ['Crosshair Animation'] = true, ['Camera View'] = true, ['Bullet Tracers'] = true, ['Movement Recorder'] = true, ['Auto Loadout & Ban'] = true, -- the new extras: one short header each until opened ['Silent Aim Rules'] = true, ['Enemy Extras'] = true, ['Teammate Extras'] = true, ['ESP Style'] = true, ['Spoofer: Names & Avatar'] = true, ['Spoofer: Own Profile'] = true, } extras.CollapseLongGroups = function() for _, tab in pairs(Library.Tabs or {}) do for title, groupbox in pairs(tab.Groupboxes or {}) do if LONG_GROUPS[title] and type(groupbox.SetCollapsed) == 'function' then pcall(groupbox.SetCollapsed, groupbox, true) end end end end end, function(err) RivalsRuntimeBridge.Extras.UiError = tostring(err) task.defer(function() pcall(ReportRivalsRuntimeIssue, 'extras_ui_load', tostring(err)) end) return err end) -- ====================================================================== -- ESP text style extras -- ====================================================================== RivalsRuntimeBridge.EspExtras = { Fonts = { UI = 0, System = 1, Plex = 2, Monospace = 3 }, Surrounds = { ['[ ]'] = { '[', ']' }, ['( )'] = { '(', ')' }, ['< >'] = { '<', '>' }, ['- -'] = { '- ', ' -' }, ['* *'] = { '*', '*' }, }, } function RivalsRuntimeBridge.EspExtras.Read() local extras = RivalsRuntimeBridge.EspExtras local fontOption = Options and Options.PX9D1 local caseOption = Options and Options.PX9D2 local surroundOption = Options and Options.PX9D3 local sizeOption = Options and Options.PX9S1 local referenceOption = Options and Options.PX9S2 return { UseDisplayName = Toggles and Toggles.PX9T1 and Toggles.PX9T1.Value == true, Font = extras.Fonts[fontOption and fontOption.Value or 'Monospace'] or 3, FontSize = sizeOption and tonumber(sizeOption.Value) or 16, Case = caseOption and caseOption.Value or 'Normal', Surround = surroundOption and surroundOption.Value or 'None', DistanceScaling = Toggles and Toggles.PX9T2 and Toggles.PX9T2.Value == true, ReferenceDistance = referenceOption and tonumber(referenceOption.Value) or 60, MarkCheaters = Toggles and Toggles.PX10T3 and Toggles.PX10T3.Value == true, } end function RivalsRuntimeBridge.EspExtras.ApplyCase(text, mode) if mode == 'UPPER' then return string.upper(text) elseif mode == 'lower' then return string.lower(text) end return text end function RivalsRuntimeBridge.EspExtras.FormatName(player, extras) local name = player.Name if extras.UseDisplayName and player.DisplayName ~= '' then name = player.DisplayName end name = RivalsRuntimeBridge.EspExtras.ApplyCase(name, extras.Case) local pair = RivalsRuntimeBridge.EspExtras.Surrounds[extras.Surround] if pair then name = pair[1] .. name .. pair[2] end local detector = RivalsRuntimeBridge.CheaterDetector if extras.MarkCheaters and detector and detector.IsFlagged(player) then name = name .. ' [CHEATER?]' end return name end function RivalsRuntimeBridge.EspExtras.TextSize(extras, uiScale, distance) local size = extras.FontSize * uiScale if extras.DistanceScaling and type(distance) == 'number' and distance > 0 then size = size * math.clamp(extras.ReferenceDistance / distance, 0.55, 1.6) end return math.max(math.round(size), 8) end local function UpdateESP(deltaTime) RivalsRuntimeBridge.Extras.EspStep(deltaTime) local settings = EspRenderSettings local espEnabled = settings.Enabled if not espEnabled then RivalsRuntimeBridge.ESPUpdateAccumulator = 0 RivalsRuntimeBridge.NextESPReconcileAt = 0 DestroyAllESP() return end RivalsRuntimeBridge.ESPUpdateAccumulator = (RivalsRuntimeBridge.ESPUpdateAccumulator or 0) + (type(deltaTime) == 'number' and deltaTime or (1 / 60)) if RivalsRuntimeBridge.ESPUpdateAccumulator < (1 / 60) then return end RivalsRuntimeBridge.ESPUpdateAccumulator = 0 local now = tick() if now >= (RivalsRuntimeBridge.NextESPReconcileAt or 0) then RivalsRuntimeBridge.NextESPReconcileAt = now + 0.25 ReconcileESPPlayers() end local espColor = settings.VisibleColor local hiddenColor = settings.HiddenColor local maxDist = DEFAULT_ESP_MAX_DISTANCE local fillAlpha = settings.FillAlpha local outlineAlpha = settings.OutlineAlpha local showHighlight = settings.ShowHighlight local showName = settings.ShowName local showHealth = settings.ShowHealth local showArrows = settings.ShowArrows local showDistance = settings.ShowDistance local showWeapon = settings.ShowWeapon local showLookDir = settings.ShowLookDir local showTeammates = settings.ShowTeammates local showBox = settings.ShowBox local showAmmoText = settings.ShowAmmoText local showStatus = settings.ShowRank or settings.ShowWinStreak or settings.ShowDeflecting local teammateColor = espColor local lookDirColor = settings.LookDirColor local rawCardLayout = PLAYER_CARD_THEME local myTeamId = showTeammates and LP:GetAttribute('TeamID') or nil local myRobloxTeam = showTeammates and LP.Team or nil local cam = Workspace.CurrentCamera if not cam then return end local viewportSize = cam.ViewportSize local vpX, vpY = viewportSize.X, viewportSize.Y local centerX, centerY = vpX / 2, vpY / 2 local uiScale = (vpY > 0 and vpY or 1080) / 1080 local paneGap = uiScale local statusGap = uiScale local espExtras = RivalsRuntimeBridge.EspExtras.Read() local textFont = espExtras.Font local cardLayout = { HorizontalBarWidth = rawCardLayout.HorizontalBarWidth * uiScale, NameOffsetY = rawCardLayout.NameOffsetY * uiScale, BarTopGap = rawCardLayout.BarTopGap * uiScale, HorizontalBarOffsetY = rawCardLayout.HorizontalBarOffsetY * uiScale, BarHeight = rawCardLayout.BarHeight * uiScale, HorizontalBarTextPadding = rawCardLayout.HorizontalBarTextPadding * uiScale, HorizontalBarTextPaddingY = rawCardLayout.HorizontalBarTextPaddingY * uiScale, BarRadius = rawCardLayout.BarRadius * uiScale, BarInset = rawCardLayout.BarInset * uiScale, } InitCharacter() local rootPosition = HumanoidRootPart and HumanoidRootPart.Position or nil local maxDistSquared = maxDist * maxDist for player, entry in pairs(ESPObjects) do repeat local character = player.Character if not character or not ShouldShow(player, showTeammates) then HideESP(entry) break end local characterParts = ReadCharacterEspParts(character, entry.characterParts) entry.characterParts = characterParts local hrp = characterParts.HumanoidRootPart if not hrp then HideESP(entry) break end local hum = characterParts.Humanoid local rootPos = hrp.Position local dist = 0 if rootPosition then local rootOffset = rootPos - rootPosition local worldDistanceSquared = rootOffset:Dot(rootOffset) if worldDistanceSquared > maxDistSquared then HideESP(entry) break end dist = math.sqrt(worldDistanceSquared) end local isTeammate = false if showTeammates then local theirTeam = player:GetAttribute('TeamID') if myTeamId and theirTeam then isTeammate = myTeamId == theirTeam elseif player.Team and myRobloxTeam then isTeammate = player.Team == myRobloxTeam end end local screenPos, onScreen = cam:WorldToViewportPoint(rootPos) if not onScreen or screenPos.Z <= 0 then entry.highlight.Enabled = false SetDrawingVisible(entry.nameText, false) HideRoundedRect(entry.healthBarBg) HideRoundedRect(entry.healthBarFill) SetDrawingVisible(entry.healthText, false) SetDrawingVisible(entry.healthSuffixText, false) SetDrawingVisible(entry.metaText, false) SetDrawingVisible(entry.ammoText, false) SetDrawingVisible(entry.distanceText, false) SetDrawingVisible(entry.lookLine, false) if entry.statusText then SetDrawingVisible(entry.statusText, false) end RivalsRuntimeBridge.ESPClassic.HideBox(entry) HideArrowIndicator(entry.arrow) if showArrows then local dir = cam.CFrame:VectorToObjectSpace(rootPos - cam.CFrame.Position) local angle = math.atan2(dir.X, -dir.Z) local edgeDist = math.min(vpX, vpY) * PLAYER_CARD_THEME.ArrowEdgeScale local arrowCenter = Vector2.new(centerX + math.sin(angle) * edgeDist, centerY - math.cos(angle) * edgeDist) local arrowDirection = Vector2.new(math.sin(angle), -math.cos(angle)) local arrowColor = isTeammate and teammateColor or hiddenColor DrawArrowIndicator(entry.arrow, arrowCenter, arrowDirection, arrowColor, uiScale) end break end local _, _, visible = RivalsRuntimeBridge.ResolveVisibleAimbotTargetPart( character, 'Auto', 'Players', ESP_VISIBILITY_CACHE_PROFILE ) local currentColor if isTeammate then currentColor = teammateColor else currentColor = visible and espColor or hiddenColor end entry.highlight.Enabled = showHighlight if showHighlight then local outlineColor = BlendColor(currentColor, PLAYER_CARD_THEME.FrameColor, 0.24) entry.highlight.Adornee = character entry.highlight.OutlineColor = outlineColor entry.highlight.FillColor = currentColor entry.highlight.FillTransparency = fillAlpha entry.highlight.OutlineTransparency = outlineAlpha end if RivalsRuntimeBridge.PlayerChams then RivalsRuntimeBridge.PlayerChams.Step(player, character, currentColor, visible) end local depth = screenPos.Z local boxHeight = (5.5 / depth) * vpY * 0.5 local boxY = screenPos.Y - boxHeight / 2 HideArrowIndicator(entry.arrow) local displayName = RivalsRuntimeBridge.EspExtras.FormatName(player, espExtras) local weaponInfo = (showWeapon or showAmmoText) and GetCachedWeaponInfo(player) or nil local hasHealthBar = showHealth and hum ~= nil local useVerticalHealth = hasHealthBar and settings.UseVerticalHealth local useHorizontalHealth = hasHealthBar and not useVerticalHealth local screenBounds = nil if hasHealthBar or showName or showWeapon or showDistance or showBox or showAmmoText or showStatus then screenBounds = GetHitboxScreenBounds(characterParts, character, cam) end local weaponValue = RivalsRuntimeBridge.EspExtras.ApplyCase( BuildPlayerWeaponText(weaponInfo and weaponInfo.label or nil, showWeapon), espExtras.Case ) local distanceValue = BuildPlayerDistanceText(dist, showDistance) local healthValue = useVerticalHealth and BuildVerticalHealthText(hum) or BuildHealthText(hum, useHorizontalHealth) local mainTextSize = RivalsRuntimeBridge.EspExtras.TextSize(espExtras, uiScale, dist) local hasName = showName and displayName ~= '' local hasMeta = weaponValue ~= '' local hasDistance = distanceValue ~= '' local hasHealthText = healthValue ~= '' local ammo = weaponInfo and weaponInfo.ammo or nil local maxAmmo = weaponInfo and weaponInfo.maxAmmo or nil local hasMaxAmmo = type(maxAmmo) == 'number' and maxAmmo > 0 and maxAmmo < math.huge -- the magazine size comes from the item info; without it the plain count is still worth showing local hasAmmoText = showAmmoText and type(ammo) == 'number' and ammo < math.huge local ammoValue = '' if hasAmmoText then ammoValue = hasMaxAmmo and string.format('%s/%s', FormatAmmoValue(ammo), FormatAmmoValue(maxAmmo)) or FormatAmmoValue(ammo) end local ammoColor = PLAYER_CARD_THEME.BarTextColor if hasAmmoText and (ammo <= 0 or (weaponInfo and weaponInfo.isReloading)) then ammoColor = settings.EmptyAmmoColor end SetDrawingTextState( entry.nameText, hasName, displayName, mainTextSize, true, PLAYER_CARD_THEME.BarTextColor, textFont ) SetDrawingTextState( entry.metaText, hasMeta, weaponValue, mainTextSize, true, PLAYER_CARD_THEME.BarTextColor, textFont ) SetDrawingTextState( entry.ammoText, hasAmmoText, ammoValue, mainTextSize, true, ammoColor, textFont ) SetDrawingTextState( entry.distanceText, hasDistance, distanceValue, mainTextSize, true, PLAYER_CARD_THEME.BarTextColor, textFont ) local hasHealthSuffix = useHorizontalHealth and hasHealthText SetDrawingTextState( entry.healthText, hasHealthBar and hasHealthText, healthValue, mainTextSize, not useHorizontalHealth, PLAYER_CARD_THEME.BarTextColor, textFont ) SetDrawingTextState( entry.healthSuffixText, hasHealthBar and hasHealthSuffix, 'HP', mainTextSize, false, PLAYER_CARD_THEME.BarTextColor, textFont ) local nameBounds = hasName and entry.nameText.TextBounds or Vector2.new(0, 0) local metaBounds = hasMeta and entry.metaText.TextBounds or Vector2.new(0, 0) local ammoBounds = hasAmmoText and entry.ammoText.TextBounds or Vector2.new(0, 0) local healthTextBounds = (hasHealthBar and hasHealthText) and entry.healthText.TextBounds or Vector2.new(0, 0) local healthSuffixBounds = (hasHealthBar and hasHealthSuffix) and entry.healthSuffixText.TextBounds or Vector2.new(0, 0) local healthGap = hasHealthSuffix and (2 * uiScale) or 0 local healthCombinedWidth = healthTextBounds.X + healthGap + healthSuffixBounds.X local nameHeight = hasName and math.max(nameBounds.Y, mainTextSize) or 0 local metaHeight = hasMeta and math.max(metaBounds.Y, mainTextSize) or 0 local ammoHeight = hasAmmoText and math.max(ammoBounds.Y, mainTextSize) or 0 local fallbackWidth = boxHeight * 0.55 local renderBounds = screenBounds or { MinX = screenPos.X - (fallbackWidth * 0.5), MaxX = screenPos.X + (fallbackWidth * 0.5), MinY = boxY, MaxY = boxY + boxHeight, Width = fallbackWidth, Height = boxHeight, CenterX = screenPos.X, CenterY = screenPos.Y, } if showBox and not RivalsRuntimeBridge.Extras.SuppressCornerBox(isTeammate) then RivalsRuntimeBridge.ESPClassic.DrawBox(entry, renderBounds, currentColor, uiScale) else RivalsRuntimeBridge.ESPClassic.HideBox(entry) end if useHorizontalHealth then local horizontalBarWidth = hasHealthText and math.max( cardLayout.HorizontalBarWidth, healthCombinedWidth + (cardLayout.HorizontalBarTextPadding * 2) ) or cardLayout.HorizontalBarWidth local horizontalBarHeight = hasHealthText and math.max(cardLayout.BarHeight, healthTextBounds.Y + (cardLayout.HorizontalBarTextPaddingY * 2)) or cardLayout.BarHeight local topClusterHeight = horizontalBarHeight + (hasName and (nameHeight + cardLayout.BarTopGap) or 0) local cursorY = renderBounds.MinY - topClusterHeight - 2 - cardLayout.NameOffsetY if hasName then entry.nameText.Position = Vector2.new(math.round(renderBounds.CenterX), math.round(cursorY)) cursorY = cursorY + nameHeight + cardLayout.BarTopGap end local healthRatio = math.clamp(hum.Health / math.max(hum.MaxHealth, 1), 0, 1) local healthColor = RivalsRuntimeBridge.Extras.HealthColor(healthRatio, GetHealthBarColor(healthRatio)) local barWidth = horizontalBarWidth local barX = renderBounds.CenterX - (barWidth * 0.5) local barY = cursorY + cardLayout.HorizontalBarOffsetY local barInset = cardLayout.BarInset local fillX = barX + barInset local fillY = barY + barInset local fillHeight = math.max(horizontalBarHeight - (barInset * 2), 1) local fillWidth = math.max(healthRatio * math.max(barWidth - (barInset * 2), 0), healthRatio > 0 and 1 or 0) SetRoundedRect( entry.healthBarBg, barX, barY, barWidth, horizontalBarHeight, cardLayout.BarRadius, PLAYER_CARD_THEME.BarBackgroundColor, PLAYER_CARD_THEME.BarBackgroundTransparency ) if fillWidth > 0 then SetRoundedRect( entry.healthBarFill, fillX, fillY, fillWidth, fillHeight, math.max(cardLayout.BarRadius - barInset, 0), healthColor, 1 ) else HideRoundedRect(entry.healthBarFill) end if hasHealthText then local valueX = renderBounds.CenterX - (healthCombinedWidth * 0.5) local suffixX = valueX + healthTextBounds.X + healthGap local textY = barY + (horizontalBarHeight * 0.5) - (healthTextBounds.Y * 0.5) entry.healthText.Center = false entry.healthText.Position = Vector2.new(math.round(valueX), math.round(textY)) entry.healthSuffixText.Center = false entry.healthSuffixText.Position = Vector2.new(math.round(suffixX), math.round(textY)) end else if hasName then entry.nameText.Position = Vector2.new(math.round(renderBounds.CenterX), math.round(renderBounds.MinY - nameHeight - paneGap)) end SetDrawingVisible(entry.healthSuffixText, false) if useVerticalHealth then local healthRatio = math.clamp(hum.Health / math.max(hum.MaxHealth, 1), 0, 1) entry.displayHealthRatio = RivalsRuntimeBridge.ESPClassic.SmoothRatio(entry.displayHealthRatio, healthRatio, deltaTime) local railWidth = math.max(PLAYER_CARD_THEME.Classic.BarWidth * uiScale, 3) local railGap = PLAYER_CARD_THEME.Classic.BarGap * uiScale local railX = renderBounds.MinX - railWidth - railGap local railY = renderBounds.MinY local railHeight = math.max(renderBounds.Height, 1) local barInset = math.max(PLAYER_CARD_THEME.Classic.BarInset * uiScale, 0.75) local fillHeight = math.max( entry.displayHealthRatio * math.max(railHeight - (barInset * 2), 0), entry.displayHealthRatio > 0 and 1 or 0 ) local fillY = railY + railHeight - barInset - fillHeight SetRoundedRect( entry.healthBarBg, railX, railY, railWidth, railHeight, 0, PLAYER_CARD_THEME.BarBackgroundColor, PLAYER_CARD_THEME.BarBackgroundTransparency ) if fillHeight > 0 then SetRoundedRect( entry.healthBarFill, railX + barInset, fillY, math.max(railWidth - (barInset * 2), 1), fillHeight, 0, RivalsRuntimeBridge.Extras.HealthColor( entry.displayHealthRatio, RivalsRuntimeBridge.ESPClassic.GetReactiveBarColor(entry.displayHealthRatio) ), 1 ) else HideRoundedRect(entry.healthBarFill) end if hasHealthText then local labelX = railX - (healthTextBounds.X * 0.5) - paneGap local labelY = math.clamp( fillY - (healthTextBounds.Y * 0.5), railY, math.max(railY + railHeight - healthTextBounds.Y, railY) ) entry.healthText.Center = true entry.healthText.Position = Vector2.new(math.round(labelX), math.round(labelY)) else SetDrawingVisible(entry.healthText, false) end else HideRoundedRect(entry.healthBarBg) HideRoundedRect(entry.healthBarFill) SetDrawingVisible(entry.healthText, false) end end local infoStartY = renderBounds.MaxY + paneGap if hasAmmoText then entry.ammoText.Position = Vector2.new(math.round(renderBounds.CenterX), math.round(infoStartY)) infoStartY = infoStartY + ammoHeight + paneGap end if hasMeta then entry.metaText.Position = Vector2.new(math.round(renderBounds.CenterX), math.round(infoStartY)) infoStartY = infoStartY + metaHeight + paneGap end if hasDistance then entry.distanceText.Position = Vector2.new(math.round(renderBounds.CenterX), math.round(infoStartY)) end local statusValue = RivalsRuntimeBridge.Extras.FlagText( showStatus and RivalsRuntimeBridge.ESPClassic.BuildStatusText(player, settings, entry) or '' ) if statusValue ~= '' then local statusText = RivalsRuntimeBridge.ESPClassic.EnsureStatusText(entry) local statusSize = RivalsRuntimeBridge.Extras.FlagSize(math.max(math.round(12 * uiScale), 8)) SetDrawingTextState( statusText, true, statusValue, statusSize, false, PLAYER_CARD_THEME.BarTextColor, RivalsRuntimeBridge.Extras.FlagFont(textFont) ) statusText.Position = Vector2.new( math.round(renderBounds.MaxX + statusGap + RivalsRuntimeBridge.Extras.StatusXOffset(isTeammate)), math.round(renderBounds.MinY) ) elseif entry.statusText then SetDrawingVisible(entry.statusText, false) end SetDrawingVisible(entry.lookLine, showLookDir) if showLookDir then local head = characterParts.HitboxHead if head then local headPos = head.Position local lookEnd = headPos + head.CFrame.LookVector * 5 local headScreen, headOn = cam:WorldToViewportPoint(headPos) local endScreen, endOn = cam:WorldToViewportPoint(lookEnd) if headOn and endOn and headScreen.Z > 0 then entry.lookLine.From = Vector2.new(headScreen.X, headScreen.Y) entry.lookLine.To = Vector2.new(endScreen.X, endScreen.Y) entry.lookLine.Color = lookDirColor entry.lookLine.Thickness = math.max(uiScale, 1) else SetDrawingVisible(entry.lookLine, false) end else SetDrawingVisible(entry.lookLine, false) end end until true end end RivalsRuntimeBridge.UpdateESP = UpdateESP RivalsRuntimeBridge.CharacterLoadingState = {} RivalsRuntimeBridge.DiagnosticLogState = { LastByMessage = {}, ThrottleSeconds = 2 } RivalsRuntimeBridge.InitialSpawnCooldownComplete = false RivalsRuntimeBridge.MatchBoundaryDetectionInstalled = false -- ====================================================================== -- Local ready state -- ====================================================================== RivalsRuntimeBridge.LocalReadyState = { ShieldSeen = false, LastSeenFighter = nil, ReadyNotified = false, InitialLifeAdoptionOpen = true, } RivalsRuntimeBridge.ResetLocalShieldLatch = function() local state = RivalsRuntimeBridge.LocalReadyState state.ShieldSeen = false state.LastSeenFighter = nil state.ReadyNotified = false state.InitialLifeAdoptionOpen = false end RivalsRuntimeBridge.NotifyLocalReadyOnce = function(source) local state = RivalsRuntimeBridge.LocalReadyState if state.ReadyNotified then return end state.ReadyNotified = true RivalsRuntimeBridge.LogDiagnosticEvent('Ready', 'Loaded -- ready to fight (' .. tostring(source) .. ')') end RivalsRuntimeBridge.IsLocalReadyToFight = function() if not IsAimbotGameReady() then return false end local duel = FighterDataCache.LocalDuel if duel and duel.Seeded and duel.IsInShootingRange == true then RivalsRuntimeBridge.NotifyLocalReadyOnce('shooting range') return true end local state = RivalsRuntimeBridge.LocalReadyState if state.ShieldSeen then return true end local fighter = ResolveLocalFighter() if not fighter then return false end if type(fighter.Data) == 'table' and fighter.Data.IsInShootingRange == true then RivalsRuntimeBridge.NotifyLocalReadyOnce('shooting range (sync)') return true end if state.LastSeenFighter ~= fighter then state.LastSeenFighter = fighter end local entity = fighter.Entity if entity and type(entity.Data) == 'table' and entity.Data.IsInvincible == true then state.ShieldSeen = true RivalsRuntimeBridge.NotifyLocalReadyOnce('spawn shield') return true end if FighterDataCache.Cache[fighter] == true then state.ShieldSeen = true RivalsRuntimeBridge.NotifyLocalReadyOnce('spawn shield (cache)') return true end if state.InitialLifeAdoptionOpen then if type(entity) == 'table' and rawget(entity, '_is_in_world') == true and rawget(fighter, 'EquippedItem') ~= nil then state.InitialLifeAdoptionOpen = false state.ShieldSeen = true RivalsRuntimeBridge.NotifyLocalReadyOnce('mid-life adopt (equipped item)') return true end end return false end RivalsRuntimeBridge.WaitForLocalReady = function(timeoutSec) local deadline = timeoutSec and (tick() + timeoutSec) or nil while not RivalsRuntimeBridge.IsLocalReadyToFight() do if deadline and tick() > deadline then return false end task.wait() end return true end RivalsRuntimeBridge.EnsureMatchBoundaryDetection = function() if RivalsRuntimeBridge.MatchBoundaryDetectionInstalled then return end local controller = ResolveFighterController() if not controller then return end local addedSignal = controller.ObjectAdded if type(addedSignal) ~= 'table' or type(addedSignal.Connect) ~= 'function' then return end local ok, conn = pcall(function() return addedSignal:Connect(function(newFighter) if type(newFighter) ~= 'table' then return end local isLocal = newFighter.IsLocalPlayer == true or rawget(newFighter, 'Player') == LP if not isLocal then return end RivalsRuntimeBridge.InitialSpawnCooldownComplete = false RivalsRuntimeBridge.ResetLocalShieldLatch() RivalsRuntimeBridge.LogDiagnosticEvent('MatchBoundary', 'New local fighter detected; readiness latch reset') if RivalsRuntimeBridge.RunPickupSpawnSetup then RivalsRuntimeBridge.RunPickupSpawnSetup() end if RivalsRuntimeBridge.AutoWeaponMenu then RivalsRuntimeBridge.AutoWeaponMenu.OnRoundStart() end end) end) if ok and conn then if Library and type(Library.GiveSignal) == 'function' then pcall(function() Library:GiveSignal(conn) end) end RivalsRuntimeBridge.MatchBoundaryDetectionInstalled = true if controller.LocalFighter and RivalsRuntimeBridge.RunPickupSpawnSetup then RivalsRuntimeBridge.RunPickupSpawnSetup() end end end RivalsRuntimeBridge.LogDiagnosticEvent = function(scope, message) if not (Toggles.P9S3T1 and Toggles.P9S3T1.Value == true) then return end local now = tick() local key = tostring(scope) .. '|' .. tostring(message) local state = RivalsRuntimeBridge.DiagnosticLogState local last = state.LastByMessage[key] if last and (now - last) < state.ThrottleSeconds then return end state.LastByMessage[key] = now if Library and type(Library.Notify) == 'function' then pcall( function() Library:Notify({ Title = 'Character Loading Check', Description = string.format('[%s] %s', tostring(scope), tostring(message)), Time = 3, }) end ) end end RivalsRuntimeBridge.RecordCharacterLoadingCheckpoint = function(featureName, isReady, isEnabled) local state = RivalsRuntimeBridge.CharacterLoadingState local wasReady = state[featureName] == true state[featureName] = isReady if not isReady or not isEnabled or wasReady then return end if not (Toggles.P9S3T1 and Toggles.P9S3T1.Value == true) then return end if Library and type(Library.Notify) == 'function' then pcall( function() Library:Notify({ Title = 'Character Loading Check', Description = featureName .. ' ready', Time = 3, }) end ) end end RivalsRuntimeBridge.IsReadyToFight = function() if IsZombieEventActive() and IsAimbotCameraReady() then return true end if not IsAimbotCameraReady() then return false end if not IsAimbotGameReady() then return false end local fighter = ResolveLocalFighter() local entity = fighter and fighter.Entity if type(entity) == 'table' and rawget(entity, '_is_in_world') ~= true then return false end RivalsRuntimeBridge.EnsureMatchBoundaryDetection() if not RivalsRuntimeBridge.IsLocalReadyToFight() then return false end return true end RivalsRuntimeBridge.AcquireActiveCharacter = function() if not RivalsRuntimeBridge.IsReadyToFight() then return nil end if LP.Character ~= Char then return nil end if not HumanoidRootPart or HumanoidRootPart.Parent ~= Char then return nil end return Char, HumanoidRootPart, Humanoid end end -- ====================================================================== -- Hub shell (tower v3): event tab, tower kit, features, menus -- ====================================================================== (function() local BuildRivalsEspUi = nil local BuildRivalsAimbotUi = nil -- ============================================================ -- Event tab (Zombie Towers): zombie ESP + zombie aimbot -- ============================================================ local EventZombieState = { ESP = { Enabled = false, ShowBox = true, ShowName = true, ShowHealth = true, ShowDistance = true, MaxDistance = 600, BoxColor = Color3.fromRGB(255, 90, 90), }, Aim = { Enabled = false, FireMethod = 'FireServer', Priority = 'Crosshair', AimPart = 'Auto', FireInterval = 0.08, FOV = 250, ShowFOV = true, AutoReload = true, FirePart = 'PhysicalHitboxHead', Origin = 'Character', }, EspObjects = {}, FovCircle = nil, TargetDot = nil, CurrentAim = nil, CurrentFire = nil, AimKeyPicker = nil, LastFireStatus = 'idle', LastFireAt = 0, StartShootingToken = nil, Utility = nil, UseItemRemote = nil, Running = true, Firing = false, Testing = false, TestStatus = '', StatusLabel = nil, } local function EventZombieResolveModules() local rs = ReplicatedStorage local modules = rs:FindFirstChild('Modules') if modules and not EventZombieState.Utility then local utilityScript = modules:FindFirstChild('Utility') if utilityScript then local ok, m = pcall(require, utilityScript) if ok and type(m) == 'table' then EventZombieState.Utility = m end end end if not EventZombieState.UseItemRemote or not EventZombieState.UseItemRemote.Parent then local remotes = rs:FindFirstChild('Remotes') local repl = remotes and remotes:FindFirstChild('Replication') local f = repl and repl:FindFirstChild('Fighter') local u = f and f:FindFirstChild('UseItem') if u and (u:IsA('RemoteEvent') or u:IsA('UnreliableRemoteEvent')) then EventZombieState.UseItemRemote = u end end if not EventZombieState.StartShootingToken then local el = ResolveEnumLibrary() if type(el) == 'table' and el._enum_builder_complete == true and type(el.ToEnum) == 'function' then local ok, t = pcall(el.ToEnum, el, 'StartShooting') if ok and type(t) == 'string' and #t > 0 then EventZombieState.StartShootingToken = t end end end end local function EventZombieIsDummy(name) return name == 'Target' or name == 'Dummy' or name == 'DPS Dummy' end local function EventZombieModel(enemy) return enemy.Model or (enemy.Entity and rawget(enemy.Entity, 'Model')) end local function EventZombieIsZombie(enemy) local m = EventZombieModel(enemy) if not m or not m.Parent then return false end if EventZombieIsDummy(m.Name) then return false end if m:FindFirstChildOfClass('Humanoid') then return true end if m:FindFirstChild('HitboxHead') then return true end return false end local function EventZombieHealth(model, entity) local hum = model and model:FindFirstChildOfClass('Humanoid') if hum then return hum.Health, hum.MaxHealth end local data = entity and rawget(entity, 'Data') if type(data) == 'table' then local hp, mhp = rawget(data, 'Health'), rawget(data, 'MaxHealth') if type(hp) == 'number' then return hp, mhp or hp end end return nil, nil end local function EventZombieList() local ec = ResolveEnemyController() if not ec then return {} end local out = {} for _, e in ipairs(ec.Objects or {}) do if EventZombieIsZombie(e) then local m = EventZombieModel(e) if m and m.Parent then local hp = EventZombieHealth(m, e.Entity) if hp == nil or hp > 0 then out[#out + 1] = { Model = m, Entity = e.Entity, Enemy = e, HP = hp } end end end end return out end local function EventZombieAimPart(model) if not model then return nil end local towerKit = RivalsRuntimeBridge.TowerKit if towerKit and towerKit.Aim then local smartPart = towerKit.Aim.PartFor(model, towerKit.AimPartFor(model)) if smartPart then return smartPart end end local order if EventZombieState.Aim.AimPart == 'Head' then order = { 'HitboxHead', 'Head', 'HumanoidRootPart' } elseif EventZombieState.Aim.AimPart == 'Body' then order = { 'HitboxBody', 'HumanoidRootPart', 'Head' } else order = { 'HitboxHead', 'HitboxBody', 'Head', 'HumanoidRootPart' } end for _, name in ipairs(order) do local p = model:FindFirstChild(name, true) if p and p:IsA('BasePart') then return p end end if model.PrimaryPart then return model.PrimaryPart end return model:FindFirstChildWhichIsA('BasePart', true) end local EVENT_FIRE_PART_MAP = { HitboxHead = 'PhysicalHitboxHead', HitboxBody = 'PhysicalHitbox', Head = 'PhysicalHitboxHead', HumanoidRootPart = 'PhysicalHitbox', } local function EventZombieFirePart(aimPart) if not aimPart or not aimPart.Parent then return nil end if EventZombieState.Aim.FirePart == 'Auto' then local autoName = EVENT_FIRE_PART_MAP[aimPart.Name] local autoPart = autoName and aimPart.Parent:FindFirstChild(autoName, true) if autoPart and autoPart:IsA('BasePart') then return autoPart end return aimPart end if EventZombieState.Aim.FirePart == 'SameAsAim' then return aimPart end local named = aimPart.Parent:FindFirstChild(EventZombieState.Aim.FirePart, true) if named and named:IsA('BasePart') then return named end local auto = EVENT_FIRE_PART_MAP[aimPart.Name] if auto then local real = aimPart.Parent:FindFirstChild(auto, true) if real and real:IsA('BasePart') then return real end end return aimPart end local function EventZombieCanFire(item, force) if not item then return false, 'no item' end if not force then local reload = tonumber(rawget(item, '_reload_cooldown')) if reload and reload > tick() then return false, 'reloading' end local cooldown = tonumber(rawget(item, '_shoot_cooldown')) if cooldown and cooldown > tick() then return false, 'cooldown' end end local ammo = ReadItemValue(item, 'Ammo') if type(ammo) == 'number' and ammo <= 0 then if EventZombieState.Aim.AutoReload and type(item.StartReloading) == 'function' then pcall(item.StartReloading, item) return false, 'reloading (empty)' end return false, 'no ammo' end return true end local function EventZombieEncodeCF(cf) local util = EventZombieState.Utility if util and type(util.EncodeCFrame) == 'function' then local ok, v = pcall(util.EncodeCFrame, util, cf) if ok and v then return v end end local rx, ry, rz = cf:ToOrientation() return { ['\0'] = cf.X, ['\1'] = cf.Y, ['\2'] = cf.Z, ['\3'] = rx, ['\4'] = ry, ['\5'] = rz } end local function EventZombieShotOrigin() if EventZombieState.Aim.Origin == 'Camera' then local cam = Workspace.CurrentCamera return cam and cam.CFrame.Position or Vector3.zero end local character = LP.Character if character then local head = character:FindFirstChild('Head') if head and head:IsA('BasePart') then return head.Position end local hrp = character:FindFirstChild('HumanoidRootPart') if hrp and hrp:IsA('BasePart') then return hrp.Position end end local cam = Workspace.CurrentCamera return cam and cam.CFrame.Position or Vector3.zero end local function EventZombieFireServerAt(firePart, force) EventZombieResolveModules() if not EventZombieState.UseItemRemote then return false, 'no UseItem remote' end if not EventZombieState.StartShootingToken then return false, 'no StartShooting token' end local fighter = ResolveLocalFighter() local item = fighter and fighter.EquippedItem if not item then return false, 'no item equipped' end local can, reason = EventZombieCanFire(item, force) if not can then return false, reason end local objectId = ReadItemValue(item, 'ObjectID') if not objectId then return false, 'no ObjectID' end local origin = EventZombieShotOrigin() local look = CFrame.lookAt(origin, firePart.Position) local data = { ['\1'] = { ['\0'] = EventZombieEncodeCF(look), ['\1'] = EventZombieEncodeCF(look), ['\2'] = firePart, ['\3'] = EventZombieEncodeCF(firePart.CFrame:ToObjectSpace(CFrame.new(firePart.Position))), }, ['\2'] = true, } local ok, err = pcall( function() EventZombieState.UseItemRemote:FireServer(objectId, EventZombieState.StartShootingToken, data, nil) end ) if not ok then return false, tostring(err) end return true end local function EventZombieHookAt(firePart) local fighter = ResolveLocalFighter() local item = fighter and fighter.EquippedItem if not item then return false, 'no item' end if type(item.StartShooting) ~= 'function' then return false, 'no StartShooting on item' end local can, reason = EventZombieCanFire(item) if not can then return false, reason end if not firePart or not firePart.Parent then return false, 'no target part' end local camera = Workspace.CurrentCamera if not camera then return false, 'no camera' end local savedCFrame = camera.CFrame -- Block the base silent-shot wrapper for the duration of this call so it -- can't re-aim at a human fighter and cancel our zombie aim. local prevPrimeDepth = nil if AimbotSilentState then prevPrimeDepth = AimbotSilentState.ShotPrimeDepth AimbotSilentState.ShotPrimeDepth = (AimbotSilentState.ShotPrimeDepth or 0) + 1 end camera.CFrame = CFrame.lookAt(savedCFrame.Position, firePart.Position) local ok, err = pcall(item.StartShooting, item) camera.CFrame = savedCFrame if AimbotSilentState and prevPrimeDepth ~= nil then AimbotSilentState.ShotPrimeDepth = prevPrimeDepth end if not ok then return false, tostring(err) end return true end local function EventZombieTryFire(force) if not EventZombieState.CurrentFire or not EventZombieState.CurrentFire.Parent then return false, 'no target' end local method = EventZombieState.Aim.FireMethod if method == 'FireServer' then return EventZombieFireServerAt(EventZombieState.CurrentFire, force) elseif method == 'Hook' then return EventZombieHookAt(EventZombieState.CurrentFire) end return false, 'unknown method' end -- ESP drawing local EventZombieAimCache = setmetatable({}, { __mode = 'k' }) local EventZombieBoundsCache = setmetatable({}, { __mode = 'k' }) local EVENT_AIM_CACHE_TTL = 0.5 local EVENT_BOUNDS_CACHE_TTL = 1.0 local function EventZombieCachedAimPart(model) local entry = EventZombieAimCache[model] if entry and (os.clock() - entry.At) < EVENT_AIM_CACHE_TTL and entry.Part.Parent then return entry.Part end local part = EventZombieAimPart(model) if part then EventZombieAimCache[model] = { Part = part, At = os.clock() } end return part end local function EventZombieCachedBounds(model) local entry = EventZombieBoundsCache[model] if entry and (os.clock() - entry.At) < EVENT_BOUNDS_CACHE_TTL then return entry end local ok, cf, size = pcall(model.GetBoundingBox, model) if not ok or not size then return nil end local pivot = model:GetPivot() local offset = pivot:PointToObjectSpace(cf.Position) local corners = {} for _, x in ipairs({ -0.5, 0.5 }) do for _, y in ipairs({ -0.5, 0.5 }) do for _, z in ipairs({ -0.5, 0.5 }) do corners[#corners + 1] = offset + Vector3.new(x * size.X, y * size.Y, z * size.Z) end end end entry = { Corners = corners, At = os.clock() } EventZombieBoundsCache[model] = entry return entry end -- ESP drawing local function EventZombieGetEsp(model) local e = EventZombieState.EspObjects[model] if e then return e end e = { Box = Drawing.new('Square'), Name = Drawing.new('Text'), Health = Drawing.new('Text'), Distance = Drawing.new('Text'), } e.Box.Thickness = 1 e.Box.Filled = false e.Box.Transparency = 1 for _, k in ipairs({ 'Name', 'Health', 'Distance' }) do e[k].Center = true e[k].Outline = true e[k].Font = 2 e[k].Color = Color3.fromRGB(255, 255, 255) e[k].Visible = false end e.Name.Size = 14 e.Health.Size = 12 e.Distance.Size = 12 EventZombieState.EspObjects[model] = e return e end local function EventZombieHideEsp(e) if not e then return end for _, k in ipairs({ 'Box', 'Name', 'Health', 'Distance' }) do if e[k] then e[k].Visible = false end end end local function EventZombieDrawEsp(zombie, camera, rootPos) local model = zombie.Model if not model or not model.Parent then EventZombieHideEsp(EventZombieState.EspObjects[model]) return end local aimPart = EventZombieCachedAimPart(model) if not aimPart then EventZombieHideEsp(EventZombieState.EspObjects[model]) return end local dist = rootPos and (aimPart.Position - rootPos).Magnitude or 0 if dist > EventZombieState.ESP.MaxDistance then EventZombieHideEsp(EventZombieState.EspObjects[model]) return end local bounds = EventZombieCachedBounds(model) if not bounds then EventZombieHideEsp(EventZombieState.EspObjects[model]) return end local pivot = model:GetPivot() local minX, maxX, minY, maxY = math.huge, -math.huge, math.huge, -math.huge local proj = 0 for _, localCorner in ipairs(bounds.Corners) do local world = pivot:PointToWorldSpace(localCorner) local sp, on = camera:WorldToViewportPoint(world) if on and sp.Z > 0 then proj = proj + 1 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 if proj < 2 then EventZombieHideEsp(EventZombieState.EspObjects[model]) return end local e = EventZombieGetEsp(model) e.Box.Position = Vector2.new(minX, minY) e.Box.Size = Vector2.new(maxX - minX, maxY - minY) e.Box.Color = EventZombieState.ESP.BoxColor e.Box.Visible = EventZombieState.ESP.ShowBox e.Name.Position = Vector2.new((minX + maxX) / 2, minY - 16) e.Name.Text = model.Name e.Name.Visible = EventZombieState.ESP.ShowName local hp, mhp = EventZombieHealth(model, zombie.Entity) if hp and EventZombieState.ESP.ShowHealth then e.Health.Text = mhp and string.format('%d/%d', math.floor(hp), math.floor(mhp)) or string.format('%d HP', math.floor(hp)) e.Health.Position = Vector2.new((minX + maxX) / 2, maxY + 2) e.Health.Visible = true else e.Health.Visible = false end e.Distance.Text = string.format('%dm', math.floor(dist)) e.Distance.Position = Vector2.new((minX + maxX) / 2, maxY + 16) e.Distance.Visible = EventZombieState.ESP.ShowDistance end local function EventZombieClearEsp() for m, e in pairs(EventZombieState.EspObjects) do for _, k in ipairs({ 'Box', 'Name', 'Health', 'Distance' }) do if e[k] then pcall(function() e[k]:Remove() end) end end EventZombieState.EspObjects[m] = nil end end -- Target selection local function EventZombieBestZombie() local towerKit = RivalsRuntimeBridge.TowerKit if towerKit and towerKit.Aim then local towerCam = Workspace.CurrentCamera if not towerCam then return nil end return towerKit.Aim.PickLegacy(towerCam, EventZombieState.Aim.FOV) end local cam = Workspace.CurrentCamera if not cam then return nil end local center = Vector2.new(cam.ViewportSize.X / 2, cam.ViewportSize.Y / 2) local root = LP.Character and LP.Character:FindFirstChild('HumanoidRootPart') local rootPos = root and root.Position local best, bestScore = nil, math.huge for _, z in ipairs(EventZombieList()) do local part = EventZombieCachedAimPart(z.Model) if part then local sp, on = cam:WorldToViewportPoint(part.Position) if on and sp.Z > 0 then local screenDist = (Vector2.new(sp.X, sp.Y) - center).Magnitude if screenDist <= EventZombieState.Aim.FOV then local score if EventZombieState.Aim.Priority == 'Closest' then score = rootPos and (part.Position - rootPos).Magnitude or screenDist elseif EventZombieState.Aim.Priority == 'LowestHP' then score = (z.HP or 100) + screenDist / 10000 else score = screenDist end if score < bestScore then bestScore, best = score, z end end end end end return best end local function EventZombieShouldFire() if not EventZombieState.Aim.Enabled then return false end local botKit = RivalsRuntimeBridge.TowerKit if botKit and botKit.Bot and botKit.Bot.Driving() then return false end local kp = EventZombieState.AimKeyPicker if kp and type(kp.GetState) == 'function' then return kp:GetState() == true end return false end -- Per-frame visuals local function EventZombieStep(deltaTime) if not EventZombieState.Running then return end local cam = Workspace.CurrentCamera if not cam then return end local root = LP.Character and LP.Character:FindFirstChild('HumanoidRootPart') local rootPos = root and root.Position if not EventZombieState.FovCircle then local c = Drawing.new('Circle') c.Filled = false c.NumSides = 48 c.Thickness = 1.5 c.Transparency = 0.55 c.Color = Color3.fromRGB(200, 200, 200) EventZombieState.FovCircle = c end if not EventZombieState.TargetDot then local d = Drawing.new('Circle') d.Filled = true d.NumSides = 20 d.Radius = 5 d.Color = Color3.fromRGB(255, 60, 60) EventZombieState.TargetDot = d end if EventZombieState.ESP.Enabled then local alive = {} for _, z in ipairs(EventZombieList()) do alive[z.Model] = true pcall(EventZombieDrawEsp, z, cam, rootPos) end for m, e in pairs(EventZombieState.EspObjects) do if not alive[m] then EventZombieHideEsp(e) end end else for _, e in pairs(EventZombieState.EspObjects) do EventZombieHideEsp(e) end end if not EventZombieState.Testing then EventZombieState._TargetAccum = (EventZombieState._TargetAccum or 0) + (deltaTime or 0) if EventZombieState._TargetAccum >= 0.05 then EventZombieState._TargetAccum = 0 local best = EventZombieBestZombie() if best then local aimPart = EventZombieCachedAimPart(best.Model) EventZombieState.CurrentAim = aimPart EventZombieState.CurrentFire = aimPart and EventZombieFirePart(aimPart) or nil else EventZombieState.CurrentAim, EventZombieState.CurrentFire = nil, nil end end end if EventZombieState.Aim.ShowFOV and EventZombieState.Aim.Enabled then EventZombieState.FovCircle.Position = Vector2.new(cam.ViewportSize.X / 2, cam.ViewportSize.Y / 2) EventZombieState.FovCircle.Radius = EventZombieState.Aim.FOV EventZombieState.FovCircle.Visible = true else EventZombieState.FovCircle.Visible = false end if EventZombieState.CurrentAim and EventZombieState.Aim.Enabled then local sp, on = cam:WorldToViewportPoint(EventZombieState.CurrentAim.Position) if on and sp.Z > 0 then EventZombieState.TargetDot.Position = Vector2.new(sp.X, sp.Y) EventZombieState.TargetDot.Visible = true else EventZombieState.TargetDot.Visible = false end else EventZombieState.TargetDot.Visible = false end end local function EventZombieFireLoop() while EventZombieState.Running do task.wait(EventZombieState.Aim.FireInterval) if not EventZombieState.Running then break end if EventZombieState.Firing or EventZombieState.Testing then continue end if not (EventZombieShouldFire() and EventZombieState.CurrentFire and EventZombieState.CurrentFire.Parent) then continue end EventZombieState.Firing = true local ok, err = EventZombieTryFire() EventZombieState.Firing = false EventZombieState.LastFireAt = tick() if ok then EventZombieState.LastFireStatus = 'fired via ' .. EventZombieState.Aim.FireMethod else EventZombieState.LastFireStatus = 'fail: ' .. tostring(err) end end end -- Diagnostics helpers local function EventZombieDumpStructure() local list = EventZombieList() if #list == 0 then print('[Event] no zombies') return end local z = list[1] print('[Event] ' .. z.Model:GetFullName() .. ' HP ' .. tostring(z.HP)) for _, d in ipairs(z.Model:GetDescendants()) do if d:IsA('BasePart') then print(' BasePart: ' .. d.Name) elseif d:IsA('Humanoid') then print(' Humanoid: ' .. d.Name .. ' HP ' .. d.Health) end end end local function EventZombieDumpEnum() local modules = ReplicatedStorage:FindFirstChild('Modules') local em = modules and modules:FindFirstChild('EnumLibrary') if not em then print('[Event] no EnumLibrary') return end local ok, el = pcall(require, em) if not ok or type(el) ~= 'table' then print('[Event] EnumLibrary unusable') return end print('[Event] EnumLibrary keys:') local top = {} for k, v in pairs(el) do if type(k) == 'string' and (k:lower():find('shoot') or k:lower():find('enum')) then table.insert(top, k .. ' = ' .. tostring(v)) end end table.sort(top) for _, line in ipairs(top) do print(' ' .. line) end local fromEnum = rawget(el, '_from_enum') if type(fromEnum) == 'table' then print('[Event] _from_enum entries containing Shoot:') for k, v in pairs(fromEnum) do if tostring(v):lower():find('shoot') then print(' ' .. tostring(k) .. ' -> ' .. tostring(v)) end end end end local function EventZombieTestDamage() if not ResolveEnemyController() then return 'no EnemyController' end local list = EventZombieList() if #list == 0 then return 'no zombies alive' end local cam = Workspace.CurrentCamera if not cam then return 'no camera' end local best, bestScore = nil, math.huge local center = Vector2.new(cam.ViewportSize.X / 2, cam.ViewportSize.Y / 2) for _, z in ipairs(list) do local aim = EventZombieAimPart(z.Model) if aim then local sp, on = cam:WorldToViewportPoint(aim.Position) if on and sp.Z > 0 then local d = (Vector2.new(sp.X, sp.Y) - center).Magnitude if d < bestScore then bestScore, best = d, z end end end end best = best or list[1] if not best then return 'no in-FOV zombie' end local aimPart = EventZombieAimPart(best.Model) if not aimPart then return 'no aim part' end local firePart = EventZombieFirePart(aimPart) local fighter = ResolveLocalFighter() local item = fighter and fighter.EquippedItem if not item then return 'no weapon equipped' end local hpBefore = EventZombieHealth(best.Model, best.Entity) local ammoBefore = tonumber(ReadItemValue(item, 'Ammo')) local savedAim, savedFire = EventZombieState.CurrentAim, EventZombieState.CurrentFire local savedEnabled = EventZombieState.Aim.Enabled EventZombieState.CurrentAim, EventZombieState.CurrentFire = aimPart, firePart EventZombieState.Aim.Enabled = true EventZombieState.Testing = true local ok, err = EventZombieTryFire(true) task.wait(0.3) EventZombieState.Testing = false EventZombieState.Aim.Enabled = savedEnabled EventZombieState.CurrentAim, EventZombieState.CurrentFire = savedAim, savedFire local hpAfter = EventZombieHealth(best.Model, best.Entity) local ammoAfter = tonumber(ReadItemValue(item, 'Ammo')) local dmg = (hpBefore and hpAfter) and (hpBefore - hpAfter) or nil local used = (ammoBefore and ammoAfter) and (ammoBefore - ammoAfter) or nil if not ok then return 'SEND FAIL: ' .. tostring(err) elseif dmg and dmg > 0 then return string.format( 'HIT -%d HP via %s, ammo %d to %d', math.floor(dmg + 0.5), EventZombieState.Aim.FireMethod, ammoBefore or -1, ammoAfter or -1 ) elseif used and used > 0 then return string.format('PACKET ACCEPTED NO DAMAGE via %s, ammo -%d', EventZombieState.Aim.FireMethod, used) elseif hpBefore and hpAfter and hpBefore == hpAfter then return 'SERVER DROPPED PACKET via ' .. EventZombieState.Aim.FireMethod else return 'ambiguous missing HP or ammo data' end end local function EventZombieBuildStatus() local lines = {} lines[#lines + 1] = 'EnemyController: ' .. (ResolveEnemyController() and 'ok' or 'MISSING') lines[#lines + 1] = 'FighterController: ' .. (ResolveFighterController() and 'ok' or 'MISSING') lines[#lines + 1] = 'CameraController: ' .. (RivalsModsState.ResolveCameraController() and 'ok' or 'MISSING') lines[#lines + 1] = 'Utility: ' .. (EventZombieState.Utility and 'ok' or 'MISSING') lines[#lines + 1] = 'UseItem remote:' .. (EventZombieState.UseItemRemote and 'ok' or 'MISSING') lines[#lines + 1] = 'StartShooting: ' .. tostring(EventZombieState.StartShootingToken) lines[#lines + 1] = '' local fighter = ResolveLocalFighter() local item = fighter and fighter.EquippedItem if item then lines[#lines + 1] = 'Weapon:' .. tostring(item.Name) lines[#lines + 1] = 'Ammo: ' .. tostring(ReadItemValue(item, 'Ammo')) else lines[#lines + 1] = 'Weapon:none' end lines[#lines + 1] = 'Zombies: ' .. #EventZombieList() lines[#lines + 1] = '' lines[#lines + 1] = 'Aim Enabled: ' .. tostring(EventZombieState.Aim.Enabled) lines[#lines + 1] = 'Fire Method: ' .. EventZombieState.Aim.FireMethod lines[#lines + 1] = 'Current Target:' .. (EventZombieState.CurrentAim and EventZombieState.CurrentAim:GetFullName() or 'none') lines[#lines + 1] = 'Last Status: ' .. EventZombieState.LastFireStatus if EventZombieState.TestStatus ~= '' then lines[#lines + 1] = '' lines[#lines + 1] = 'Test: ' .. EventZombieState.TestStatus end return table.concat(lines, '\n') end -- ===== Tower kit v2 :: core (mob model, tower data, drawing, alerts, prompts, queue) ===== RivalsRuntimeBridge.TowerKit = (function() local Kit = {} local CollectionService = game:GetService('CollectionService') local ProximityPromptService = game:GetService('ProximityPromptService') local cfg = { Spawns = false, SpawnDist = 450, Triggers = false, Waypoint = false, Hud = false, Alerts = false, AlertNotify = true, Pickup = false, PickupRange = 12, Interact = false, Record = false, QueueMode = 'Zombie Tower (solo)', PickWeapon = 'Handgun', FastMeleeAlways = false, -- mobs that do not take head damage; edited from the Aim Assist group BodyOnly = { ['Helmet Zombie'] = true, ['Charger Zombie'] = true }, Esp = { Enabled = false, Style = 'Corners', Name = true, Health = true, Distance = true, Tracer = false, MaxDistance = 500, OnlySpecial = false, ColorMode = 'Threat', BodyHint = true, TargetMark = true, BlastRing = true, }, Aim = { Mode = 'Smart', Priority = 'Crosshair', ExplosiveGuard = true, ExplosiveSafe = 0, ExplosivesFirst = true, }, Bot = { Enabled = false, DryRun = false, Advance = true, WaveTriggers = true, PauseOnInput = true, EngageRange = 90, RetreatPct = 30, ExplosiveMode = 'Careful', MinHpExplosive = 70, ClearDelay = 3, Spacing = true, SpacingMin = 5.5, StandOff = 7, HuntRange = 25, LowHpAction = 'Stop', StopAtBoss = true, Mode = 'Legit', AutoPick = true, }, Rage = { Position = 'Above', Distance = 2.6, VerticalOffset = 6.0, MaxHop = 60, MinHop = 6, HopCap = 160, HopCooldown = 0.12, LockTolerance = 1.6, SettleTime = 0.1, Noclip = true, TargetOrder = 'Isolated', SmartSpot = true, StuckTime = 1.0, Aura = true, AuraCone = 55, Grenade = true, GrenadeMin = 4, GrenadeSafe = 9, Collect = 'Heal + Ammo', CollectBelow = 95, ReachScale = 1, Kite = true, KiteDistance = 6, CollectRange = 150, FastMelee = true, SpeedBoost = 85, ExposureRadius = 7, FleeMargin = 2, RetreatPct = 0, ExplosivePolicy = 'Snipe', -- the horde (50 mobs, ~4000 hp) cannot be shot down with a 100-round rifle: lives 22 and 33 emptied it from 50-70 studs for 4 kills in 100 s -- and died; life 28 punched its way through with an empty shotgun and lived. Off. HordeShoot = false, -- circle the mob being fought, a hop every OrbitGap s, favouring its back (how a player does it: the attack's hit area never lines up) -- (tried 'a ghost in reach is killed, not dodged' - GhostStand 9: two test matches took 250 damage each, with the dodge on 0 and 0) GhostStand = 0, Orbit = true, OrbitStep = 55, OrbitGap = 0.26, OrbitRadius = 2.4, OrbitVertical = 4.8, OrbitFlyerVertical = 3.6, ExplosiveFlee = true, -- an Explosive Zombie within ExplosiveDanger studs is stepped away from before anything else ExplosiveDanger = 28, SnipeMargin = 4, SnipeRange = 80, MinHpExplosive = 80, WaveDelay = 1.2, Evade = true, EvadeBelow = 100, -- every announced swing is dodged, at any health: measured live, 50 damage by floor 7 instead of 190-300 for the same pace EvadeRange = 12, EvadeLift = 13, Strafe = true, StrafeMin = 3, StepDistance = 28, StepHold = 0.9, HoldEngage = 14, Escape = true, EscapeAt = 6, EscapeDistance = 30, EscapeGap = 0.7, Focus = true, FocusRadius = 11, KillOrder = true, LongReach = 0, -- studs of Knife Extender range the farm asks the hub for (0 = off); the hover height follows it (see R.ApplyLift) LiftMargin = 2.5, MaxLift = 10, -- measured live: hits land up to ~16 studs above the mob and not at 21 (extender 12 -> hover 15.5) TargetLock = true, HordeWait = true, -- after the pinata pops: nothing is attacked until the horde has finished spawning HordeMinWait = 4, HordeSettle = 2.5, HordeWaitMax = 20, HordeWaitDistance = 110, HordeWaitLift = 8, TierWeight = 3000, TierRadius = 36, TierKeep = 10, TierExposure = 6, TierLowHp = 55, FlyerFirstRange = 34, GunDryMax = 8, GunDryBan = 15, FlyerFirstMax = 15, TierRadiusLow = 18, TierShooterRadius = 60, -- hops are cheap now (the farm hovers 15 above and kills from 15): a skeleton is worth a trip across the room ShootersFirst = true, FlyerImminent = 20, -- a flyer this close comes before everything, shooters included TierExtras = 6, GunLock = true, TrojanHeight = 22, NestPatience = 20, LauncherHunt = true, Launcher = true, LauncherMin = 4, LauncherRange = 70, }, } Kit.Cfg = cfg if getgenv and getgenv().__TOWERKIT_EXPOSE then getgenv().__TOWERKIT = Kit end Kit.Running = true Kit.GetMobs = false Kit.StatusLabel = false Kit.ExplosiveSeenAt = -100 Kit.QueueModes = { ['Gargoyle Tower (team)'] = 'ltm_monstertower_gargoyletower', ['Gargoyle Tower (solo)'] = 'ltm_monstertower_gargoyletower_solo', ['Zombie Tower (team)'] = 'ltm_monstertower_zombietower', ['Zombie Tower (solo)'] = 'ltm_monstertower_zombietower_solo', } Kit.QueueModeOrder = { 'Gargoyle Tower (team)', 'Gargoyle Tower (solo)', 'Zombie Tower (team)', 'Zombie Tower (solo)' } Kit.PromptLog = {} local SPAWN_COLOR = Color3.fromRGB(255, 200, 80) local TRIGGER_COLOR = Color3.fromRGB(255, 120, 40) local WAYPOINT_COLOR = Color3.fromRGB(90, 220, 255) local ALERT_COLOR = Color3.fromRGB(255, 70, 70) local HUD_COLOR = Color3.fromRGB(235, 235, 235) local function now() return os.clock() end Kit.Now = now function Kit.MyRoot() local character = LP.Character return character and character:FindFirstChild('HumanoidRootPart') or nil end function Kit.MyHum() local character = LP.Character return character and character:FindFirstChildOfClass('Humanoid') or nil end local function rootPos() local root = Kit.MyRoot() return root and root.Position or nil end local lastNote = {} -- the hub runs with cfg.QuietNotify: everything below stays silent (mob skipped, stall, grenade, horde, nest, weapon pick, preset...); -- only the answer to a button, the stop of the farm and the menu closing are shown local QUIET_ALLOW = { queue = true, playagain = true, botstop = true, ['rage-stop'] = true, menu = true } function Kit.Notify(key, title, text, gap, force) if cfg.QuietNotify and not QUIET_ALLOW[key] then return end if not force and not cfg.AlertNotify then return end local t = now() if lastNote[key] and t - lastNote[key] < (gap or 3) then return end lastNote[key] = t if Library and type(Library.Notify) == 'function' then pcall(Library.Notify, Library, { Title = title, Description = text, Time = 4 }) end end local notify = Kit.Notify -- ----------------------------------------------------------- mob classes -- first match wins, so specific names come before general ones local CLASSES = { { 'grim', { Name = 'Grim Reaper', Tag = '[GRIM] ', Avoid = true, Threat = 4, Color = Color3.fromRGB(255, 40, 40) } }, { 'reaper', { Name = 'Reaper', Tag = '[REAPER] ', Threat = 3, Flyer = true, AlwaysDodge = true, Color = Color3.fromRGB(255, 90, 90), }, }, { 'ghost', { Name = 'Ghost', Tag = '[GHOST] ', Threat = 1, Flyer = true, Color = Color3.fromRGB(190, 220, 255) } }, { 'trojan', { Name = 'Chicken Trojan', Tag = '[PINATA] ', Pinata = true, Threat = 0, Color = Color3.fromRGB(255, 200, 80) }, }, { 'giant acidic', { Name = 'Giant Acidic Gargoyle', Tag = '[BOSS] ', Boss = true, Threat = 3, Color = Color3.fromRGB(255, 70, 200), }, }, { 'giant gargoyle', { Name = 'Giant Gargoyle', Tag = '[BOSS] ', Boss = true, Threat = 3, Color = Color3.fromRGB(255, 70, 200) }, }, { 'giant chombie', { Name = 'Giant Chombie', Tag = '[BOSS] ', Boss = true, Threat = 3, Color = Color3.fromRGB(255, 70, 200) }, }, { 'giant', { Name = 'Giant', Tag = '[BOSS] ', Boss = true, Threat = 3, Color = Color3.fromRGB(255, 70, 200) } }, -- the small gargoyle of the Gargoyle Tower flies at us and strikes 0.03-0.4 s after its packet: the dodge has to be in the same frame { 'gargoyle', { Name = 'Gargoyle', Tag = '[GARGOYLE] ', Threat = 2, Flyer = true, AlwaysDodge = true, Color = Color3.fromRGB(170, 190, 255), }, }, { 'shadow', { Name = 'Shadow', Tag = '[SHADOW] ', Threat = 2, Color = Color3.fromRGB(120, 120, 160) } }, { 'brute', { Name = 'Brute Zombie', Tag = '[BRUTE] ', Threat = 2, Ranged = true, CloseDodge = true, Reach = 14, Color = Color3.fromRGB(200, 120, 80), }, }, { 'explosive', { Name = 'Explosive Zombie', Tag = '[BOOM] ', Explosive = true, Threat = 2, Color = Color3.fromRGB(255, 150, 40), }, }, { 'charger', { Name = 'Charger Zombie', Tag = '[CHARGE] ', Charger = true, Threat = 2, Color = Color3.fromRGB(255, 110, 60) }, }, { 'shocker', { Name = 'Shocker Zombie', Tag = '[ZAP] ', Threat = 2, Ranged = true, Color = Color3.fromRGB(90, 190, 255) }, }, { 'hunter', { Name = 'Hunter Zombie', Tag = '[LEAP] ', Threat = 2, Ranged = true, Color = Color3.fromRGB(255, 220, 60) }, }, { 'flag', { Name = 'Flag Zombie', Tag = '[BUFFER] ', Threat = 2, Color = Color3.fromRGB(255, 255, 120) } }, { 'chombie', { Name = 'Chombie', Tag = '[BRUTE] ', Threat = 2, Color = Color3.fromRGB(190, 120, 255) } }, { 'reanimating', { Name = 'Reanimating Zombie', Tag = '[REVIVES] ', Threat = 1, Color = Color3.fromRGB(120, 255, 160) }, }, { 'stored', { Name = 'Stored Monster', Tag = '[HIDDEN] ', Threat = 1, Color = Color3.fromRGB(160, 160, 255) } }, { 'helmet', { Name = 'Helmet Zombie', Tag = '[ARMOR] ', Threat = 1, Color = Color3.fromRGB(190, 190, 200) } }, -- a skeleton shoots bones from afar (25 each, about 0.6 s after its animation starts): only a clear line can hurt, so landing out of its sight is the dodge { 'skeleton', { Name = 'Skeleton', Tag = '[SKELETON] ', Threat = 2, Ranged = true, Shooter = true, Color = Color3.fromRGB(225, 225, 205), }, }, } Kit.MobNames = { 'Zombie', 'Helmet Zombie', 'Flag Zombie', 'Explosive Zombie', 'Hunter Zombie', 'Charger Zombie', 'Shocker Zombie', 'Reanimating Zombie', 'Skeleton', 'Chombie', 'Giant Chombie', 'Stored Monster', 'Giant Gargoyle', 'Giant Acidic Gargoyle', } local classCache = {} function Kit.Classify(name) name = type(name) == 'string' and name or '' local cached = classCache[name] if cached then return cached end local lowered = name:lower() local result for _, entry in ipairs(CLASSES) do if lowered:find(entry[1], 1, true) then result = entry[2] break end end result = result or { Name = name ~= '' and name or 'Zombie', Tag = '', Threat = 0 } classCache[name] = result return result end -- kill order: 1 flyers (ghost, reaper, gargoyle), 2 skeletons, brutes and the other big zombies, 3 plain zombies. -- Explosive zombies and the pinata stay in the last group: they have their own rules (range, blast, gun) function Kit.KillTier(class) if class.Flyer then return 1 end -- the skeletons throw bones from up to 100 studs for as long as they live (4 hub lives: 275 of the ~1000 damage taken): they go first if class.Shooter and cfg.Rage.ShootersFirst ~= false and not class.Explosive and not class.Pinata then return 1 end if class.Explosive or class.Pinata or class.Avoid then return 3 end if class.Shooter or class.Boss or class.Ranged or class.CloseDodge or class.Charger or (class.Threat or 0) >= 2 then return 2 end return 3 end Kit.TierPicks = { 0, 0, 0 } Kit.TierHeld = 0 function Kit.IsBodyOnly(model) if not model then return false end return cfg.BodyOnly[Kit.Classify(model.Name).Name] == true end -- 'Head' or 'Body' for this mob under the current aim mode function Kit.AimPartFor(model) local mode = cfg.Aim.Mode if mode == 'Head' then return 'Head' elseif mode == 'Body' then return 'Body' end return Kit.IsBodyOnly(model) and 'Body' or 'Head' end function Kit.Health(model) local humanoid = model and model:FindFirstChildOfClass('Humanoid') if humanoid then return humanoid.Health, humanoid.MaxHealth end return nil, nil end -- ---------------------------------------------------------------- mobs local DUMMY = { Target = true, Dummy = true, ['DPS Dummy'] = true } local mobCache = { At = -10, List = {}, ByModel = {} } Kit.PinataModels = {} local motion = setmetatable({}, { __mode = 'k' }) function Kit.Mobs() local t = now() if t - mobCache.At < 0.04 then return mobCache.List end mobCache.At = t local list, byModel = {}, {} local ok, controller = pcall(ResolveEnemyController) if ok and controller then for _, enemy in ipairs(controller.Objects or {}) do local model = enemy.Model or (enemy.Entity and rawget(enemy.Entity, 'Model')) if model and model.Parent and not DUMMY[model.Name] then local humanoid = model:FindFirstChildOfClass('Humanoid') local root = model:FindFirstChild('HumanoidRootPart') or model.PrimaryPart if root and (humanoid or model:FindFirstChild('HitboxHead')) then local hp = humanoid and humanoid.Health or nil if hp == nil or hp > 0 then local pos = root.Position local m = motion[model] local velocity = Vector3.zero if m and t > m.T then local raw = (pos - m.Pos) / (t - m.T) velocity = m.Vel:Lerp(raw, 0.5) end motion[model] = { Pos = pos, T = t, Vel = velocity } local record = { Model = model, Enemy = enemy, Class = Kit.Classify(model.Name), Root = root, Humanoid = humanoid, Look = root.CFrame.LookVector, Pos = pos, Vel = velocity, HP = hp, MaxHP = humanoid and humanoid.MaxHealth or nil, } list[#list + 1] = record byModel[model] = record if record.Class.Explosive then Kit.ExplosiveSeenAt = t end end end end end end -- pinata style entities (no humanoid, health kept in attributes) are not enemies of the controller -- Every hit on the pinata moves its model into workspace.HurtEffect for about a second (the damage flash), so for that -- second it is not in Entities at all. Both places are looked at, and one record per name is kept under a stable -- model reference, otherwise the bot loses the target after every shot (which is what made it swap to the fists -- between shots). local tower0 = Kit.FindTower() local entities0 = tower0 and tower0:FindFirstChild('Entities') local hurt0 = Workspace:FindFirstChild('HurtEffect') local pinataSeen = {} for _, folder in ipairs({ entities0 or false, hurt0 or false }) do for _, model in ipairs(folder and folder:GetChildren() or {}) do if model:IsA('Model') and not byModel[model] and not pinataSeen[model.Name] and model:FindFirstChild('PinataHitbox') then local pinataRoot = model:FindFirstChild('HumanoidRootPart') or model.PrimaryPart local pinataHp = tonumber(model:GetAttribute('Health')) if pinataRoot and (pinataHp == nil or pinataHp > 0) then pinataSeen[model.Name] = true local canon = Kit.PinataModels[model.Name] if not canon or not canon.Parent then canon = model Kit.PinataModels[model.Name] = model end local record = { Model = canon, Enemy = nil, Class = Kit.Classify(model.Name), Root = pinataRoot, Humanoid = nil, Look = pinataRoot.CFrame.LookVector, Pos = pinataRoot.Position, Vel = Vector3.zero, HP = pinataHp, MaxHP = tonumber(model:GetAttribute('MaxHealth')), } list[#list + 1] = record byModel[canon] = record end end end end mobCache.List, mobCache.ByModel = list, byModel return list end function Kit.MobOf(model) Kit.Mobs() return mobCache.ByModel[model] end -- ------------------------------------------------------- blast learning local learn = { Radius = nil, Damage = nil, LastAt = -10, Samples = {}, HookedHum = nil, Conn = nil } Kit.Learn = learn local DEFAULT_BLAST = 12 function Kit.BlastRadius() return learn.Radius or DEFAULT_BLAST end function Kit.ExplosiveSafeDistance() local override = cfg.Aim.ExplosiveSafe if type(override) == 'number' and override > 0 then return override end return Kit.BlastRadius() + 3 end -- ------------------------------------------------------ attack warnings -- An enemy swing announces itself: the server sends the attacker's PlayAnimation packet about 0.4-0.7 s -- before the damage lands. The plain zombie's attack animation is known; the others are learned from the -- hits taken (an animation that precedes two of our hits is an attack). Kit.AttackAnims = { ['rbxassetid://102425113550637'] = true, -- plain / helmet zombie ['rbxassetid://116181844632618'] = true, -- plain zombie, second swing ['rbxassetid://134538543310990'] = true, -- ghost ['rbxassetid://77084924915946'] = true, -- ghost, second attack ['rbxassetid://127358072436601'] = true, -- brute zombie ['rbxassetid://118484538703523'] = true, -- brute zombie, second attack (seen once) ['rbxassetid://83652161573309'] = true, -- skeleton ['rbxassetid://88038605096017'] = true, -- reaper (the small flyer, 50 damage; it used to be learned only after two hits) ['hunter:leap'] = true, -- hunter zombie leap (own packet) ['rbxassetid://116320095247011'] = true, -- hunter zombie claw (five 25-damage hits in 4 s before it was learned) ['rbxassetid://94937005224967'] = true, -- explosive zombie detonation (50 damage twice in 2 s before it was learned) ['rbxassetid://113019234162696'] = true, -- hunter zombie, second attack: four 25-damage hits in 0.6 s on floor 7 (17 sightings, never learned in time) ['rbxassetid://111405960447143'] = true, -- explosive zombie, second animation (seen in two big hits): an explosive zombie does 150 ['rbxassetid://79657408283737'] = true, -- gargoyle (Gargoyle Tower), 25 damage, lands 0.03-0.37 s after the packet (8 sightings, 3 hits) ['rbxassetid://86062401772155'] = true, -- gargoyle, second attack, lands 0.03-0.31 s after (9 sightings, 5 hits) } Kit.Attacks = setmetatable({}, { __mode = 'k' }) Kit.AttackLog = {} Kit.AttackCandidates = {} Kit.Windup = { Min = 0.38, Max = 0.95, List = {}, ById = {} } Kit.Windup.ById['rbxassetid://134538543310990'] = { Min = 0.5, Max = 1.4, List = {} } Kit.Windup.ById['rbxassetid://77084924915946'] = { Min = 0.5, Max = 1.4, List = {} } -- brute: measured live frame by frame, the swing lands 0.59-0.61 s after the announcement and the brute stands still -- for the whole sequence (it swings about every 1.4 s). Slack is the extra time a dodge keeps waiting after Max: the -- generic 0.15 s would keep us out of reach for most of the brute's 1.4 s cycle, 0.05 s lets us step back in the moment -- the swing has landed and punch it until the next one is announced Kit.Windup.ById['rbxassetid://127358072436601'] = { Min = 0.5, Max = 0.68, Slack = 0.05, List = {} } Kit.Windup.ById['rbxassetid://118484538703523'] = { Min = 0.5, Max = 0.68, Slack = 0.05, List = {} } Kit.Windup.ById['rbxassetid://88038605096017'] = { Min = 0.55, Max = 0.85, List = {} } Kit.Windup.ById['rbxassetid://113019234162696'] = { Min = 0.12, Max = 0.55, List = {} } Kit.Windup.ById['rbxassetid://111405960447143'] = { Min = 0.3, Max = 1.0, List = {} } Kit.Windup.ById['hunter:leap'] = { Min = 0.25, Max = 1.0, List = {} } Kit.Windup.ById['rbxassetid://83652161573309'] = { Min = 0.45, Max = 1.3, List = {} } Kit.Windup.ById['rbxassetid://79657408283737'] = { Min = 0.02, Max = 0.5, Slack = 0.05, List = {} } Kit.Windup.ById['rbxassetid://86062401772155'] = { Min = 0.02, Max = 0.5, Slack = 0.05, List = {} } -- the windup range of one attack animation once it has been measured a few times, otherwise the overall one function Kit.WindupFor(id) local entry = Kit.Windup.ById[id] if entry and entry.Min then return entry end return Kit.Windup end Kit.Dodges = 0 function Kit.NoteAnimation(entity, id) if type(id) ~= 'string' or type(entity) ~= 'table' then return end local model = entity.Model local root = Kit.MyRoot() local info = model and Kit.MobOf(model) if not model or not root or model == LP.Character or not info then return end local mobRoot = entity.RootPart or model:FindFirstChild('HumanoidRootPart') if not mobRoot then return end local distance = (mobRoot.Position - root.Position).Magnitude -- a skeleton throws from 28-35 studs (every unannounced hit on floor 12 came from there) if distance > ((info.Class and info.Class.Shooter) and (Kit.ShooterAnnounceRange or 90) or 30) then return end local t = now() Kit.Attacks[model] = { At = t, Id = id, Dist = distance, Known = Kit.AttackAnims[id] == true } local log = Kit.AttackLog log[#log + 1] = { t = t, id = id, dist = distance, model = model, pos = mobRoot.Position } if #log > 60 then table.remove(log, 1) end end -- called for every drop in our health: which announced attacks came right before it? function Kit.LearnFromHit(lost) local t = now() local seen = {} for _, entry in ipairs(Kit.AttackLog) do local age = t - entry.t if age >= 0.2 and age <= (Kit.AttackAnims[entry.id] and 1.5 or 1.0) and entry.dist <= 10 and (not seen[entry.id] or age > seen[entry.id]) then seen[entry.id] = age end end for id, age in pairs(seen) do if Kit.AttackAnims[id] then local own = Kit.Windup.ById[id] if not own then own = { List = {} } Kit.Windup.ById[id] = own end own.List[#own.List + 1] = age if #own.List > 16 then table.remove(own.List, 1) end if #own.List >= 3 then local low, high = math.huge, 0 for _, value in ipairs(own.List) do low, high = math.min(low, value), math.max(high, value) end own.Min = math.clamp(low - 0.04, 0.22, 0.9) own.Max = math.clamp(high + 0.08, 0.5, 1.4) end local list = Kit.Windup.List list[#list + 1] = age if #list > 24 then table.remove(list, 1) end if #list >= 4 then local low, high = math.huge, 0 for _, value in ipairs(list) do low, high = math.min(low, value), math.max(high, value) end Kit.Windup.Min = math.clamp(low - 0.04, 0.22, 0.5) Kit.Windup.Max = math.clamp(high + 0.08, 0.5, 1.1) end else Kit.AttackCandidates[id] = (Kit.AttackCandidates[id] or 0) + 1 if Kit.AttackCandidates[id] >= 2 then Kit.AttackAnims[id] = true Kit.Notify('attack', 'Tower', 'learned an attack animation (' .. tostring(id) .. ')', 2, true) end end end end local function watchDamage() local humanoid = Kit.MyHum() if humanoid == learn.HookedHum then return end if learn.Conn then pcall(function() learn.Conn:Disconnect() end) learn.Conn = nil end learn.HookedHum = humanoid if not humanoid then return end local last = humanoid.Health learn.Conn = humanoid.HealthChanged:Connect(function(health) local delta = health - last last = health if delta < -0.5 then pcall(Kit.LearnFromHit, -delta) end if delta < -0.5 and now() - learn.LastAt < 0.7 then local lost = -delta learn.Damage = math.max(learn.Damage or 0, lost) if #learn.Samples < 20 then local root = Kit.MyRoot() learn.Samples[#learn.Samples + 1] = { Damage = lost, Distance = learn.Pos and root and (learn.Pos - root.Position).Magnitude or nil } end end end) end -- ---------------------------------------------------------------- drawing local draw = { Text = {}, Line = {}, Rect = {}, TextCount = 0, LineCount = 0, RectCount = 0 } Kit.DrawState = draw local function newText() local t = Drawing.new('Text') t.Center = true t.Outline = true t.Font = 2 t.Size = 13 t.Visible = false return t end local function newLine() local l = Drawing.new('Line') l.Thickness = 1.5 l.Transparency = 1 l.Visible = false return l end local function newRect() local r = Drawing.new('Square') r.Thickness = 1 r.Filled = false r.Transparency = 1 r.Visible = false return r end local G = {} Kit.Gfx = G function G.begin() draw.TextCount, draw.LineCount, draw.RectCount = 0, 0, 0 end function G.finish() for i = draw.TextCount + 1, #draw.Text do draw.Text[i].Visible = false end for i = draw.LineCount + 1, #draw.Line do draw.Line[i].Visible = false end for i = draw.RectCount + 1, #draw.Rect do draw.Rect[i].Visible = false end end function G.label(position, text, color, size, left) draw.TextCount += 1 local t = draw.Text[draw.TextCount] if not t then t = newText() draw.Text[draw.TextCount] = t end t.Center = not left t.Text = text t.Position = position t.Color = color or HUD_COLOR t.Size = size or 13 t.Visible = true end function G.seg(a, b, color, thickness) draw.LineCount += 1 local l = draw.Line[draw.LineCount] if not l then l = newLine() draw.Line[draw.LineCount] = l end l.From = a l.To = b l.Color = color l.Thickness = thickness or 1.5 l.Visible = true end function G.rect(position, size, color, filled, thickness, transparency) draw.RectCount += 1 local r = draw.Rect[draw.RectCount] if not r then r = newRect() draw.Rect[draw.RectCount] = r end r.Position = position r.Size = size r.Color = color r.Filled = filled == true r.Thickness = thickness or 1 r.Transparency = transparency or 1 r.Visible = true end function G.project(cam, world) local sp = cam:WorldToViewportPoint(world) if sp.Z <= 0 then return nil end return Vector2.new(sp.X, sp.Y) end function G.segWorld(cam, a, b, color, thickness) local pa, pb = G.project(cam, a), G.project(cam, b) if pa and pb then G.seg(pa, pb, color, thickness) end end function G.ring(cam, center, radius, color, thickness) local steps = 28 local prev for i = 0, steps do local angle = (i / steps) * math.pi * 2 local p = G.project(cam, center + Vector3.new(math.cos(angle) * radius, 0, math.sin(angle) * radius)) if p and prev then G.seg(prev, p, color, thickness) end prev = p end end function G.box(cam, cf, size, color) local hx, hy, hz = size.X / 2, size.Y / 2, size.Z / 2 local c = {} for _, x in ipairs({ -hx, hx }) do for _, y in ipairs({ -hy, hy }) do for _, z in ipairs({ -hz, hz }) do c[#c + 1] = cf:PointToWorldSpace(Vector3.new(x, y, z)) end end end local edges = { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 }, { 1, 3 }, { 2, 4 }, { 5, 7 }, { 6, 8 }, { 1, 5 }, { 2, 6 }, { 3, 7 }, { 4, 8 }, } for _, e in ipairs(edges) do G.segWorld(cam, c[e[1]], c[e[2]], color) end end -- ------------------------------------------------------------ tower data local towerCache = { Model = nil, At = -10 } function Kit.FindTower() local model = towerCache.Model if model and model.Parent then return model end local t = now() if t - towerCache.At < 1 then return nil end towerCache.At = t towerCache.Model = nil for _, child in ipairs(Workspace:GetChildren()) do if (child:IsA('Model') or child:IsA('Folder')) and child.Name:find('Tower') and child:FindFirstChild('Floors') then towerCache.Model = child return child end end return nil end local function withinPart(part, point) local localPoint = part.CFrame:PointToObjectSpace(point) local half = part.Size / 2 return math.abs(localPoint.X) <= half.X and math.abs(localPoint.Y) <= half.Y and math.abs(localPoint.Z) <= half.Z end Kit.WithinPart = withinPart function Kit.FloorAt(tower, point) local floors = tower and tower:FindFirstChild('Floors') if not floors or not point then return nil end local matches = {} for _, floor in ipairs(floors:GetChildren()) do local important = floor:FindFirstChild('Important') local volume = important and important:FindFirstChild('Volume') if volume and volume:IsA('BasePart') and withinPart(volume, point) and tonumber(floor.Name) then matches[#matches + 1] = tonumber(floor.Name) end end if #matches <= 1 then return matches[1] end table.sort(matches) -- the floors above 10 are built into the volume of the one below: the lower floor stays "current" while it has a trigger -- left to fire, after that the highest floor whose entrance we are already above is for _, number in ipairs(matches) do if #Kit.PendingTriggers(tower, number) > 0 then return number end end local best = matches[1] for _, number in ipairs(matches) do local entrance = Kit.Entrance(tower, number) if not entrance or entrance.Position.Y <= point.Y + 4 then best = number end end return best end local function floorOfInstance(inst) local node = inst while node and node.Parent do if node.Parent.Name == 'Floors' then return tonumber(node.Name) end node = node.Parent end return nil end local tagCache = {} local function taggedInTower(tag, tower) local entry = tagCache[tag] local t = now() if entry and t - entry.At < 1 then return entry.List end local list = {} for _, inst in ipairs(CollectionService:GetTagged(tag)) do if inst:IsA('BasePart') and inst.Parent and (not tower or inst:IsDescendantOf(tower)) then list[#list + 1] = { Part = inst, Name = inst:GetAttribute('MobName') or '?', Count = inst:GetAttribute('MobCount') or 1, Floor = floorOfInstance(inst), } end end tagCache[tag] = { At = t, List = list } return list end Kit.Tagged = taggedInTower -- triggers that still exist (the game removes a group's trigger once it fires) function Kit.PendingTriggers(tower, floor) local out = {} for _, s in ipairs(taggedInTower('MobTrigger', tower)) do if s.Part.Parent and s.Part:GetAttribute('Triggered') ~= true and (floor == nil or s.Floor == floor) then out[#out + 1] = s end end return out end function Kit.Entrance(tower, floor) local floors = tower and tower:FindFirstChild('Floors') local node = floors and floors:FindFirstChild(tostring(floor)) local important = node and node:FindFirstChild('Important') local entrance = important and important:FindFirstChild('Entrance') if entrance and entrance:IsA('BasePart') then return entrance end return nil end function Kit.GroundAt(position, maxDown) local params = RaycastParams.new() params.FilterType = Enum.RaycastFilterType.Exclude local ignore = { LP.Character } local tower = Kit.FindTower() local entities = tower and tower:FindFirstChild('Entities') if entities then ignore[#ignore + 1] = entities end params.FilterDescendantsInstances = ignore local hit = Workspace:Raycast(position, Vector3.new(0, -(maxDown or 40), 0), params) return hit and hit.Position or nil end -- ------------------------------------------------------- walls and shots -- The bot teleports with noclip on, so a hop can end inside a wall or a pillar, and from there every shot and grenade goes into -- the wall's interior (ammo and grenades for nothing). SpotFree asks the physics engine whether a body-sized box at a point -- overlaps collidable map geometry (GetPartsInPart is exact for blocks and meshes); LineClear / ArcClear walk a bullet's or a -- shell's path with raycasts. local spotProbe = Instance.new('Part') spotProbe.Anchored = true spotProbe.CanCollide = false spotProbe.CanTouch = false spotProbe.CanQuery = false spotProbe.Transparency = 1 local BODY_BOX = Vector3.new(3, 4.4, 3) local shotParams = RaycastParams.new() shotParams.FilterType = Enum.RaycastFilterType.Exclude shotParams.RespectCanCollide = true local overlapParams = OverlapParams.new() overlapParams.FilterType = Enum.RaycastFilterType.Exclude overlapParams.RespectCanCollide = true overlapParams.MaxParts = 6 local function shotIgnore() local list = { LP.Character } local tower = Kit.FindTower() local entities = tower and tower:FindFirstChild('Entities') if entities then list[#list + 1] = entities end local viewmodels = Workspace:FindFirstChild('ViewModels') if viewmodels then list[#list + 1] = viewmodels end -- a mob that was just hit is parked in HurtEffect for a moment (the damage flash): it is a target, not a wall local hurt = Workspace:FindFirstChild('HurtEffect') if hurt then list[#list + 1] = hurt end return list end function Kit.SpotFree(point, size) overlapParams.FilterDescendantsInstances = shotIgnore() spotProbe.Size = size or BODY_BOX spotProbe.CFrame = CFrame.new(point) local ok, parts = pcall(Workspace.GetPartsInPart, Workspace, spotProbe, overlapParams) return not ok or #parts == 0, ok and parts[1] or nil end local NUDGES = {} for _, radius in ipairs({ 2.5, 4.5, 7, 10 }) do for step = 0, 7 do local angle = step * math.pi / 4 NUDGES[#NUDGES + 1] = Vector3.new(math.cos(angle) * radius, 0, math.sin(angle) * radius) end NUDGES[#NUDGES + 1] = Vector3.new(0, radius, 0) NUDGES[#NUDGES + 1] = Vector3.new(0, -radius, 0) end -- the nearest spot to `point` (same height preferred) where the body box overlaps nothing; nil when there is none within 10 studs function Kit.NearestFree(point, size) if Kit.SpotFree(point, size) then return point end for _, offset in ipairs(NUDGES) do local candidate = point + offset if Kit.SpotFree(candidate, size) then return candidate end end return nil end local function shotCast(origin, vector) shotParams.FilterDescendantsInstances = shotIgnore() local ignore = shotParams.FilterDescendantsInstances -- a weapon crate or other drop is not a wall: look through it for _ = 1, 3 do local hit = Workspace:Raycast(origin, vector, shotParams) if hit and (hit.Instance.Name == '_drop' or hit.Instance:FindFirstAncestor('_drop')) then ignore[#ignore + 1] = hit.Instance shotParams.FilterDescendantsInstances = ignore else return hit end end return nil end -- is the straight line from origin to target free of walls (up to `slack` studs short of the target)? function Kit.LineClear(origin, target, slack) local vector = target - origin local hit = shotCast(origin, vector) return hit == nil or (hit.Position - origin).Magnitude >= vector.Magnitude - (slack or 2.5), hit end -- the path of a shell: launched from origin along direction at speed, falling at `gravity` studs/s^2, until it has covered -- `flatDistance` studs horizontally. Clear when nothing solid is hit before it gets within `slack` studs of the end. function Kit.ArcClear(origin, direction, speed, gravity, flatDistance, slack) local flatSpeed = math.sqrt(direction.X * direction.X + direction.Z * direction.Z) * speed if flatSpeed < 1e-3 then return true end local total = flatDistance / flatSpeed local steps = 12 local previous = origin local travelled = 0 for k = 1, steps do local t = total * k / steps local point = origin + direction * (speed * t) + Vector3.new(0, -0.5 * gravity * t * t, 0) local vector = point - previous local hit = shotCast(previous, vector) if hit then local remaining = total * (1 - (k - 1) / steps) * flatSpeed - (hit.Position - previous).Magnitude return remaining <= (slack or 4), hit end travelled += vector.Magnitude previous = point end return true end -- ------------------------------------------------------------ skeleton bones -- A skeleton's throw is a model named 'Skeleton' directly under Workspace (not in the tower's Entities folder): a 1x1x1 Hitbox -- (CanTouch) and a bone mesh, flying in a straight line at about 15 studs a second for ten seconds. Slow, visible and -- predictable: where it will pass is known long before it gets there. local boneSeen = setmetatable({}, { __mode = 'k' }) function Kit.Bones() local list = {} local t = now() for _, inst in ipairs(Workspace:GetChildren()) do if inst.Name == 'Skeleton' and (inst:IsA('Model') or inst:IsA('BasePart')) then local hitbox = inst:IsA('BasePart') and inst or inst:FindFirstChild('Hitbox') if hitbox and hitbox:IsA('BasePart') then local pos = hitbox.Position local vel = hitbox.AssemblyLinearVelocity local seen = boneSeen[inst] -- not moving by physics (or not yet): the velocity between two looks if vel.Magnitude < 2 and seen and t - seen.T > 0.02 then vel = (pos - seen.Pos) / (t - seen.T) end if not seen or t - seen.T > 0.02 then boneSeen[inst] = { Pos = pos, T = t } end list[#list + 1] = { Pos = pos, Vel = vel, Model = inst } end end end return list end -- the first moment (seconds from now, within `horizon`) at which a bone passes within `radius` studs of `point`, and which bone function Kit.BoneThreat(bones, point, horizon, radius) local best, bestBone radius = radius or 4 for _, bone in ipairs(bones or {}) do local rel = point - bone.Pos local speed2 = bone.Vel:Dot(bone.Vel) local tClosest = speed2 > 1 and rel:Dot(bone.Vel) / speed2 or 0 if tClosest > -0.15 then tClosest = math.min(math.max(tClosest, 0), horizon or 1.6) local closest = (bone.Pos + bone.Vel * tClosest - point).Magnitude if closest < radius and (not best or tClosest < best) then best, bestBone = tClosest, bone end end end return best, bestBone end -- parts that kill the player (the zombie kill bricks are marked EnemiesOnly and are harmless to us) local lethalCache = { At = -10, List = {} } function Kit.IsLethal(point) local t = now() if t - lethalCache.At > 5 then lethalCache.At = t local list = {} local function consider(inst) if inst:IsA('BasePart') and inst:GetAttribute('EnemiesOnly') ~= true then list[#list + 1] = inst end end local tower = Kit.FindTower() local floors = tower and tower:FindFirstChild('Floors') for _, floor in ipairs(floors and floors:GetChildren() or {}) do local important = floor:FindFirstChild('Important') for _, inst in ipairs(important and important:GetChildren() or {}) do local name = inst.Name:lower() if name:find('killoob', 1, true) or name:find('outofbounds', 1, true) or name:find('killbrick', 1, true) then consider(inst) end end end for _, tag in ipairs({ 'KillBrick', 'OutOfBoundsPart' }) do for _, inst in ipairs(CollectionService:GetTagged(tag)) do consider(inst) end end lethalCache.List = list end for _, part in ipairs(lethalCache.List) do if part.Parent and withinPart(part, point) then return true end end return false end -- lava (the OnyxLava slabs: floors 1, 8, 9 and the whole of floor 11 at y 3600) costs 38 hp a touch and throws us upwards. -- The character reaches about 3 studs under the root and 2.5 over it, so the root keeps 4.5 clear below a slab and 5 above it. -- A thin sheet (floor 11) is never crossed: coming from under it, everything above it is out. local lavaCache = { At = -10, List = {} } function Kit.LavaParts() if Kit.LavaOverride then return Kit.LavaOverride end local t = now() if t - lavaCache.At > 0.3 then lavaCache.At = t local list = {} for _, inst in ipairs(CollectionService:GetTagged('OnyxLava')) do if inst:IsA('BasePart') and inst.Parent then list[#list + 1] = inst end end lavaCache.List = list end return lavaCache.List end Kit.SheetMargin = 8.5 -- Floor 11: the arena is ABOVE the lava sheet (a player climbs the ladder up from the entrance and fights at y 3608; the walkers stand at -- 3606-3609, the sheet is at 3599.75). The bot used to stay UNDER the sheet and died to it twice (-38 each) after a hop behind a flying ghost. -- Now it stays above it: a point over a thin sheet's footprint is never lower than SheetAbove over the sheet, and mobs under the sheet -- are left alone (they are waited for, see the decision) unless the wait turns into a stall (StayAboveSheet is switched off for a while). Kit.StayAboveSheet = true Kit.SheetAbove = 6.0 -- which side of a thin lava sheet a point is on: 'above' (1.5+ over it), 'below' (1.5+ under it), 'band' (touching distance), nil -- when no thin sheet lies over/under that spot. Second result: the sheet. function Kit.SheetSide(point) for _, part in ipairs(Kit.LavaParts()) do if part.Parent and part.Size.Y < 2 then local half = part.Size / 2 local localPoint = part.CFrame:PointToObjectSpace(point) if math.abs(localPoint.X) <= half.X + 2 and math.abs(localPoint.Z) <= half.Z + 2 then local top, bottom = part.Position.Y + half.Y, part.Position.Y - half.Y if point.Y >= top + 1.5 then return 'above', part elseif point.Y <= bottom - 1.5 then return 'below', part end return 'band', part end end end return nil end -- a point under a thin sheet but inside its margin, moved down to the margin (nil when it is fine or not under a sheet) function Kit.SheetGuard(point, wasAbove) for _, part in ipairs(Kit.LavaParts()) do if part.Parent and part.Size.Y < 2 then local localPoint = part.CFrame:PointToObjectSpace(point) local half = part.Size / 2 if math.abs(localPoint.X) <= half.X + 2 and math.abs(localPoint.Z) <= half.Z + 2 then local top, bottom = part.Position.Y + half.Y, part.Position.Y - half.Y if Kit.StayAboveSheet ~= false and wasAbove then -- we were above it a moment ago and are in touching distance now (the root sinks or is knocked): back up if point.Y < top + 4 and point.Y > bottom - 3 then return Vector3.new(point.X, top + Kit.SheetAbove, point.Z) end return nil end local below = bottom - Kit.SheetMargin if point.Y < bottom and point.Y > below then return Vector3.new(point.X, below, point.Z) end end end end return nil end -- does the straight way from a to b pass through a thin lava sheet (a on one side of it, b on the other, both over its footprint)? function Kit.CrossesSheet(a, b) for _, part in ipairs(Kit.LavaParts()) do if part.Parent and part.Size.Y < 2 then local half = part.Size / 2 local la, lb = part.CFrame:PointToObjectSpace(a), part.CFrame:PointToObjectSpace(b) if math.abs(la.X) <= half.X + 2 and math.abs(la.Z) <= half.Z + 2 and math.abs(lb.X) <= half.X + 2 and math.abs(lb.Z) <= half.Z + 2 then if (la.Y > 0) ~= (lb.Y > 0) then return true end end end end return false end -- is this point close to a thin lava sheet (floor 11): within its footprint and 9 studs of its height? function Kit.LavaNear(point) for _, part in ipairs(Kit.LavaParts()) do if part.Parent and part.Size.Y < 2 then local localPoint = part.CFrame:PointToObjectSpace(point) local half = part.Size / 2 if math.abs(localPoint.X) <= half.X + 2 and math.abs(localPoint.Z) <= half.Z + 2 and math.abs(point.Y - part.Position.Y) < 9 then return true end end end return false end Kit.WallMargin = 5.5 -- an upright wall of lava (floor 13's curtain: 3 x 93 x 106): a point inside its margin is moved out sideways, to the side of -- `from` (or its own side), keeping height and the other coordinate. Returns nil when the point is not near such a wall. function Kit.WallExit(part, point, from) local half = part.Size / 2 if half.Y < 2 or math.min(half.X, half.Z) >= 4 then return nil end local localPoint = part.CFrame:PointToObjectSpace(point) local margin = Kit.WallMargin if math.abs(localPoint.X) > half.X + margin or math.abs(localPoint.Z) > half.Z + margin or math.abs(localPoint.Y) > half.Y + margin then return nil end local thinX = half.X <= half.Z local thin = thinX and localPoint.X or localPoint.Z local halfThin = thinX and half.X or half.Z if math.abs(thin) > halfThin + margin then return nil end local side = thin >= 0 and 1 or -1 if from then local fromLocal = part.CFrame:PointToObjectSpace(from) local fromThin = thinX and fromLocal.X or fromLocal.Z if math.abs(fromThin) > halfThin then side = fromThin >= 0 and 1 or -1 end end local exit = side * (halfThin + margin + 0.5) local moved = thinX and Vector3.new(exit, localPoint.Y, localPoint.Z) or Vector3.new(localPoint.X, localPoint.Y, exit) return part.CFrame:PointToWorldSpace(moved) end function Kit.AvoidLava(point, from) for _, part in ipairs(Kit.LavaParts()) do if part.Parent then local wall = Kit.WallExit(part, point, from) if wall then return wall end local localPoint = part.CFrame:PointToObjectSpace(point) local half = part.Size / 2 if half.Y >= 2 and math.min(half.X, half.Z) < 4 then -- an upright wall the point is not near: the slab rules below do not apply to it continue end if half.Y < 2 and Kit.StayAboveSheet ~= false and math.abs(localPoint.X) <= half.X + 2 and math.abs(localPoint.Z) <= half.Z + 2 then -- stay above a thin sheet: nothing over its footprint is lower than SheetAbove over it. The one exception is a bot that is itself deep -- under the sheet (the entrance level) hopping to another place deep under it: that stays where it is. local top = part.Position.Y + half.Y local bottom = part.Position.Y - half.Y if point.Y < top + Kit.SheetAbove then local fromDeepUnder = from and from.Y < bottom - Kit.SheetMargin if not (fromDeepUnder and point.Y < bottom - Kit.SheetMargin) then return Vector3.new(point.X, top + Kit.SheetAbove, point.Z) end end continue end if math.abs(localPoint.X) <= half.X + 2 and math.abs(localPoint.Z) <= half.Z + 2 then local top = part.Position.Y + half.Y local bottom = part.Position.Y - half.Y -- a thin sheet (floor 11) gets 6.5 under it: the character was seen bobbing 2-5 studs at 4.7 under it, and touching it costs 38 hp local below, above = bottom - (half.Y < 2 and Kit.SheetMargin or 4.5), top + 5 if half.Y < 2 and from and from.Y < bottom then -- only a spot inside the band is pushed back under the sheet; one beyond it is simply jumped to (a teleport touches nothing on the way: -- floor 11's trapdoor to floor 12 is above the sheet, and the bot sat under it for good) if point.Y > below and point.Y < above then return Vector3.new(point.X, below, point.Z) end elseif point.Y > below and point.Y < above then -- coming from above a thin sheet, a spot in the lower half of its band is under the sheet (the last zombie of floor 11 stood -- 16 studs under the bot, and 'over it' was pushed back up to where the bot already was: a stall of minutes). A hop touches nothing on the way. if from and from.Y >= top and not (half.Y < 2 and point.Y < part.Position.Y) then return Vector3.new(point.X, above, point.Z) end return Vector3.new(point.X, below, point.Z) end end end end return point end -- Where we come to rest matters as much as where we hop to: floor 11's central structure rises through the sheet, so a hop -- that ends over it under the sheet drops us onto a surface inside the lava. Under a thin sheet, a hop must come to rest -- on something that is itself under the sheet (the decks, the pit floor); if not, a nearby column is used, or nothing. local lavaRay = RaycastParams.new() lavaRay.FilterType = Enum.RaycastFilterType.Exclude lavaRay.RespectCanCollide = true local function restRoot(x, z, startY) local ignore = { LP.Character } local tower = Kit.FindTower() local entities = tower and tower:FindFirstChild('Entities') if entities then ignore[#ignore + 1] = entities end lavaRay.FilterDescendantsInstances = ignore local hit = Workspace:Raycast(Vector3.new(x, startY, z), Vector3.new(0, -120, 0), lavaRay) return hit and (hit.Position.Y + 3) or nil end function Kit.LavaSafe(point, from) local adjusted = Kit.AvoidLava(point, from) local sheet for _, part in ipairs(Kit.LavaParts()) do if part.Parent and part.Size.Y < 2 then local localPoint = part.CFrame:PointToObjectSpace(adjusted) local half = part.Size / 2 if math.abs(localPoint.X) <= half.X + 2 and math.abs(localPoint.Z) <= half.Z + 2 then sheet = part break end end end if not sheet then return adjusted end local below = sheet.Position.Y - sheet.Size.Y / 2 - Kit.SheetMargin if adjusted.Y > below + 0.01 then return adjusted end local function fine(x, z) local rest = restRoot(x, z, adjusted.Y + 1.5) return rest == nil or rest <= below + 0.5 end if fine(adjusted.X, adjusted.Z) then return adjusted end for _, radius in ipairs({ 6, 12, 20 }) do for step = 0, 7 do local angle = step * math.pi / 4 local x, z = adjusted.X + math.cos(angle) * radius, adjusted.Z + math.sin(angle) * radius if fine(x, z) then return Vector3.new(x, adjusted.Y, z) end end end return adjusted end local function nearFloor(itemFloor, currentFloor) if itemFloor == nil then return true end if currentFloor == nil then return itemFloor <= 2 end return itemFloor >= currentFloor and itemFloor <= currentFloor + 1 end -- ------------------------------------------------------------- event info local function formatTime(seconds) seconds = math.max(0, math.floor(seconds)) local d = math.floor(seconds / 86400) local h = math.floor((seconds % 86400) / 3600) local m = math.floor((seconds % 3600) / 60) if d > 0 then return string.format('%dd %02dh', d, h) end return string.format('%dh %02dm', h, m) end local TASK_SHORT = { EventDuelRoundPlayed = 'Rounds', EventEliminations = 'Elims', EventDuelWon = 'Win', EventDuelWon2 = 'Win', EventDuelWon3 = 'Win', EventDuelWon4 = 'Win', } local eventCache = { At = -10, Text = nil } function Kit.EventInfo() local t = now() if t - eventCache.At < 1 then return eventCache.Text end eventCache.At = t local ok, text = pcall(function() local scripts = LP:FindFirstChild('PlayerScripts') local controllers = scripts and scripts:FindFirstChild('Controllers') if not controllers then return nil end local parts = {} local eventModule = controllers:FindFirstChild('EventController') if eventModule then local remaining = require(eventModule):GetTimeRemaining() if type(remaining) == 'number' then parts[#parts + 1] = 'ends ' .. formatTime(remaining) end end local dataModule = controllers:FindFirstChild('PlayerDataController') if dataModule then local data = require(dataModule) local candy = data:Get('EventCurrency') if type(candy) == 'number' then parts[#parts + 1] = 'Candy ' .. candy end local tasks = data:Get('EventTasks') local taskLibrary = ReplicatedStorage:FindFirstChild('Modules') and ReplicatedStorage.Modules:FindFirstChild('TaskLibrary') local info = taskLibrary and require(taskLibrary).Info or {} if type(tasks) == 'table' then local wins = {} for _, entry in ipairs(tasks) do local goal = info[entry.Name] and info[entry.Name].Goal local label = TASK_SHORT[entry.Name] or tostring(entry.Name) local piece = entry.Completed and 'done' or (tostring(entry.Progress or 0) .. '/' .. tostring(goal or '?')) if label == 'Win' then wins[#wins + 1] = piece else parts[#parts + 1] = label .. ' ' .. piece end end if #wins > 0 then parts[#parts + 1] = 'Wins ' .. table.concat(wins, ' ') end end end return #parts > 0 and table.concat(parts, ' | ') or nil end) eventCache.Text = ok and text or nil return eventCache.Text end -- ----------------------------------------------------------- attack alerts local alerts = {} local rings = {} local bindings = {} local bindTries = 0 local nextBindAt = 0 local hookWanted = false Kit.Telegraphs = {} Kit.Reviving = setmetatable({}, { __mode = 'k' }) local function enemyById(id) local ok, controller = pcall(ResolveEnemyController) if not ok or not controller then return nil end for _, enemy in ipairs(controller.Objects or {}) do local data = rawget(enemy, 'Data') if type(data) == 'table' and data.ObjectID == id then return enemy end end return nil end local function pushAlert(kind, text, duration, color) local t = now() for i = #alerts, 1, -1 do if alerts[i].Kind == kind then table.remove(alerts, i) end end alerts[#alerts + 1] = { Kind = kind, Text = text, Until = t + duration, Color = color or ALERT_COLOR } end local function pushRing(position, radius, duration, color) local t = now() rings[#rings + 1] = { Pos = position, Radius = radius, From = t, Until = t + duration, Color = color or ALERT_COLOR } if #rings > 24 then table.remove(rings, 1) end end local function num(value, fallback) return type(value) == 'number' and value or fallback end -- telegraphs the bot reads: kind -> { Model, Until } local function markTelegraph(kind, model, duration) Kit.Telegraphs[kind] = { Model = model, Until = now() + duration } end function Kit.OnEnemyEvent(name, packet) if type(packet) ~= 'table' then return end local args = packet.args or {} local enemy = enemyById(packet.objectId) local model = enemy and enemy.Model local who = model and model.Name or 'Enemy' local show = cfg.Alerts if name == 'ZombieExplosionEffect' then learn.LastAt = now() if typeof(args[1]) == 'Vector3' then learn.Pos = args[1] end if type(args[2]) == 'number' and args[2] > 1 then learn.Radius = args[2] end if show and typeof(args[1]) == 'Vector3' then pushRing(args[1], num(args[2], DEFAULT_BLAST), 1.5, Color3.fromRGB(255, 160, 40)) end elseif name == 'SlamEffect' then local windup = num(args[3], 0) + num(args[4], 0) markTelegraph(name, model, windup + 0.6) if show then if typeof(args[1]) == 'Vector3' then pushRing(args[1], num(args[2], 20), windup + 0.6) end pushAlert(name, 'SLAM', math.max(windup, 0.6)) notify(name, 'Tower', who .. ' slam incoming') end elseif name == 'AcidSlam' then markTelegraph(name, model, math.max(num(args[1], 1), 0.6)) if show then pushAlert(name, 'ACID SLAM', math.max(num(args[1], 1), 0.6)) notify(name, 'Tower', who .. ' acid slam') end elseif name == 'ClawAttack' then markTelegraph(name, model, math.max(num(args[1], 1), 0.6)) if show then pushAlert(name, 'CLAW', math.max(num(args[1], 1), 0.6)) notify(name, 'Tower', who .. ' claw attack') end elseif name == 'AcidExplosion' then if show and typeof(args[1]) == 'Vector3' then pushRing(args[1], num(args[2], 12), 1.5, Color3.fromRGB(255, 160, 40)) end elseif name == 'ChargerZombieChargeStart' then markTelegraph(name, model, math.max(num(args[1], 2), 0.6)) if show then pushAlert(name, 'CHARGE', math.max(num(args[1], 2), 0.6), Color3.fromRGB(255, 110, 60)) end elseif name == 'ShockerZombieZap' then local part = args[1] local character = LP.Character if show and typeof(part) == 'Instance' and character and part:IsDescendantOf(character) then pushAlert(name, 'ZAPPED', 1.2) end elseif name == 'HunterLeapEffect' then if show then pushAlert(name, 'LEAP', 1.0, Color3.fromRGB(255, 220, 60)) end elseif name == 'ZombieReanimation' then -- a zombie that is getting back up cannot be hurt: leave it alone until it is up again if model then Kit.Reviving[model] = now() + math.max(num(args[1], 5), 1) + 0.6 end if show then pushAlert(name, 'REVIVING', math.max(num(args[1], 5), 1), Color3.fromRGB(120, 255, 160)) end elseif name == 'FutileIntro' or name == 'FlightIntro' then if show then notify(name, 'Tower', who .. ' boss intro', 8) end end end local ALERT_ENUMS = { 'SlamEffect', 'AcidSlam', 'ClawAttack', 'AcidExplosion', 'ZombieExplosionEffect', 'ChargerZombieChargeStart', 'ShockerZombieZap', 'HunterLeapEffect', 'ZombieReanimation', 'FutileIntro', 'FlightIntro', } local function bindAlerts() local hook = RivalsRuntimeBridge.ReplicateHook if not hook or type(hook.Reserve) ~= 'function' then return false end for _, enumName in ipairs(ALERT_ENUMS) do if not bindings[enumName] then local ok, binding = pcall(hook.Reserve, { enumName }) if ok and binding then binding:SetHandler(function(packet) Kit.OnEnemyEvent(enumName, packet) end) bindings[enumName] = binding end end end for _, enumName in ipairs(ALERT_ENUMS) do if not bindings[enumName] then return false end end return true end local function setAlertBindings(enabled) for _, binding in pairs(bindings) do binding:SetEnabled(enabled) end if not enabled then local hook = RivalsRuntimeBridge.ReplicateHook if hook and type(hook.RevertIfIdle) == 'function' then hook.RevertIfIdle() end end end -- every replicate packet passes through the hook, so it is only installed while something needs it: -- the alert overlay, the bot's dodging, or learning the blast radius while an explosive zombie is around local function hookNeeded() if cfg.Alerts or cfg.Bot.Enabled then return true end if (cfg.Aim.ExplosiveGuard or cfg.Esp.BlastRing) and now() - Kit.ExplosiveSeenAt < 30 then return true end return false end function Kit.SetAlerts(enabled) cfg.Alerts = enabled if not enabled then alerts, rings = {}, {} end end -- ------------------------------------------------------------------ prompts local shown = {} Kit.ShownPrompts = shown local promptConnections = {} local firedAt = setmetatable({}, { __mode = 'k' }) local loggedKeys = {} local itemMap local lastPromptAt = 0 local function items() if itemMap == nil then itemMap = {} local modules = ReplicatedStorage:FindFirstChild('Modules') local library = modules and modules:FindFirstChild('ItemLibrary') local ok, lib = pcall(require, library) if ok and type(lib) == 'table' and type(lib.Items) == 'table' then itemMap = lib.Items end end return itemMap end local function promptPosition(prompt) local parent = prompt.Parent if not parent then return nil end if parent:IsA('BasePart') then return parent.Position elseif parent:IsA('Attachment') then return parent.WorldPosition elseif parent:IsA('Model') then return parent:GetPivot().Position end return nil end local function promptKey(prompt) local names = {} local node = prompt.Parent for _ = 1, 4 do if not node or node == Workspace then break end names[#names + 1] = node.Name node = node.Parent end return table.concat(names, '<') .. ' | ' .. prompt.ActionText .. ' | ' .. prompt.ObjectText end local INTERACT_WORDS = { 'lever', 'gate', 'switch', 'button', 'valve', 'crank', 'pull' } local function looksInteractive(prompt) local text = (prompt.Parent and prompt.Parent.Name or '') .. ' ' .. prompt.ActionText .. ' ' .. prompt.ObjectText text = text:lower() for _, word in ipairs(INTERACT_WORDS) do if text:find(word, 1, true) then return true end end return false end local function promptStep(t) if t - lastPromptAt < 0.15 then return end lastPromptAt = t if not (cfg.Pickup or cfg.Interact or cfg.Record) then return end local origin = rootPos() if not origin then return end local lobby = Workspace:FindFirstChild('Lobby') local range = cfg.PickupRange local tower = Kit.FindTower() local canFire = type(fireproximityprompt) == 'function' for prompt in pairs(shown) do if not prompt.Parent then shown[prompt] = nil elseif not (lobby and prompt:IsDescendantOf(lobby)) then if cfg.Record then local key = promptKey(prompt) if not loggedKeys[key] then loggedKeys[key] = true if #Kit.PromptLog < 200 then Kit.PromptLog[#Kit.PromptLog + 1] = key .. ' | hold ' .. tostring(prompt.HoldDuration) end print('[Tower] prompt: ' .. key .. ' | hold ' .. tostring(prompt.HoldDuration)) end end local at = promptPosition(prompt) local close = at and (at - origin).Magnitude <= math.min(range, prompt.MaxActivationDistance) local ready = canFire and prompt.Enabled and close and (not firedAt[prompt] or t - firedAt[prompt] > 1) if ready then local isWeapon = cfg.Pickup and prompt.ObjectText ~= '' and items()[prompt.ObjectText] ~= nil local isLever = cfg.Interact and tower and prompt:IsDescendantOf(tower) and looksInteractive(prompt) if isWeapon or isLever then firedAt[prompt] = t pcall(fireproximityprompt, prompt) end end end end end local function connectPrompts() promptConnections[#promptConnections + 1] = ProximityPromptService.PromptShown:Connect( function(prompt) shown[prompt] = true end ) promptConnections[#promptConnections + 1] = ProximityPromptService.PromptHidden:Connect( function(prompt) shown[prompt] = nil end ) end -- -------------------------------------------------------------------- queue function Kit.JoinQueue() local key = Kit.QueueModes[cfg.QueueMode] if not key then return end task.spawn(function() local remotes = ReplicatedStorage:FindFirstChild('Remotes') local folder = remotes and remotes:FindFirstChild('Matchmaking') local remote = folder and folder:FindFirstChild('JoinQueue') if not (remote and remote:IsA('RemoteFunction')) then notify('queue', 'Tower', 'JoinQueue remote not found (are you in the lobby?)', 1, true) return end local ok, result = pcall(remote.InvokeServer, remote, key) notify( 'queue', 'Tower', ok and ('Queue ' .. key .. ': ' .. tostring(result)) or ('Queue failed: ' .. tostring(result)), 1, true ) end) end -- the results screen's own buttons: one call each, made when the user presses the matching button in the tab function Kit.PlayAgain() local remotes = ReplicatedStorage:FindFirstChild('Remotes') local folder = remotes and remotes:FindFirstChild('Matchmaking') local remote = folder and folder:FindFirstChild('PlayAgain') if remote and remote:IsA('RemoteEvent') then remote:FireServer() notify('playagain', 'Tower', 'Play Again sent (works from the results screen)', 1, true) else notify('playagain', 'Tower', 'Play Again is not available here', 1, true) end end function Kit.LeaveMatch() local remotes = ReplicatedStorage:FindFirstChild('Remotes') local folder = remotes and remotes:FindFirstChild('Duels') local remote = folder and folder:FindFirstChild('LeaveDuel') if remote and remote:IsA('RemoteEvent') then remote:FireServer() end end function Kit.LeaveQueue() local remotes = ReplicatedStorage:FindFirstChild('Remotes') local folder = remotes and remotes:FindFirstChild('Matchmaking') local remote = folder and folder:FindFirstChild('TryLeaveQueue') if remote and remote:IsA('RemoteEvent') then remote:FireServer() end end -- ------------------------------------------------------------ weapon pick -- The tower opens a weapon pick window before a fight counts. Until a weapon is picked the fighter has no -- items and nothing you swing does any damage. The pick is an ordinary button, so it gets an ordinary click. local pick = { At = -10, Busy = false, Count = 0 } Kit.PickState = pick -- what to take from the window a claimed "New Weapon" drop opens, best first: hitscan guns that hurt a -- 75 hp zombie in few shots and keep their range -- the Grenade Launcher (projectile, 45 + 45 splash in 14 studs, 6 + 24 shells) is worth more than any of them Kit.LauncherName = 'Grenade Launcher' -- a gun whose bullets are projectiles but fly so fast and flat that it is aimed like a hitscan gun with a little lead -- (the Paintball Gun: 550 studs/s, no drop, 22 damage every 0.15 s; life 27 lost a good gun for one and then had none) function Kit.IsFastProjectile(info) if type(info) ~= 'table' or info.IsRaycast ~= false or info.Type ~= 'Gun' then return false end local capacity = (tonumber(info.MaxAmmo) or 0) + (tonumber(info.MaxAmmoReserve) or 0) return (tonumber(info.ProjectileSpeed) or 0) >= 250 and (tonumber(info.ProjectileGravity) or 1) <= 0.1 and (tonumber(info.ShootDamage) or 0) >= 8 and (tonumber(info.ShootExplosionRadius) or 0) <= 8 and capacity >= 30 end function Kit.IsLauncher(info) -- a projectile that explodes in a radius: fired at the middle of a pack from outside its blast return type(info) == 'table' and info.IsProjectile == true and (tonumber(info.ShootExplosionRadius) or 0) >= 10 and (tonumber(info.ShootExplosionDamage) or 0) >= 25 and (tonumber(info.ProjectileSpeed) or 0) >= 100 end -- area weapons first (Grenade Launcher 30 shells x 45 + 45 in 14 studs, RPG 18 rockets x 50 + 50 in 16, Distortion 45 shots x 5 pellets x 35), -- then what keeps firing (Energy Rifle never runs dry) and what kills most per magazine Kit.ClaimRanking = { 'Grenade Launcher', 'RPG', 'Distortion', 'Energy Rifle', 'Revolver', 'Assault Rifle', 'Wildcat', 'Burst Rifle', 'Paintball Gun', 'Scepter', 'Permafrost', 'Uzi', 'Shotgun', 'Handgun', 'Spray', 'Crossbow', 'Exogun', 'Shorty', 'Energy Pistols', } Kit.LastOffer = {} -- ---------------------------------------------------------- weapon vetting -- The bot only ever shoots plain hitscan guns that fire on a single click. Anything that has to be charged or -- wound up (Minigun, Bow) or that switches between modes (Gunblade) is never taken, and neither is anything -- else whose data says it works that way. The verdict comes from the item's own stats, the list is only a -- safety net for the names already known to be trouble. The Sniper is on it because Larpbase said so. Kit.BannedWeapons = { 'Minigun', 'Gunblade', 'Bow', 'Flamethrower', 'Freeze Ray', 'Sniper' } local itemInfoCache = {} function Kit.ItemInfo(name) local cached = itemInfoCache[name] if cached ~= nil then return cached or nil end local ok, library = pcall(function() return require(ReplicatedStorage.Modules.ItemLibrary) end) local info = ok and type(library) == 'table' and type(library.Items) == 'table' and library.Items[name] or nil itemInfoCache[name] = info or false return info end local SPECIAL_WORDS = { 'charg', 'windup', 'spinup', 'blade', 'transition', 'overheat' } -- no 'vortex': the Distortion's Vortex* keys are its right-click ability, its plain click is an ordinary hitscan shot -- 'good' = safe to take and shoot, 'weak' = harmless but useless to the bot (projectiles, one shot), 'banned' = never function Kit.WeaponVerdict(name, info) for _, banned in ipairs(Kit.BannedWeapons) do if banned == name then return 'banned', 'on the never-pick list' end end info = info or Kit.ItemInfo(name) if type(info) ~= 'table' then return 'weak', 'no data for it' end if info.Type ~= 'Gun' or info.Class == 'Utility' then return 'banned', 'not a plain gun' end for key, value in pairs(info) do if type(key) == 'string' and type(value) ~= 'table' and value ~= nil and value ~= false and value ~= 0 and value ~= '' then local lowered = key:lower() for _, word in ipairs(SPECIAL_WORDS) do if lowered:find(word, 1, true) then return 'banned', 'has to be charged or switches mode (' .. key .. ')' end end end end -- the one projectile gun the bot knows how to fire: it lobs the shell on a ballistic arc at the middle of a pack if Kit.IsLauncher(info) then return 'good', 'grenade launcher' end if Kit.IsFastProjectile(info) then return 'good', 'fast projectile' end if info.IsRaycast == false then return 'weak', 'projectile weapon' end local capacity = (tonumber(info.MaxAmmo) or 0) + (tonumber(info.MaxAmmoReserve) or 0) if capacity < 4 then return 'weak', 'almost no ammo' end if (tonumber(info.ShootDamage) or 0) <= 0 then return 'weak', 'no damage' end return 'good', '' end -- which of the offered weapon titles to take: the first wanted one that is good, then the best good one, then a -- harmless one, and only when the window offers nothing but banned weapons the least bad of those (the window cannot -- be closed without a pick). 'Random' is only ever taken when everything on offer is good anyway. Kit.BannedFallback = { 'Gunblade', 'Bow', 'Minigun', 'Flamethrower', 'Freeze Ray', 'Sniper' } function Kit.ChooseWeapon(offered, wanted) local present = {} local real = {} for _, title in ipairs(offered) do present[title] = true if title ~= 'Random' then real[#real + 1] = title end end local verdicts = {} local allGood = #real > 0 for _, title in ipairs(real) do verdicts[title] = Kit.WeaponVerdict(title) if verdicts[title] ~= 'good' then allGood = false end end for _, candidate in ipairs(wanted) do if candidate == 'Random' then if present.Random and allGood then return 'Random', 'good' end elseif present[candidate] and verdicts[candidate] == 'good' then return candidate, 'good' end end local function bestOf(tier) for _, candidate in ipairs(Kit.ClaimRanking) do if candidate ~= 'Random' and present[candidate] and verdicts[candidate] == tier then return candidate end end for _, title in ipairs(real) do if verdicts[title] == tier then return title end end return nil end local good = bestOf('good') if good then return good, 'good' end local weak = bestOf('weak') if weak then return weak, 'weak' end for _, candidate in ipairs(Kit.BannedFallback) do if present[candidate] then return candidate, 'banned' end end return real[1] or offered[1], 'banned' end -- Is one of the game's own menus open (a player profile card with Add Friend / Challenge / Party Invite...)? The bot -- attacks and shoots with a click in the middle of the screen, which would press whatever button sits there, so no -- click is sent while such a menu is open. local menuCache = { At = -10, Open = false } local MENU_TEXTS = { ['Party Invite'] = true, ['Add Friend'] = true, ['Challenge'] = true, ['Send Gift'] = true, ['Remove Friend'] = true, } function Kit.GameMenuOpen() local t = now() if t - menuCache.At < 0.8 then return menuCache.Open end menuCache.At = t local open = false local gui = LP:FindFirstChild('PlayerGui') local camera = Workspace.CurrentCamera local viewport = camera and camera.ViewportSize or Vector2.new(1920, 1080) for _, d in ipairs(gui and gui:GetDescendants() or {}) do -- the player list keeps its inspect panel parked off the right edge of the screen while it is closed, so the -- element has to be on screen (inside the viewport and not see-through) to count if (d:IsA('TextLabel') or d:IsA('TextButton')) and MENU_TEXTS[d.Text] and d.Visible and d.AbsoluteSize.X > 0 and d.TextTransparency < 0.95 and d.AbsolutePosition.X < viewport.X and d.AbsolutePosition.X + d.AbsoluteSize.X > 0 and d.AbsolutePosition.Y < viewport.Y and d.AbsolutePosition.Y + d.AbsoluteSize.Y > 0 then local node, shown = d.Parent, true while node and node ~= gui do if (node:IsA('GuiObject') and not node.Visible) or (node:IsA('ScreenGui') and not node.Enabled) then shown = false break end node = node.Parent end if shown then open = true break end end end menuCache.Open = open return open end function Kit.PickWindow() local gui = LP:FindFirstChild('PlayerGui') local main = gui and gui:FindFirstChild('MainGui') local frame = main and main:FindFirstChild('MainFrame') local pages = frame and frame:FindFirstChild('Pages') local page = pages and pages:FindFirstChild('PickWeapons') if not page then return nil end local node = page while node and node.Parent do if node:IsA('GuiObject') and not node.Visible then return nil end if node:IsA('ScreenGui') and not node.Enabled then return nil end node = node.Parent end return page, main end function Kit.HasLoadout() local ok, items = pcall(function() return ResolveLocalFighter().Items end) return ok and type(items) == 'table' and next(items) ~= nil end -- click the wanted weapon on the pick window; returns true when a click was started function Kit.PickWeapon(name) local t = now() if pick.Busy or t - pick.At < 2 then return false end local page, main = Kit.PickWindow() if not page then return false end local wanted = name or cfg.PickWeapon or 'Handgun' -- a single title, or a list ranked best first (the first one offered is taken) local wantList = type(wanted) == 'table' and wanted or { wanted } -- hunting the Grenade Launcher: it is taken whenever it is offered, whatever else was asked for if (cfg.Rage.LauncherHunt ~= false) and wantList[1] ~= Kit.LauncherName then local withLauncher = { Kit.LauncherName } for _, title in ipairs(wantList) do withLauncher[#withLauncher + 1] = title end wantList = withLauncher end local list = page:FindFirstChild('List') local container = list and list:FindFirstChild('Container') local byTitle, fallback, offered = {}, nil, {} for _, slot in ipairs(container and container:GetChildren() or {}) do local b = slot:IsA('GuiObject') and slot.Visible and slot:FindFirstChild('Button') local title = b and b:FindFirstChild('Title') if b and b:IsA('GuiButton') and title and title:IsA('TextLabel') then fallback = fallback or b byTitle[title.Text] = b offered[#offered + 1] = title.Text end end local chosen, verdict = Kit.ChooseWeapon(offered, wantList) local button = chosen and byTitle[chosen] or nil if not button then return false end wanted = chosen if verdict ~= 'good' then notify( 'pickverdict', 'Tower', 'no good weapon on offer (' .. verdict .. '): taking ' .. tostring(chosen), 5, true ) end Kit.LastOffer = offered Kit.OfferLog = Kit.OfferLog or {} -- the game's own pool for this mode (the picker lists what the fighter 'CanUseWeapon'; Random only picks among those shown) local okPool, poolInfo = pcall(function() local fighter = ResolveLocalFighter() local pool = fighter:Get('WeaponPool') return { Pool = type(pool) == 'table' and table.concat(pool, ',') or tostring(pool), Filter = tostring(fighter:Get('WeaponPoolFilterType')), Launcher = select(2, pcall(function() return tostring(fighter:CanUseWeapon(Kit.LauncherName)) end)), } end) Kit.OfferLog[#Kit.OfferLog + 1] = { Offered = offered, Picked = chosen, Pool = okPool and poolInfo or nil } pick.Busy, pick.At = true, t pick.Count += 1 task.spawn(function() pcall(function() local vim = game:GetService('VirtualInputManager') local inset = game:GetService('GuiService'):GetGuiInset() local center = button.AbsolutePosition + button.AbsoluteSize / 2 local x, y = center.X, center.Y + (main.IgnoreGuiInset and 0 or inset.Y) vim:SendMouseMoveEvent(x, y, game) task.wait(0.15) vim:SendMouseButtonEvent(x, y, 0, true, game, 0) task.wait(0.08) vim:SendMouseButtonEvent(x, y, 0, false, game, 0) end) pick.Busy = false end) notify( 'pick', 'Tower', 'picking weapon: ' .. tostring(wanted) .. ' (offered: ' .. table.concat(offered, ', ') .. ')', 5 ) return true end -- ------------------------------------------------------------------ drops -- Dead mobs sometimes leave a '_drop' part (about 8 studs wide, collected by touching it) holding a model -- named after what it is (seen so far: Ammo). The label is read from that model. local dropCache = { At = -10, List = {} } function Kit.Drops() local t = now() if t - dropCache.At < 0.25 then return dropCache.List end dropCache.At = t local list = {} for _, child in ipairs(Workspace:GetChildren()) do if child.Name == '_drop' and child:IsA('BasePart') then local label = '' for _, inner in ipairs(child:GetChildren()) do if inner:IsA('Model') then label = inner.Name break end end -- a drop with a prompt on it is the "Claim | New Weapon" kind local prompt = child:FindFirstChildWhichIsA('ProximityPrompt', true) if prompt then label = 'Weapon' end list[#list + 1] = { Part = child, Pos = child.Position, Label = label, Prompt = prompt } end end dropCache.List = list return list end local HEAL_WORDS = { 'heal', 'health', 'med', 'aid', 'hp', 'cross', 'regen', 'potion', 'food', 'life', 'heart' } function Kit.IsHealLabel(label) label = tostring(label or ''):lower() for _, word in ipairs(HEAL_WORDS) do if label:find(word, 1, true) then return true end end return false end -- ------------------------------------------------------------ melee speed -- Same idea as the hub's "Weapon Attack Speed": the melee item type's StartShooting is wrapped so every -- swing starts with a shorter AttackCooldown, and the real value goes back as soon as the swing started. -- Inside the hub that wrapper already exists behind its own switch (P4S1T1 / P4S1S4), so the switch is used. local fast = { Hooked = false, Module = nil, Original = nil, Wrapper = nil, Boost = 85, Saved = nil } Kit.FastMelee = fast local function callAtIdentity2(fn, ...) local previous = type(getthreadidentity) == 'function' and getthreadidentity() or nil if previous ~= nil and previous ~= 2 and type(setthreadidentity) == 'function' then pcall(setthreadidentity, 2) end local args = table.pack(...) local results = table.pack(pcall(fn, table.unpack(args, 1, args.n))) if previous ~= nil and previous ~= 2 and type(setthreadidentity) == 'function' then pcall(setthreadidentity, previous) end return results end -- the scale applied to the melee cooldown right now (1 = normal speed) function Kit.MeleeSpeedScale() if fast.Saved then return 1 / (1 + math.clamp(Options.P4S1S4.Value, 0, 85) / 100) end if fast.Hooked then return 1 / (1 + fast.Boost / 100) end return 1 end -- returns 'hub', 'own' or false. Several things can want the speed (the bot, the always-on switch): it stays -- on while any owner wants it and goes back to normal when the last one lets go. fast.Owners = {} function Kit.SetMeleeSpeed(wanted, boost, owner) fast.Owners[owner or 'bot'] = wanted and true or nil local on = next(fast.Owners) ~= nil boost = math.clamp(tonumber(boost) or 85, 0, 85) fast.Boost = boost if Toggles and Toggles.P4S1T1 and Options and Options.P4S1S4 then if on then if not fast.Saved then fast.Saved = { Toggle = Toggles.P4S1T1.Value, Boost = Options.P4S1S4.Value } end if not Toggles.P4S1T1.Value then pcall(function() Toggles.P4S1T1:SetValue(true) end) end pcall(function() Options.P4S1S4:SetValue(boost) end) return 'hub' elseif fast.Saved then local saved = fast.Saved fast.Saved = nil pcall(function() Options.P4S1S4:SetValue(saved.Boost) end) pcall(function() Toggles.P4S1T1:SetValue(saved.Toggle) end) end return false end if on then if fast.Hooked then return 'own' end local scripts = LP:FindFirstChild('PlayerScripts') local modules = scripts and scripts:FindFirstChild('Modules') local itemTypes = modules and modules:FindFirstChild('ItemTypes') local moduleScript = itemTypes and itemTypes:FindFirstChild('Melee') if not moduleScript then return false end local ok, melee = pcall(require, moduleScript) if not ok or type(melee) ~= 'table' or type(melee.StartShooting) ~= 'function' then return false end local original = melee.StartShooting local function wrapper(self, ...) local item = self if not (type(item) == 'table' and type(item.Name) == 'string') then local fighterOk, fighter = pcall(ResolveLocalFighter) item = fighterOk and fighter and fighter.EquippedItem or nil end local info = type(item) == 'table' and item.Info or nil local previous if fast.Hooked and type(info) == 'table' and type(info.AttackCooldown) == 'number' then previous = info.AttackCooldown info.AttackCooldown = math.max(0.001, previous / (1 + fast.Boost / 100)) end local results = callAtIdentity2(original, self, ...) if previous ~= nil then info.AttackCooldown = previous end if not results[1] then error(results[2], 0) end return table.unpack(results, 2, results.n) end fast.Module, fast.Original, fast.Wrapper = melee, original, wrapper melee.StartShooting = wrapper fast.Hooked = true return 'own' end if fast.Hooked then fast.Hooked = false if fast.Module and fast.Module.StartShooting == fast.Wrapper then fast.Module.StartShooting = fast.Original end fast.Module, fast.Original, fast.Wrapper = nil, nil, nil end return false end -- ----------------------------------------------------------------- grenade -- the Utility item (Grenade: 75 damage in a 16 stud radius, 2 s fuse, 30 s cooldown) function Kit.GrenadeInfo() local ok, fighter = pcall(ResolveLocalFighter) local items = ok and fighter and fighter.Items or nil if type(items) ~= 'table' then return nil end for slot, item in pairs(items) do local info = type(item) == 'table' and item.Info or nil if type(info) == 'table' and (info.Name == 'Grenade' or (info.Type == 'Throwable' and type(info.ExplosionRadius) == 'number')) then return { Slot = slot, Item = item, Info = info } end end return nil end -- ------------------------------------------------------------- explosions -- The explosive zombie's client class only draws the blast, but the server's packet carries the blast -- position and radius. Wrapping that one method lets the kit learn the real radius from the first -- explosion (the replicate hook is not needed for this). local boom = { Module = nil, Original = nil, Wrapper = nil } Kit.Booms = 0 Kit.BoomLog = {} Kit.BlastEffects = {} function Kit.WatchExplosions(on) if on then if boom.Wrapper then return true end local scripts = LP:FindFirstChild('PlayerScripts') local node = scripts and scripts:FindFirstChild('Modules') node = node and node:FindFirstChild('ClientReplicatedClasses') node = node and node:FindFirstChild('Enemies') node = node and node:FindFirstChild('ClientExplosiveZombie') if not node then return false end local ok, class = pcall(require, node) if not ok or type(class) ~= 'table' or type(class.ReplicateFromServer) ~= 'function' then return false end local original = class.ReplicateFromServer local function wrapper(self, name, ...) if name == 'ZombieExplosionEffect' then local position, radius = ... learn.LastAt = now() if typeof(position) == 'Vector3' then learn.Pos = position end if type(radius) == 'number' and radius > 1 then learn.Radius = radius end Kit.Booms += 1 -- what does the blast do to the other mobs around it? hp of everything within 30 studs now and 0.45 s later if typeof(position) == 'Vector3' and #Kit.BlastEffects < 40 then local before = {} for _, record in ipairs(Kit.Mobs()) do local gap = (record.Pos - position).Magnitude if gap < 30 and record.HP and record.Model ~= self.Model then before[record.Model] = { Gap = gap, HP = record.HP, Name = record.Model.Name } end end task.delay(0.45, function() local lost, died = {}, {} for model, entry in pairs(before) do local humanoid = model.Parent and model:FindFirstChildOfClass('Humanoid') local after = humanoid and humanoid.Health or 0 local bucket = entry.Gap < 8 and 1 or (entry.Gap < 14 and 2 or (entry.Gap < 22 and 3 or 4)) lost[bucket] = lost[bucket] or { n = 0, hp = 0 } lost[bucket].n += 1 lost[bucket].hp += math.max(0, entry.HP - after) if after <= 0 then died[bucket] = (died[bucket] or 0) + 1 end end Kit.BlastEffects[#Kit.BlastEffects + 1] = { Lost = lost, Died = died } end) end local root = Kit.MyRoot() if typeof(position) == 'Vector3' and root then Kit.BoomLog[#Kit.BoomLog + 1] = { Distance = (position - root.Position).Magnitude, Radius = radius, At = now(), Pos = position } if #Kit.BoomLog > 60 then table.remove(Kit.BoomLog, 1) end end end local results = callAtIdentity2(original, self, name, ...) if not results[1] then error(results[2], 0) end return table.unpack(results, 2, results.n) end boom.Module, boom.Original, boom.Wrapper = class, original, wrapper class.ReplicateFromServer = wrapper return true end if boom.Wrapper and boom.Module and boom.Module.ReplicateFromServer == boom.Wrapper then boom.Module.ReplicateFromServer = boom.Original end boom.Module, boom.Original, boom.Wrapper = nil, nil, nil return false end -- Wraps the shared enemy replicate handler so every PlayAnimation packet is seen (and passed on untouched). local attackWatch = { Module = nil, Original = nil, Wrapper = nil } function Kit.WatchAttacks(on) if on then if attackWatch.Wrapper then return true end local scripts = LP:FindFirstChild('PlayerScripts') local node = scripts and scripts:FindFirstChild('Modules') node = node and node:FindFirstChild('ClientReplicatedClasses') node = node and node:FindFirstChild('ClientEntity') node = node and node:FindFirstChild('ClientHumanoidEntity') if not node then return false end local ok, class = pcall(require, node) if not ok or type(class) ~= 'table' or type(class.ReplicateFromServer) ~= 'function' then return false end local original = class.ReplicateFromServer local function wrapper(self, name, ...) if name == 'PlayAnimation' then pcall(Kit.NoteAnimation, self, (...)) end local results = callAtIdentity2(original, self, name, ...) if not results[1] then error(results[2], 0) end return table.unpack(results, 2, results.n) end attackWatch.Module, attackWatch.Original, attackWatch.Wrapper = class, original, wrapper class.ReplicateFromServer = wrapper local hunterNode = scripts:FindFirstChild('Modules') hunterNode = hunterNode and hunterNode:FindFirstChild('ClientReplicatedClasses') hunterNode = hunterNode and hunterNode:FindFirstChild('Enemies') hunterNode = hunterNode and hunterNode:FindFirstChild('ClientHunterZombie') local okHunter, hunterClass = pcall(require, hunterNode or Instance.new('Folder')) if okHunter and type(hunterClass) == 'table' and type(hunterClass.ReplicateFromServer) == 'function' then local hunterOriginal = hunterClass.ReplicateFromServer local function hunterWrapper(self, name, ...) if name == 'HunterLeapEffect' then pcall(Kit.NoteAnimation, self, 'hunter:leap') end local results = callAtIdentity2(hunterOriginal, self, name, ...) if not results[1] then error(results[2], 0) end return table.unpack(results, 2, results.n) end attackWatch.HunterModule, attackWatch.HunterOriginal, attackWatch.HunterWrapper = hunterClass, hunterOriginal, hunterWrapper hunterClass.ReplicateFromServer = hunterWrapper end return true end if attackWatch.Wrapper and attackWatch.Module and attackWatch.Module.ReplicateFromServer == attackWatch.Wrapper then attackWatch.Module.ReplicateFromServer = attackWatch.Original end if attackWatch.HunterWrapper and attackWatch.HunterModule and attackWatch.HunterModule.ReplicateFromServer == attackWatch.HunterWrapper then attackWatch.HunterModule.ReplicateFromServer = attackWatch.HunterOriginal end attackWatch.HunterModule, attackWatch.HunterOriginal, attackWatch.HunterWrapper = nil, nil, nil attackWatch.Module, attackWatch.Original, attackWatch.Wrapper = nil, nil, nil return false end -- The Giant Chombie's slam is announced with its impact point, radius and timings (SlamEffect), so it can be -- stepped out of exactly. Wraps that one class method and passes everything on untouched. Kit.Slams = {} local slamWatch = { Module = nil, Original = nil, Wrapper = nil } function Kit.WatchSlams(on) if on then if slamWatch.Wrapper then return true end local scripts = LP:FindFirstChild('PlayerScripts') local node = scripts and scripts:FindFirstChild('Modules') node = node and node:FindFirstChild('ClientReplicatedClasses') node = node and node:FindFirstChild('Enemies') node = node and node:FindFirstChild('ClientGiantChombie') if not node then return false end local ok, class = pcall(require, node) if not ok or type(class) ~= 'table' or type(class.ReplicateFromServer) ~= 'function' then return false end local original = class.ReplicateFromServer local function wrapper(self, name, ...) if name == 'SlamEffect' then local position, radius, windup, delay, vulnerable = ... if typeof(position) == 'Vector3' and type(radius) == 'number' then local t = now() Kit.Slams[#Kit.Slams + 1] = { Pos = position, Radius = radius, At = t, HitAt = t + (tonumber(windup) or 0) + (tonumber(delay) or 0), Vulnerable = vulnerable and true or false, } if #Kit.Slams > 12 then table.remove(Kit.Slams, 1) end end end local results = callAtIdentity2(original, self, name, ...) if not results[1] then error(results[2], 0) end return table.unpack(results, 2, results.n) end slamWatch.Module, slamWatch.Original, slamWatch.Wrapper = class, original, wrapper class.ReplicateFromServer = wrapper return true end if slamWatch.Wrapper and slamWatch.Module and slamWatch.Module.ReplicateFromServer == slamWatch.Wrapper then slamWatch.Module.ReplicateFromServer = slamWatch.Original end slamWatch.Module, slamWatch.Original, slamWatch.Wrapper = nil, nil, nil return false end -- ------------------------------------------------------------------ packet tap -- Every replicate packet the server sends (fighters, enemies, the duel) passes ReplicatedController._ObjectChanged. The tap -- records, per packet name, how often it came, from which kind of object, and the arguments of the first few: the way to learn -- what an enemy's attack looks like on the wire (skeleton bones, charges, zaps, acid, claws...) and which state fields the server -- keeps replicating for each enemy. Observation only: the packet is passed on untouched. Kit.Tap = { On = false, Names = {}, Fields = {}, Total = 0, Since = 0 } local tapState = { Controller = nil, Original = nil, Wrapper = nil, Classes = {}, Models = {}, ClassesAt = -10, Enum = nil, Ring = {}, } Kit.Tap.Anims = {} local function tapEnum() if tapState.Enum == nil then local ok, library = pcall( function() return require(ReplicatedStorage:WaitForChild('Modules'):WaitForChild('EnumLibrary')) end ) tapState.Enum = ok and library or false end return tapState.Enum or nil end local function tapDecode(value) local library = tapEnum() if not library or type(value) ~= 'string' or #value == 0 or #value > 4 then return nil end local ok, name = pcall(library.FromEnum, library, value, true) return ok and type(name) == 'string' and name or nil end local function tapSummary(value, depth) local kind = typeof(value) if kind == 'number' then return string.format('%.3g', value) elseif kind == 'boolean' or kind == 'nil' then return tostring(value) elseif kind == 'string' then local decoded = tapDecode(value) if decoded then return '<' .. decoded .. '>' end return '"' .. value:sub(1, 36) .. '"' elseif kind == 'Vector3' then return string.format('V(%.0f,%.0f,%.0f)', value.X, value.Y, value.Z) elseif kind == 'CFrame' then local p = value.Position return string.format('CF(%.0f,%.0f,%.0f)', p.X, p.Y, p.Z) elseif kind == 'Instance' then return 'I:' .. value.Name elseif kind == 'table' then if depth >= 2 then return '{..}' end local parts, count = {}, 0 for key, item in pairs(value) do count += 1 if count > 8 then parts[#parts + 1] = '..' break end parts[#parts + 1] = tostring(key) .. '=' .. tapSummary(item, depth + 1) end return '{' .. table.concat(parts, ',') .. '}' end return kind end local function tapClassOf(objectId) local t = now() if t - tapState.ClassesAt > 0.75 then tapState.ClassesAt = t local ok, controller = pcall(ResolveEnemyController) if ok and controller then for _, enemy in ipairs(controller.Objects or {}) do local data = rawget(enemy, 'Data') if type(data) == 'table' and data.ObjectID ~= nil and enemy.Model then tapState.Classes[data.ObjectID] = enemy.Model.Name tapState.Models[data.ObjectID] = enemy.Model end end end end return tapState.Classes[objectId] end local ALERT_SET = {} for _, enumName in ipairs(ALERT_ENUMS) do ALERT_SET[enumName] = true end local function tapRecord(packed, offset) local objectId, token = packed[offset + 1], packed[offset + 2] if type(token) ~= 'string' then return end local name = tapDecode(token) or ('?' .. #token) local tap = Kit.Tap tap.Total += 1 local class = type(objectId) == 'string' and tapClassOf(objectId) or nil if (name == 'PlayAnimation' or name == 'CreateSound') and class and type(packed[offset + 3]) == 'string' then local id = packed[offset + 3] local model = tapState.Models[objectId] local root = model and model:FindFirstChild('HumanoidRootPart') local mine = rootPos() local distance = (root and mine) and (root.Position - mine).Magnitude or -1 local key = class .. '|' .. (name == 'CreateSound' and 'snd:' or '') .. id local stat = tap.Anims[key] if not stat then stat = { Seen = 0, Hits = 0, Near = 0, Far = 0, DelayMin = math.huge, DelayMax = 0, DistMin = math.huge, DistMax = 0, } tap.Anims[key] = stat end stat.Seen += 1 if distance >= 0 then stat.DistMin, stat.DistMax = math.min(stat.DistMin, distance), math.max(stat.DistMax, distance) end tapState.Ring[#tapState.Ring + 1] = { T = now(), Key = key, Dist = distance } if #tapState.Ring > 80 then table.remove(tapState.Ring, 1) end end if Kit.TapDispatch and ALERT_SET[name] and type(objectId) == 'string' then local args = { n = math.max(packed.n - offset - 2, 0) } for index = 1, args.n do args[index] = packed[offset + 2 + index] end pcall(Kit.OnEnemyEvent, name, { objectId = objectId, enumName = name, args = args }) end local entry = tap.Names[name] if not entry then entry = { Count = 0, Classes = {}, Samples = {}, First = now() } tap.Names[name] = entry end entry.Count += 1 local label = class or 'other' entry.Classes[label] = (entry.Classes[label] or 0) + 1 -- the state fields an enemy keeps replicating: DataValueChanged carries (key, value) if name == 'DataValueChanged' or name == 'BulkDataValueChanged' then local key = tapDecode(packed[offset + 3]) or (type(packed[offset + 3]) == 'string' and packed[offset + 3]) or nil if name == 'DataValueChanged' and key and class then local fields = tap.Fields[class] if not fields then fields = {} tap.Fields[class] = fields end local slot = fields[key] if not slot then slot = { Count = 0 } fields[key] = slot end slot.Count += 1 if slot.Count <= 3 then slot.Last = tapSummary(packed[offset + 4], 0) end end if entry.Count > 6 then return end end local limit = (class ~= nil) and 5 or 3 if #entry.Samples < limit then local parts = {} for index = offset + 3, math.min(packed.n, offset + 9) do parts[#parts + 1] = tapSummary(packed[index], 0) end entry.Samples[#entry.Samples + 1] = string.format('%.1f %s %s', now() - tap.Since, label, table.concat(parts, ' ')) end end -- called by the harness for every drop in health: every animation announced in the 1.6 s before it gets a hit on its tally function Kit.TapNoteDamage(lost) local t = now() local marked = {} for _, entry in ipairs(tapState.Ring) do local age = t - entry.T if age >= 0.02 and age <= 1.6 and not marked[entry.Key] then marked[entry.Key] = true local stat = Kit.Tap.Anims[entry.Key] if stat then stat.Hits += 1 stat.DelayMin, stat.DelayMax = math.min(stat.DelayMin, age), math.max(stat.DelayMax, age) if entry.Dist >= 0 and entry.Dist <= 10 then stat.Near += 1 elseif entry.Dist > 10 then stat.Far += 1 end end end end end function Kit.StartTap() if tapState.Wrapper then return true end local scripts = LP:FindFirstChild('PlayerScripts') local node = scripts and scripts:FindFirstChild('Modules') node = node and node:FindFirstChild('ReplicatedController') if not node then return false end local ok, controller = pcall(require, node) if not ok or type(controller) ~= 'table' then return false end local original = rawget(controller, '_ObjectChanged') if type(original) ~= 'function' then return false end local function wrapper(...) if Kit.Tap.On then local packed = table.pack(...) pcall(tapRecord, packed, (type(packed[1]) == 'table') and 1 or 0) end return (coroutine.wrap(original)(...)) end rawset(controller, '_ObjectChanged', wrapper) tapState.Controller, tapState.Original, tapState.Wrapper = controller, original, wrapper Kit.Tap.On, Kit.Tap.Since = true, now() return true end function Kit.StopTap() Kit.Tap.On = false if tapState.Wrapper and tapState.Controller and rawget(tapState.Controller, '_ObjectChanged') == tapState.Wrapper then rawset(tapState.Controller, '_ObjectChanged', tapState.Original) end tapState.Controller, tapState.Original, tapState.Wrapper = nil, nil, nil end -- the whole catalogue as text: packets by name (rarest first, the enemy-specific ones are the interesting ones), then the state -- fields per enemy kind function Kit.TapReport() local tap = Kit.Tap local lines = { string.format('PACKET CATALOGUE: %d packets in %.0f s', tap.Total, now() - tap.Since) } local names = {} for name in pairs(tap.Names) do names[#names + 1] = name end table.sort(names, function(a, b) local ea, eb = tap.Names[a], tap.Names[b] if ea.Count ~= eb.Count then return ea.Count < eb.Count end return a < b end) for _, name in ipairs(names) do local entry = tap.Names[name] local classes = {} for label, count in pairs(entry.Classes) do classes[#classes + 1] = label .. 'x' .. count end table.sort(classes) lines[#lines + 1] = string.format('PKT %s n=%d from %s', name, entry.Count, table.concat(classes, ', ')) for _, sample in ipairs(entry.Samples) do lines[#lines + 1] = ' ' .. sample end end local animKeys = {} for key in pairs(tap.Anims) do animKeys[#animKeys + 1] = key end table.sort(animKeys) for _, key in ipairs(animKeys) do local stat = tap.Anims[key] lines[#lines + 1] = string.format( 'ANIM %s seen %d before-damage %d (within 10: %d, farther: %d) delay %s mob distance %.0f-%.0f', key, stat.Seen, stat.Hits, stat.Near, stat.Far, stat.Hits > 0 and string.format('%.2f-%.2f s', stat.DelayMin, stat.DelayMax) or '-', stat.DistMin == math.huge and 0 or stat.DistMin, stat.DistMax ) end local kinds = {} for class in pairs(tap.Fields) do kinds[#kinds + 1] = class end table.sort(kinds) for _, class in ipairs(kinds) do local keys = {} for key in pairs(tap.Fields[class]) do keys[#keys + 1] = key end table.sort(keys) local parts = {} for _, key in ipairs(keys) do local slot = tap.Fields[class][key] parts[#parts + 1] = string.format('%s(%d)=%s', key, slot.Count, slot.Last or '?') end lines[#lines + 1] = 'FIELDS ' .. class .. ': ' .. table.concat(parts, ' ') end return table.concat(lines, '\n') end -- how far from an explosive zombie the player has to be when it dies function Kit.SnipeSafeDistance() return Kit.BlastRadius() + (cfg.Rage.SnipeMargin or 4) end -- ------------------------------------------------------------------- gun -- the picked hitscan gun (Handgun, Revolver...), with its ammo; shooting it is how an explosive zombie -- gets killed from outside its blast function Kit.GunEntry() local ok, fighter = pcall(ResolveLocalFighter) local items = ok and fighter and fighter.Items or nil if type(items) ~= 'table' then return nil end for slot, item in pairs(items) do local info = type(item) == 'table' and item.Info or nil if type(info) == 'table' and info.Type == 'Gun' and (info.IsRaycast ~= false or Kit.IsLauncher(info) or Kit.IsFastProjectile(info)) and Kit.WeaponVerdict(tostring(info.Name), info) == 'good' then local okAmmo, ammo = pcall(function() return item:Get('Ammo') end) local okReserve, reserve = pcall(function() return item:Get('AmmoReserve') end) -- a shell flies at ProjectileSpeed and drops with ProjectileGravity (a fraction of workspace gravity) local projectile if Kit.IsLauncher(info) then projectile = { Speed = tonumber(info.ProjectileSpeed) or 100, Gravity = 196.2 * (tonumber(info.ProjectileGravity) or 0.25), Radius = tonumber(info.ShootExplosionRadius) or 14, Cooldown = tonumber(info.ShootCooldown) or 0.6, } end return { Slot = slot, Item = item, Info = info, Ammo = okAmmo and tonumber(ammo) or 0, Reserve = okReserve and tonumber(reserve) or 0, Projectile = projectile, Fast = (not projectile and Kit.IsFastProjectile(info)) and { Speed = tonumber(info.ProjectileSpeed) or 300, } or nil, } end end return nil end -- ------------------------------------------------------------ melee reach -- Same idea as the hub's Melee Reach (PX13T1 / PX13S1): the item's AttackReach is scaled. Whether the -- server honours the longer reach is what the live swing log measures. local reachSaved = setmetatable({}, { __mode = 'k' }) Kit.ReachScaleApplied = 1 function Kit.SetMeleeReach(on, scale) scale = math.clamp(tonumber(scale) or 1, 1, 3) local ok, fighter = pcall(ResolveLocalFighter) local items = ok and fighter and fighter.Items or nil if type(items) ~= 'table' then return false end local touched = false for _, item in pairs(items) do local info = type(item) == 'table' and item.Info or nil if type(info) == 'table' and (info.Class == 'Melee' or info.Type == 'Melee') then if on then if not reachSaved[info] then reachSaved[info] = { Reach = rawget(info, 'AttackReach'), Heavy = rawget(info, 'HeavyAttackReach') } end local saved = reachSaved[info] if type(saved.Reach) == 'number' then rawset(info, 'AttackReach', saved.Reach * scale) touched = true end if type(saved.Heavy) == 'number' then rawset(info, 'HeavyAttackReach', saved.Heavy * scale) end elseif reachSaved[info] then local saved = reachSaved[info] if type(saved.Reach) == 'number' then rawset(info, 'AttackReach', saved.Reach) end if type(saved.Heavy) == 'number' then rawset(info, 'HeavyAttackReach', saved.Heavy) end reachSaved[info] = nil end end end Kit.ReachScaleApplied = on and touched and scale or 1 return touched end -- ------------------------------------------------------- the hub's Knife Extender (KE_*) -- KE_Enabled / KE_Range / KE_Scope raise the weapon's AttackReach by the range and move the origin of every swing that lands past the -- real reach forward by just enough, so a mob further away than the fists reach still takes the hit. The controls only exist inside the hub. -- The farm switches it on for all melee for the run and puts the user's own settings back afterwards. local extender = { Saved = nil } Kit.Extender = extender local function extenderControls() local toggle = Toggles and Toggles.KE_Enabled local range = Options and Options.KE_Range local scope = Options and Options.KE_Scope if toggle and range and scope then return toggle, range, scope end return nil end function Kit.ExtenderAvailable() return extenderControls() ~= nil end -- while the farm holds these controls they are left out of config saves: if the server changes mid-run (the Lua state dies with it) the -- user's saved config must not keep 'all melee, 20 studs' local EXTENDER_IDS = { 'KE_Enabled', 'KE_Range', 'KE_Scope' } local function extenderSaves(allowed) local manager = SaveManager if type(manager) == 'table' and type(manager.Ignore) == 'table' then for _, id in ipairs(EXTENDER_IDS) do manager.Ignore[id] = (not allowed) or nil end end end function Kit.SetExtender(on, rangeValue) local toggle, range, scope = extenderControls() if not toggle then return false end if on then if not extender.Saved then extender.Saved = { Enabled = toggle.Value, Range = range.Value, Scope = scope.Value } end extenderSaves(false) pcall(scope.SetValue, scope, 'All melee') pcall(range.SetValue, range, rangeValue) pcall(toggle.SetValue, toggle, true) return true end local saved = extender.Saved if saved then extender.Saved = nil pcall(scope.SetValue, scope, saved.Scope) pcall(range.SetValue, range, saved.Range) pcall(toggle.SetValue, toggle, saved.Enabled) extenderSaves(true) end return false end -- the studs the extender really adds to the fists right now (0 when it is off or does not cover all melee) function Kit.ExtraReach() local toggle, range, scope = extenderControls() if toggle and toggle.Value == true and scope.Value == 'All melee' then return tonumber(range.Value) or 0 end return 0 end -- ------------------------------------------------------------- diagnostics function Kit.Dump() local tower = Kit.FindTower() print('[Tower] model: ' .. (tower and tower.Name or 'none')) if tower then local floors = tower:FindFirstChild('Floors') for _, floor in ipairs(floors and floors:GetChildren() or {}) do print('[Tower] floor ' .. floor.Name) end end local totals = {} for _, s in ipairs(taggedInTower('MobSpawner', tower)) do local key = 'floor ' .. tostring(s.Floor) .. ' ' .. tostring(s.Name) totals[key] = (totals[key] or 0) + (tonumber(s.Count) or 1) end for key, count in pairs(totals) do print('[Tower] spawn ' .. key .. ' x' .. count) end print('[Tower] triggers: ' .. #taggedInTower('MobTrigger', tower)) print('[Tower] event: ' .. tostring(Kit.EventInfo())) print( string.format( '[Tower] blast radius %s (learned %s), blast damage %s', tostring(Kit.BlastRadius()), tostring(learn.Radius ~= nil), tostring(learn.Damage) ) ) for _, sample in ipairs(learn.Samples) do print( string.format( '[Tower] blast sample: damage %.1f at %s studs', sample.Damage, sample.Distance and string.format('%.1f', sample.Distance) or '?' ) ) end for _, line in ipairs(Kit.PromptLog) do print('[Tower] prompt log: ' .. line) end end -- --------------------------------------------------------------------- step local floorCache = { At = -10, Value = nil } local statusAt = 0 local statusCache = { At = -10, Text = '', Mobs = {} } local function currentFloor(tower, pos) local t = now() if t - floorCache.At > 0.25 then floorCache.At = t floorCache.Value = pos and Kit.FloorAt(tower, pos) or nil end return floorCache.Value end Kit.CurrentFloor = currentFloor function Kit.StatusText() local t = now() if t - statusCache.At < 0.25 then return statusCache.Text, statusCache.Mobs end statusCache.At = t local pos = rootPos() local tower = Kit.FindTower() local floor = currentFloor(tower, pos) local lines = {} lines[#lines + 1] = (tower and tower.Name or 'No tower in this server') .. (floor and (' | floor ' .. floor) or '') local mobs = Kit.GetMobs and Kit.GetMobs() or Kit.Mobs() local special = 0 for _, z in ipairs(mobs) do local model = z.Model if model and Kit.Classify(model.Name).Threat > 0 then special += 1 end end lines[#lines + 1] = string.format('Mobs alive: %d (special %d)', #mobs, special) if Kit.Bot and Kit.Bot.Status then lines[#lines + 1] = Kit.Bot.Status() end lines[#lines + 1] = tostring(Kit.EventInfo() or 'Event data not ready') statusCache.Text = table.concat(lines, '\n') statusCache.Mobs = mobs return statusCache.Text, mobs end function Kit.Step() if not Kit.Running then return end local cam = Workspace.CurrentCamera if not cam then return end local t = now() promptStep(t) watchDamage() -- the always-on switch for max fist speed works with or without the bot if cfg.FastMeleeAlways and not fast.Owners.always then Kit.SetMeleeSpeed(true, cfg.Rage.SpeedBoost, 'always') elseif not cfg.FastMeleeAlways and fast.Owners.always then Kit.SetMeleeSpeed(false, nil, 'always') end -- keep the replicate hook in step with what needs it local wanted = hookNeeded() if wanted and t >= nextBindAt and (bindTries < 20 or not hookWanted) then nextBindAt = t + 2 bindTries += 1 local complete = bindAlerts() setAlertBindings(true) hookWanted = true if complete then bindTries = 20 end elseif not wanted and hookWanted then hookWanted = false bindTries = 0 setAlertBindings(false) end for i = #alerts, 1, -1 do if alerts[i].Until <= t then table.remove(alerts, i) end end for i = #rings, 1, -1 do if rings[i].Until <= t then table.remove(rings, i) end end if Kit.StatusLabel and t - statusAt > 0.5 then statusAt = t pcall(function() Kit.StatusLabel:SetText((Kit.StatusText())) end) end local espOn = cfg.Esp.Enabled local botOverlay = cfg.Bot.Enabled or cfg.Bot.DryRun local wantDraw = cfg.Spawns or cfg.Triggers or cfg.Waypoint or cfg.Hud or espOn or botOverlay or #alerts > 0 or #rings > 0 if not wantDraw then if draw.TextCount > 0 or draw.LineCount > 0 or draw.RectCount > 0 then G.begin() G.finish() end return end G.begin() local pos = rootPos() local tower = Kit.FindTower() local floor = currentFloor(tower, pos) local camPos = cam.CFrame.Position local viewport = cam.ViewportSize if cfg.Spawns and tower then for _, s in ipairs(taggedInTower('MobSpawner', tower)) do if s.Part.Parent and nearFloor(s.Floor, floor) then local distance = (s.Part.Position - camPos).Magnitude if distance <= cfg.SpawnDist then local p = G.project(cam, s.Part.Position + Vector3.new(0, 3, 0)) if p then local class = Kit.Classify(s.Name) G.label( p, string.format( '%s%s x%s %dm', class.Tag, tostring(s.Name), tostring(s.Count), math.floor(distance) ), class.Color or SPAWN_COLOR, 12 ) end end end end end if cfg.Triggers and tower then for _, s in ipairs(taggedInTower('MobTrigger', tower)) do if s.Part.Parent and nearFloor(s.Floor, floor) and (s.Part.Position - camPos).Magnitude <= cfg.SpawnDist * 1.5 then G.box(cam, s.Part.CFrame, s.Part.Size, TRIGGER_COLOR) local p = G.project(cam, s.Part.Position) if p then G.label(p, 'TRIGGER', TRIGGER_COLOR, 12) end end end end if cfg.Waypoint and tower then local nextFloor = (floor or 0) + 1 local entrance = Kit.Entrance(tower, nextFloor) if entrance then local sp = cam:WorldToViewportPoint(entrance.Position) local center = viewport / 2 local screen = Vector2.new(sp.X, sp.Y) local onScreen = sp.Z > 0 and sp.X > 30 and sp.X < viewport.X - 30 and sp.Y > 30 and sp.Y < viewport.Y - 30 local at = screen if not onScreen then local direction = screen - center if sp.Z < 0 then direction = -direction end if direction.Magnitude < 1 then direction = Vector2.new(0, -1) end direction = direction.Unit local scale = math.min( (center.X - 40) / math.max(math.abs(direction.X), 1e-3), (center.Y - 40) / math.max(math.abs(direction.Y), 1e-3) ) at = center + direction * scale end local distance = pos and (entrance.Position - pos).Magnitude or (entrance.Position - camPos).Magnitude G.label(at, string.format('Floor %d entrance %dm', nextFloor, math.floor(distance)), WAYPOINT_COLOR, 14) G.seg(center, at, WAYPOINT_COLOR) end end if espOn and Kit.Esp then Kit.Esp.Draw(cam, pos) end if botOverlay and Kit.Bot and Kit.Bot.Overlay then Kit.Bot.Overlay(cam) end for _, ring in ipairs(rings) do local fade = 1 - math.clamp((t - ring.From) / math.max(ring.Until - ring.From, 0.01), 0, 1) if fade > 0 then G.ring(cam, ring.Pos + Vector3.new(0, 0.2, 0), ring.Radius, ring.Color) end end for index, alert in ipairs(alerts) do G.label( Vector2.new(viewport.X / 2, viewport.Y * 0.18 + (index - 1) * 24), string.format('%s %.1fs', alert.Text, math.max(0, alert.Until - t)), alert.Color, 20 ) end if cfg.Hud then local text, mobs = Kit.StatusText() local y = viewport.Y * 0.32 for line in text:gmatch('[^\n]+') do G.label(Vector2.new(18, y), line, HUD_COLOR, 13, true) y += 16 end for _, z in ipairs(mobs) do local model = z.Model local class = model and Kit.Classify(model.Name) if class and class.Boss then local hp, maxHp = Kit.Health(model) local pct = hp and maxHp and maxHp > 0 and math.floor(hp / maxHp * 100) or 0 G.label(Vector2.new(18, y), string.format('%s %d%%', model.Name, pct), class.Color, 14, true) y += 18 end end end G.finish() end -- ------------------------------------------------------------------ cleanup function Kit.Destroy() Kit.Running = false cfg.Alerts = false cfg.Bot.Enabled = false pcall(Kit.SetMeleeSpeed, false, nil, 'always') pcall(Kit.SetMeleeReach, false) pcall(Kit.WatchExplosions, false) if Kit.Bot and Kit.Bot.Destroy then pcall(Kit.Bot.Destroy) end for _, binding in pairs(bindings) do pcall(function() binding:Destroy() end) end bindings = {} local hook = RivalsRuntimeBridge.ReplicateHook if hook and type(hook.RevertIfIdle) == 'function' then pcall(hook.RevertIfIdle) end if learn.Conn then pcall(function() learn.Conn:Disconnect() end) learn.Conn = nil end for _, connection in ipairs(promptConnections) do pcall(function() connection:Disconnect() end) end promptConnections = {} shown = {} Kit.ShownPrompts = shown for _, list in ipairs({ draw.Text, draw.Line, draw.Rect }) do for _, object in ipairs(list) do pcall(function() object:Remove() end) end end draw.Text, draw.Line, draw.Rect = {}, {}, {} draw.TextCount, draw.LineCount, draw.RectCount = 0, 0, 0 alerts, rings = {}, {} end connectPrompts() return Kit end)() -- ===== Tower kit v2 :: ESP ===== RivalsRuntimeBridge.TowerKit.Esp = (function(Kit) local EspApi = {} local cfg = Kit.Cfg.Esp local gfx = Kit.Gfx local boundsCache = setmetatable({}, { __mode = 'k' }) local PLAIN_COLOR = Color3.fromRGB(255, 90, 90) local WHITE = Color3.fromRGB(255, 255, 255) local BAR_BG = Color3.fromRGB(20, 20, 20) local function boundsFor(record) local entry = boundsCache[record.Model] local t = Kit.Now() if entry and t - entry.At < 1.5 then return entry end local ok, cf, size = pcall(record.Model.GetBoundingBox, record.Model) if not ok or not size then return entry end entry = { At = t, Offset = record.Root.CFrame:ToObjectSpace(cf), Size = size } boundsCache[record.Model] = entry return entry end -- screen rectangle (minX, minY, maxX, maxY) of the mob, or nil when it is behind the camera function EspApi.ScreenRect(cam, record) local entry = boundsFor(record) if not entry then return nil end local cf = record.Root.CFrame * entry.Offset local hx, hy, hz = entry.Size.X / 2, entry.Size.Y / 2, entry.Size.Z / 2 local minX, minY, maxX, maxY = math.huge, math.huge, -math.huge, -math.huge local seen = 0 for _, x in ipairs({ -hx, hx }) do for _, y in ipairs({ -hy, hy }) do for _, z in ipairs({ -hz, hz }) do local sp = cam:WorldToViewportPoint(cf:PointToWorldSpace(Vector3.new(x, y, z))) if sp.Z > 0 then seen += 1 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 seen < 4 then return nil end return minX, minY, maxX, maxY end local function healthColor(fraction) return Color3.fromRGB(255, 60, 60):Lerp(Color3.fromRGB(80, 255, 90), math.clamp(fraction, 0, 1)) end function EspApi.ColorFor(record) local mode = cfg.ColorMode if mode == 'Health' and record.HP and record.MaxHP and record.MaxHP > 0 then return healthColor(record.HP / record.MaxHP) elseif mode == 'Fixed' then return PLAIN_COLOR end return record.Class.Color or PLAIN_COLOR end local function drawCorners(x1, y1, x2, y2, color) local len = math.max(4, math.min(x2 - x1, y2 - y1) * 0.28) gfx.seg(Vector2.new(x1, y1), Vector2.new(x1 + len, y1), color) gfx.seg(Vector2.new(x1, y1), Vector2.new(x1, y1 + len), color) gfx.seg(Vector2.new(x2, y1), Vector2.new(x2 - len, y1), color) gfx.seg(Vector2.new(x2, y1), Vector2.new(x2, y1 + len), color) gfx.seg(Vector2.new(x1, y2), Vector2.new(x1 + len, y2), color) gfx.seg(Vector2.new(x1, y2), Vector2.new(x1, y2 - len), color) gfx.seg(Vector2.new(x2, y2), Vector2.new(x2 - len, y2), color) gfx.seg(Vector2.new(x2, y2), Vector2.new(x2, y2 - len), color) end -- one record's visuals; returns true when something was drawn function EspApi.DrawOne(cam, record, myPos, isTarget) local class = record.Class local distance = myPos and (record.Pos - myPos).Magnitude or (record.Pos - cam.CFrame.Position).Magnitude local limit = cfg.MaxDistance if class.Boss or isTarget then limit = math.max(limit, 1500) end if distance > limit then return false end if cfg.OnlySpecial and class.Threat == 0 and not isTarget then return false end local x1, y1, x2, y2 = EspApi.ScreenRect(cam, record) if not x1 then return false end local color = EspApi.ColorFor(record) local style = cfg.Style if style == 'Box' then gfx.rect(Vector2.new(x1, y1), Vector2.new(x2 - x1, y2 - y1), color, false, 1.5) elseif style == 'Corners' then drawCorners(x1, y1, x2, y2, color) end local midX = (x1 + x2) / 2 if cfg.Health and record.HP and record.MaxHP and record.MaxHP > 0 then local fraction = math.clamp(record.HP / record.MaxHP, 0, 1) local height = y2 - y1 gfx.rect(Vector2.new(x1 - 7, y1 - 1), Vector2.new(4, height + 2), BAR_BG, true, 1, 0.7) gfx.rect( Vector2.new(x1 - 6, y2 - height * fraction), Vector2.new(2, height * fraction), healthColor(fraction), true, 1 ) end if cfg.Name then local bodyOnly = cfg.BodyHint and Kit.IsBodyOnly(record.Model) local text = class.Tag .. record.Model.Name .. (bodyOnly and ' [BODY]' or '') gfx.label(Vector2.new(midX, y1 - 16), text, class.Color or WHITE, 13) end if cfg.Distance or (cfg.Health and record.HP) then local pieces = {} if cfg.Health and record.HP and record.MaxHP then pieces[#pieces + 1] = string.format('%d/%d', math.floor(record.HP), math.floor(record.MaxHP)) end if cfg.Distance then pieces[#pieces + 1] = string.format('%dm', math.floor(distance)) end gfx.label(Vector2.new(midX, y2 + 2), table.concat(pieces, ' '), WHITE, 12) end if cfg.Tracer then local viewport = cam.ViewportSize gfx.seg(Vector2.new(viewport.X / 2, viewport.Y), Vector2.new(midX, y2), color, 1) end if isTarget and cfg.TargetMark then gfx.rect(Vector2.new(x1 - 3, y1 - 3), Vector2.new(x2 - x1 + 6, y2 - y1 + 6), WHITE, false, 2) gfx.label(Vector2.new(midX, y1 - 30), 'TARGET', WHITE, 12) end return true end function EspApi.Draw(cam, myPos) local mobs = Kit.Mobs() local current = Kit.Aim and Kit.Aim.Current for _, record in ipairs(mobs) do EspApi.DrawOne(cam, record, myPos, current ~= nil and current == record.Model) if cfg.BlastRing and record.Class.Explosive then local distance = myPos and (record.Pos - myPos).Magnitude or 0 if distance < 160 then local ground = record.Pos - Vector3.new(0, 3, 0) gfx.ring(cam, ground, Kit.BlastRadius(), Color3.fromRGB(255, 120, 30), 1) end end end end return EspApi end)(RivalsRuntimeBridge.TowerKit) -- ===== Tower kit v2 :: Aim assist helpers ===== RivalsRuntimeBridge.TowerKit.Aim = (function(Kit) local AimApi = {} local cfg = Kit.Cfg.Aim AimApi.Current = nil local PART_NAMES = { Head = { 'HitboxHead', 'Head' }, Body = { 'HitboxBody', 'HumanoidRootPart' }, } -- the BasePart to aim at: kind is 'Head' or 'Body' function AimApi.PartFor(model, kind) if not model then return nil end for _, name in ipairs(PART_NAMES[kind] or PART_NAMES.Head) do local part = model:FindFirstChild(name, true) if part and part:IsA('BasePart') then return part end end return model.PrimaryPart or model:FindFirstChildWhichIsA('BasePart', true) end -- true when this mob must not be shot right now (an explosive zombie close enough to hurt us) function AimApi.Guarded(record, myPos) if not cfg.ExplosiveGuard or not record.Class.Explosive or not myPos then return false end return (record.Pos - myPos).Magnitude < Kit.ExplosiveSafeDistance() end -- lower is better. base is the caller's own ranking (screen distance, or the aimbot's selection value) function AimApi.Score(record, base, distance) local score = base local priority = cfg.Priority local class = record.Class if priority == 'Closest' then score = distance elseif priority == 'Lowest HP' then score = (record.HP or 100) + math.min(base, 9999) / 10000 elseif priority == 'Bosses First' and class.Boss then score -= 1e6 elseif priority == 'Threats First' then score -= class.Threat * 1e5 end if cfg.ExplosivesFirst and class.Explosive then score -= 5e5 end return score end -- the legacy auto-fire loop's target pick (screen based, same record shape as the old list) function AimApi.PickLegacy(cam, fov) local root = Kit.MyRoot() local myPos = root and root.Position local center = cam.ViewportSize / 2 local best, bestScore for _, record in ipairs(Kit.Mobs()) do if not AimApi.Guarded(record, myPos) then local part = AimApi.PartFor(record.Model, Kit.AimPartFor(record.Model)) if part then local sp = cam:WorldToViewportPoint(part.Position) if sp.Z > 0 then local screenDistance = (Vector2.new(sp.X, sp.Y) - center).Magnitude if screenDistance <= fov then local distance = myPos and (record.Pos - myPos).Magnitude or screenDistance local score = AimApi.Score(record, screenDistance, distance) if not bestScore or score < bestScore then best, bestScore = record, score end end end end end end AimApi.Current = best and best.Model or nil return best end -- name the main aimbot's part resolver understands: it knows 'Head' and 'HumanoidRootPart' (= body hitbox) function AimApi.SilentPart(model) return Kit.AimPartFor(model) == 'Head' and 'Head' or 'HumanoidRootPart' end -- silent-aim integration: returns the adjusted selection value, or nil when the mob must be skipped function AimApi.SilentSelection(model, selection) local record = Kit.MobOf(model) if not record then return selection end local root = Kit.MyRoot() local myPos = root and root.Position if AimApi.Guarded(record, myPos) then return nil end local distance = myPos and (record.Pos - myPos).Magnitude or selection return AimApi.Score(record, selection, distance) end return AimApi end)(RivalsRuntimeBridge.TowerKit) -- ===== Tower kit v2 :: Auto Tower bot (ordinary inputs only) ===== RivalsRuntimeBridge.TowerKit.Bot = (function(Kit) local B = {} local cfg = Kit.Cfg.Bot local G = Kit.Gfx local UserInputService = game:GetService('UserInputService') local PathfindingService = game:GetService('PathfindingService') cfg.ChargerSpeed = cfg.ChargerSpeed or 24 B.State = 'off' B.Reason = '' B.Plan = nil B.Trace = {} local s = { lastMobAt = -10, target = nil, dodgeUntil = 0, dodgeDir = nil, dodgeSign = 1, avoidSince = nil, blocked = {}, ignore = setmetatable({}, { __mode = 'k' }), pausedUntil = 0, lastAttackAt = -10, lastEquipAt = -10, lastState = nil, triggerGoals = { At = -10, List = {} }, nextTriggerGoals = { At = -10, List = {} }, reachEst = 5, itemReach = 9, missStreak = 0, pendingSwing = nil, swingsLanded = 0, swingsMissed = 0, } B.Memory = s local tryAttack local function flat(v) return Vector3.new(v.X, 0, v.Z) end local function rotateY(v, degrees) local r = math.rad(degrees) local c, sn = math.cos(r), math.sin(r) return Vector3.new(v.X * c - v.Z * sn, 0, v.X * sn + v.Z * c) end local function isMelee(item) local info = type(item) == 'table' and item.Info or nil return type(info) == 'table' and (info.Class == 'Melee' or info.Type == 'Melee') end -- ------------------------------------------------------------- navigation local Nav = { Goal = nil, Waypoints = nil, Index = 2, Status = 'none', ComputedAt = -10, Computing = false, Token = 0, StuckFor = 0, LastPos = nil, LastCheck = 0, NoPathSince = nil, Params = nil, ParamsAt = -10, Side = 1, } B.Nav = Nav local function rayParams() local t = Kit.Now() if Nav.Params and t - Nav.ParamsAt < 1 then return Nav.Params end local params = RaycastParams.new() params.FilterType = Enum.RaycastFilterType.Exclude local ignore = { LP.Character } local tower = Kit.FindTower() local entities = tower and tower:FindFirstChild('Entities') if entities then ignore[#ignore + 1] = entities end params.FilterDescendantsInstances = ignore Nav.Params, Nav.ParamsAt = params, t return params end local function cast(origin, direction) return Workspace:Raycast(origin, direction, rayParams()) end function Nav.Reset() Nav.Goal, Nav.Waypoints, Nav.Status = nil, nil, 'none' Nav.Token += 1 Nav.Computing = false Nav.StuckFor = 0 Nav.NoPathSince = nil Nav.LastPos = nil end function Nav.Request(from, goal) if Nav.Computing then return end Nav.Computing = true local token = Nav.Token task.spawn(function() local path = PathfindingService:CreatePath({ AgentRadius = 2, AgentHeight = 5, AgentCanJump = true, WaypointSpacing = 5, }) local ok = pcall(function() path:ComputeAsync(from, goal) end) if token ~= Nav.Token then Nav.Computing = false return end Nav.Computing = false Nav.ComputedAt = Kit.Now() if ok and path.Status == Enum.PathStatus.Success then Nav.Waypoints = path:GetWaypoints() Nav.Index = 2 Nav.Status = 'path' Nav.NoPathSince = nil else Nav.Waypoints = nil Nav.Status = 'nopath' Nav.NoPathSince = Nav.NoPathSince or Kit.Now() end end) end -- avoids walls (jump low ones, deflect around tall ones) and ledges; returns direction, jump function Nav.Avoid(pos, direction) local feet = pos - Vector3.new(0, 2.6, 0) local reach = direction * 4.5 local low = cast(feet + Vector3.new(0, 1, 0), reach) local high = cast(feet + Vector3.new(0, 4.2, 0), reach) local jump = false if low and not high then jump = true elseif low and high then local chosen for _, angle in ipairs({ 40 * Nav.Side, -40 * Nav.Side, 80 * Nav.Side, -80 * Nav.Side }) do local d = rotateY(direction, angle) if not cast(feet + Vector3.new(0, 1, 0), d * 4.5) then chosen = d break end end if chosen then direction = chosen else jump = true end end -- do not walk off into nothing local ahead = pos + direction * 5 if not cast(ahead, Vector3.new(0, -9, 0)) then for _, angle in ipairs({ 50, -50, 100, -100 }) do local d = rotateY(direction, angle) if cast(pos + d * 5, Vector3.new(0, -9, 0)) then return d, jump end end return nil, false end return direction, jump end -- one navigation step toward goal; returns direction (or nil), jump, status function Nav.Step(w, goal) local pos, t = w.me.pos, w.now if not Nav.Goal or (Nav.Goal - goal).Magnitude > 6 then Nav.Reset() Nav.Goal = goal Nav.Status = 'pending' Nav.Request(pos, goal) elseif t - Nav.ComputedAt > 4 and not Nav.Computing then Nav.Request(pos, goal) end local toGoal = flat(goal - pos) if toGoal.Magnitude < 2.5 and math.abs(goal.Y - pos.Y) < 8 then return nil, false, 'arrived' end local target = goal local wantsJump = false if Nav.Waypoints then while Nav.Index <= #Nav.Waypoints do local wp = Nav.Waypoints[Nav.Index] if flat(wp.Position - pos).Magnitude < 3.2 and math.abs(wp.Position.Y - pos.Y) < 6 then Nav.Index += 1 else break end end local wp = Nav.Waypoints[Nav.Index] if wp then target = wp.Position wantsJump = wp.Action == Enum.PathWaypointAction.Jump and flat(wp.Position - pos).Magnitude < 6 end end local delta = flat(target - pos) if delta.Magnitude < 0.1 then return nil, false, 'arrived' end local direction, jump = Nav.Avoid(pos, delta.Unit) if t - Nav.LastCheck >= 0.5 then if Nav.LastPos and (pos - Nav.LastPos).Magnitude < 0.6 and direction then Nav.StuckFor += 0.5 else Nav.StuckFor = 0 end Nav.LastPos, Nav.LastCheck = pos, t end local status = Nav.Waypoints and 'path' or (Nav.Status == 'nopath' and 'direct' or 'pending') if Nav.StuckFor >= 1 and Nav.StuckFor < 3 then jump = true elseif Nav.StuckFor >= 3 and direction then Nav.Side = -Nav.Side direction = rotateY(direction, 90 * Nav.Side) jump = true end if Nav.StuckFor >= 6 then status = 'stuck' end return direction, jump or wantsJump, status end -- ----------------------------------------------------------------- inputs local function faceTowards(point, dt, alphaOverride) local controller = RivalsModsState.ResolveCameraController() if not controller or type(controller.SetRotation) ~= 'function' then return end local current = rawget(controller, 'Rotation') local cam = Workspace.CurrentCamera if typeof(current) ~= 'Vector2' or not cam then return end local pitch, yaw = CFrame.lookAt(cam.CFrame.Position, point):ToOrientation() local alpha = alphaOverride or math.clamp(1 - math.exp(-math.clamp(dt, 0, 0.1) * 16), 0, 1) local deltaPitch = pitch - current.X local deltaYaw = (yaw - current.Y + math.pi) % (2 * math.pi) - math.pi controller:SetRotation(Vector2.new(current.X + deltaPitch * alpha, current.Y + deltaYaw * alpha)) end local vim local function click() if Library and Library.Toggled then return false end if Kit.GameMenuOpen() then return false end local cam = Workspace.CurrentCamera if not cam then return false end vim = vim or Instance.new('VirtualInputManager') local x, y = cam.ViewportSize.X / 2, cam.ViewportSize.Y / 2 vim:SendMouseButtonEvent(x, y, 0, true, game, 1) local manager = vim task.delay(0.03, function() manager:SendMouseButtonEvent(x, y, 0, false, game, 1) end) return true end -- a cooked grenade: the throw button is held down and the throw leaves on release local cookDown = false local function pressHold() if Library and Library.Toggled then return false end if Kit.GameMenuOpen() then return false end local cam = Workspace.CurrentCamera if not cam then return false end vim = vim or Instance.new('VirtualInputManager') vim:SendMouseButtonEvent(cam.ViewportSize.X / 2, cam.ViewportSize.Y / 2, 0, true, game, 1) return true end local function releaseHold() local cam = Workspace.CurrentCamera vim = vim or Instance.new('VirtualInputManager') vim:SendMouseButtonEvent( cam and cam.ViewportSize.X / 2 or 0, cam and cam.ViewportSize.Y / 2 or 0, 0, false, game, 1 ) end -- taking the fists out cancels a throw that has not left the hand yet local function forceMelee() local fighter = ResolveLocalFighter() for slot, item in pairs(fighter and fighter.Items or {}) do if isMelee(item) then pcall(function() fighter:EquipItem(slot) end) return true end end return false end local function equipMelee(now) local fighter = ResolveLocalFighter() if not fighter then return false end local equipped = fighter.EquippedItem if equipped and isMelee(equipped) then s.itemReach = (equipped.Info and equipped.Info.AttackReach) or s.itemReach return true end if now - s.lastEquipAt < 1 then return false end s.lastEquipAt = now for slot, item in pairs(fighter.Items or {}) do if isMelee(item) then local ok = pcall(function() fighter:EquipItem(slot) end) if not ok then local keys = { Enum.KeyCode.One, Enum.KeyCode.Two, Enum.KeyCode.Three } local key = keys[tonumber(slot) or 0] if key then local manager = Instance.new('VirtualInputManager') manager:SendKeyEvent(true, key, false, game) manager:SendKeyEvent(false, key, false, game) end end return false end end return false end local function userIsSteering() return UserInputService:IsKeyDown(Enum.KeyCode.W) or UserInputService:IsKeyDown(Enum.KeyCode.A) or UserInputService:IsKeyDown(Enum.KeyCode.S) or UserInputService:IsKeyDown(Enum.KeyCode.D) end -- -------------------------------------------------------------- snapshot local function triggerGoals(tower, floor, now, cacheOverride) local cache = cacheOverride or s.triggerGoals if now - cache.At < 1 then return cache.List end cache.At = now local list = {} for _, trigger in ipairs(Kit.PendingTriggers(tower, floor or 1)) do local part = trigger.Part local inside = part.CFrame:PointToWorldSpace(Vector3.new(0, -part.Size.Y / 2 + 3, 0)) local ground = Kit.GroundAt(inside + Vector3.new(0, 4, 0), 14) local spot = (ground and (ground + Vector3.new(0, 3, 0))) or inside -- floor 15's trigger starts inside a lava slab: the foot of the volume is not a place to stand, so a spot further up the -- volume that lava leaves alone is used (the hop to the foot was pushed back under the slab every frame: a 100 s stall) if (Kit.AvoidLava(spot, nil) - spot).Magnitude > 0.01 then for lift = 8, part.Size.Y - 4, 4 do local higher = part.CFrame:PointToWorldSpace(Vector3.new(0, -part.Size.Y / 2 + lift, 0)) if (Kit.AvoidLava(higher, nil) - higher).Magnitude <= 0.01 then spot = higher break end end end list[#list + 1] = { Key = 'trigger:' .. tostring(math.floor(part.Position.X)) .. ',' .. tostring(math.floor(part.Position.Z)), Pos = spot, Part = part, } end cache.List = list return list end -- the triggers worth walking into. The entrance of the next floor can sit below that floor's own volume (floor 11), so -- standing on it never makes it "the current floor": after a few seconds there without the floor changing, the next -- floor's own trigger is the way on. local function triggerCandidates(w, mem, now, near) -- once committed it stays committed: hopping onto the trigger takes us away from the entrance, which must not send us back if mem.nextGateUntil and now < mem.nextGateUntil and #(w.nextTriggers or {}) > 0 then return w.nextTriggers end if w.entrance and #(w.nextTriggers or {}) > 0 and (w.entrance - w.me.pos).Magnitude <= near then mem.atEntranceSince = mem.atEntranceSince or now if now - mem.atEntranceSince > 2.5 then mem.nextGateUntil = now + 12 return w.nextTriggers end else mem.atEntranceSince = nil end return w.triggers or {} end function B.Snapshot() local root, hum = Kit.MyRoot(), Kit.MyHum() if not root or not hum or hum.Health <= 0 then return nil end local now = Kit.Now() local pos = root.Position local tower = Kit.FindTower() local floor = Kit.CurrentFloor(tower, pos) local mobs, hazards = {}, {} for _, record in ipairs(Kit.Mobs()) do local delta = record.Pos - pos if record.Class.Avoid then hazards[#hazards + 1] = { rec = record, class = record.Class, pos = record.Pos, dist3 = delta.Magnitude } continue end local distance = flat(delta).Magnitude local rageOn = cfg.Mode == 'Rage' -- floors are 25-60 studs apart: in rage mode mobs on the floor above or below are fair game too local yLimit = rageOn and 110 or 16 local range = rageOn and 600 or cfg.EngageRange if math.abs(delta.Y) <= yLimit and distance <= range and not (s.ignore[record.Model] and s.ignore[record.Model] > now) then local velocity = flat(record.Vel) local lookFlat = flat(record.Look or Vector3.new(0, 0, 1)) mobs[#mobs + 1] = { rec = record, class = record.Class, pos = record.Pos, hp = record.HP, maxHp = record.MaxHP, dist = distance, dist3 = delta.Magnitude, look = lookFlat.Magnitude > 0.01 and lookFlat.Unit or Vector3.new(0, 0, 1), dir = distance > 0.01 and flat(delta) / distance or Vector3.zero, vel = velocity, speed = velocity.Magnitude, attack = (Kit.Attacks[record.Model] and Kit.Attacks[record.Model].Known) and Kit.Attacks[record.Model] or nil, } end end table.sort(mobs, function(a, b) return a.dist < b.dist end) -- standing outside every floor (spawn platform, after a restart): go to the highest floor built so far local entranceFloor = (floor or 0) + 1 if floor == nil and tower then local top = 0 local floorsFolder = tower:FindFirstChild('Floors') for _, child in ipairs(floorsFolder and floorsFolder:GetChildren() or {}) do local number = tonumber(child.Name) if number and number > top and Kit.Entrance(tower, number) then top = number end end if top > 0 then entranceFloor = top end end -- nothing is worth doing above the top of the highest floor built so far: hops are clamped to a little over it local ceilingY if tower then local floorsRoot = tower:FindFirstChild('Floors') for _, child in ipairs(floorsRoot and floorsRoot:GetChildren() or {}) do local important = child:FindFirstChild('Important') local volume = important and important:FindFirstChild('Volume') if volume and volume:IsA('BasePart') then local top = volume.Position.Y + volume.Size.Y / 2 ceilingY = math.max(ceilingY or top, top) end end ceilingY = ceilingY and ceilingY + 30 or nil end local entrancePart = tower and Kit.Entrance(tower, entranceFloor) or nil local entrancePos if entrancePart then local ground = Kit.GroundAt(entrancePart.Position + Vector3.new(0, 4, 0), 30) entrancePos = ground and (ground + Vector3.new(0, 3, 0)) or entrancePart.Position end return { now = now, me = { pos = pos, hp = hum.Health, maxHp = hum.MaxHealth, root = root, hum = hum }, tower = tower, floor = floor, ceilingY = ceilingY, mobs = mobs, hazards = hazards, windup = Kit.Windup, windupFor = Kit.WindupFor, slamLog = Kit.Slams, slams = (function() local active = {} for _, slam in ipairs(Kit.Slams) do if now <= slam.HitAt + 0.5 then active[#active + 1] = slam end end return active end)(), reach = (cfg.Mode == 'Rage' and Kit.Cfg.Rage.Position == 'Above') and math.max(s.reachEst, (Kit.Cfg.Rage.VerticalOffset or 5.6) + 0.3) or s.reachEst, drops = Kit.Drops(), grenade = Kit.GrenadeInfo(), gun = Kit.GunEntry(), -- is there floor under this point / a clear line from one point to another? (used to pick shooting spots) ground = function(point) return Kit.GroundAt(point + Vector3.new(0, 4, 0), 30) ~= nil end, sight = function(from, to) local hit = cast(from, to - from) return hit == nil or (hit.Position - to).Magnitude < 3 end, bones = Kit.Bones(), -- walls for shots: only what collides blocks a bullet, and a body-sized box at the spot must overlap no wall shotLine = function(from, to) return (Kit.LineClear(from, to, 3)) end, spotFree = function(point) return Kit.SpotFree(point) end, triggers = tower and triggerGoals(tower, floor, now) or {}, nextTriggers = (tower and floor and entranceFloor ~= floor) and triggerGoals(tower, entranceFloor, now, s.nextTriggerGoals) or {}, entrance = entrancePos, entranceKey = entrancePos and ('entrance:' .. tostring(entranceFloor)) or nil, entranceFloor = entranceFloor, } end -- ---------------------------------------------------------------- tactics local function canMeleeExplosive(w, explosive) local mode = cfg.ExplosiveMode if mode == 'Tank' then return true end if mode == 'Avoid' then return false end local hpPct = w.me.hp / math.max(w.me.maxHp, 1) * 100 if hpPct < cfg.MinHpExplosive then return false end for _, m in ipairs(w.mobs) do if m ~= explosive and m.dist < 10 then return false end end return true end local function pickTarget(w, mem, explosiveHeld) local current = mem.target local best for _, m in ipairs(w.mobs) do local skip = m.class.Explosive and explosiveHeld if not skip then if current and m.rec.Model == current and m.dist < (w.mobs[1].dist + 6) then return m end best = best or m end end return best end -- pure decision: no side effects besides updating the memory table function B.Decide(w, mem) local act = { State = 'idle', Reason = '' } local me, now, mobs = w.me, w.now, w.mobs local hpPct = me.hp / math.max(me.maxHp, 1) * 100 local nearest = mobs[1] if nearest then mem.lastMobAt = now end if cfg.StopAtBoss then for _, m in ipairs(mobs) do if m.class.Boss then act.State, act.Reason, act.Stop = 'stop', 'a boss appeared: ' .. m.rec.Model.Name .. ' (not automated)', true return act end end end if hpPct < cfg.RetreatPct then if cfg.LowHpAction == 'Stop' then act.State, act.Reason, act.Stop = 'stop', string.format('health %d%%', math.floor(hpPct)), true return act end mem.retreating = true elseif mem.retreating and hpPct >= cfg.RetreatPct + 10 then mem.retreating = false end if mem.dodgeUntil > now and mem.dodgeDir then act.State, act.Reason = 'dodge', 'sidestepping a charge' act.MoveDir = mem.dodgeDir act.Face = nearest and nearest.pos or nil return act end for _, m in ipairs(mobs) do if m.class.Charger and m.dist < 80 and m.dist > 4 then local tele = Kit.Telegraphs.ChargerZombieChargeStart local telegraphed = tele and tele.Model == m.rec.Model and tele.Until > now local closing = m.vel:Dot(-m.dir) if telegraphed or (m.speed > cfg.ChargerSpeed and closing > m.speed * 0.6) then mem.dodgeSign = -mem.dodgeSign local toMe = -m.dir mem.dodgeDir = Vector3.new(-toMe.Z, 0, toMe.X) * mem.dodgeSign mem.dodgeUntil = now + 0.65 act.State, act.Reason = 'dodge', 'charger incoming' act.MoveDir = mem.dodgeDir act.Face = m.pos return act end end end local safe = Kit.ExplosiveSafeDistance() local explosive for _, m in ipairs(mobs) do if m.class.Explosive then explosive = m break end end local explosiveHeld = false if explosive and explosive.dist < safe + 2 then if not canMeleeExplosive(w, explosive) then mem.avoidSince = mem.avoidSince or now if now - mem.avoidSince < 6 then act.State, act.Reason = 'avoid', string.format( 'explosive zombie %dm (blast %d)', math.floor(explosive.dist), math.floor(Kit.BlastRadius()) ) act.MoveDir = -explosive.dir act.Face = explosive.pos return act end end else mem.avoidSince = nil if explosive and cfg.ExplosiveMode ~= 'Tank' and not canMeleeExplosive(w, explosive) then explosiveHeld = true end end if mem.retreating and nearest and nearest.dist > 6 then if nearest.dist < 50 then local away = Vector3.zero for _, m in ipairs(mobs) do if m.dist < 50 then away -= m.dir / math.max(m.dist, 1) end end if away.Magnitude < 1e-3 then away = -nearest.dir end act.State, act.Reason = 'retreat', string.format('health %d%%', math.floor(hpPct)) act.MoveDir = away.Unit act.Face = nearest.pos return act end act.State, act.Reason = 'recover', string.format('health %d%%, keeping away', math.floor(hpPct)) act.Face = nearest.pos return act end local target = pickTarget(w, mem, explosiveHeld) if target then mem.target = target.rec.Model act.State = 'fight' act.Reason = string.format('%s %dm', target.rec.Model.Name, math.floor(target.dist)) act.Target = target act.Face = target.pos + Vector3.new(0, 1.2, 0) local standOff = math.min(cfg.StandOff, w.reach - 0.6) local spacingMin = math.max(2.8, math.min(cfg.SpacingMin, standOff - 1.2)) act.Attack = target.dist <= w.reach + 0.4 if cfg.Spacing and target.dist < spacingMin then act.MoveDir = -target.dir act.Reason = act.Reason .. ' (backing off)' elseif target.dist > standOff then local approaching = target.vel:Dot(-target.dir) > 3 if target.dist > cfg.HuntRange and approaching then act.State = 'hold' act.Reason = string.format('%s is coming (%dm)', target.rec.Model.Name, math.floor(target.dist)) act.Attack = false else act.Goal = target.pos end end return act elseif explosiveHeld and explosive then act.State, act.Reason = 'hold', 'letting the explosive zombie come to me' act.Face = explosive.pos return act end mem.target = nil if now - mem.lastMobAt < cfg.ClearDelay then act.State, act.Reason = 'wait', 'waiting for more spawns' return act end if cfg.WaveTriggers then local bestGoal, bestDistance for _, goal in ipairs(triggerCandidates(w, mem, now, 8)) do if not (mem.blocked[goal.Key] and mem.blocked[goal.Key] > now) then local d = flat(goal.Pos - me.pos).Magnitude if d < 400 and (not bestDistance or d < bestDistance) then bestGoal, bestDistance = goal, d end end end if bestGoal then act.State, act.Reason = 'trigger', string.format('walking into the next wave trigger (%dm)', math.floor(bestDistance)) act.Goal, act.GoalKey = bestGoal.Pos, bestGoal.Key return act end end if cfg.Advance and w.entrance and not (mem.blocked[w.entranceKey] and mem.blocked[w.entranceKey] > now) then act.State, act.Reason = 'advance', string.format('heading for floor %d', w.entranceFloor or ((w.floor or 0) + 1)) act.Goal, act.GoalKey = w.entrance, w.entranceKey return act end act.State, act.Reason = 'idle', 'nothing reachable to do' return act end -- -------------------------------------------------------------- execution function tryAttack(w, target) local fighter = ResolveLocalFighter() local item = fighter and fighter.EquippedItem if not (item and isMelee(item)) then return false end local info = item.Info or {} local cooldown = (tonumber(info.AttackCooldown) or 0.35) * Kit.MeleeSpeedScale() local interval = math.max(cooldown, cfg.Mode == 'Rage' and 0.08 or 0.3) + 0.03 local cam = Workspace.CurrentCamera local ready = w.now - s.lastAttackAt >= interval and (tonumber(rawget(item, '_shoot_cooldown')) or 0) <= tick() if not (ready and cam) then return false end local toTarget = target.pos + Vector3.new(0, 1.2, 0) - cam.CFrame.Position if toTarget.Magnitude > 0.1 and cam.CFrame.LookVector:Dot(toTarget.Unit) > 0.9 then if click() then s.lastAttackAt = w.now s.lastSwing = { T = w.now, Dist = target.dist3 or target.dist, Model = target.rec.Model, HP = target.hp } if not s.pendingSwing then s.pendingSwing = s.lastSwing end return true end end return false end function B.Exec(w, act, dt) local me = w.me if act.State ~= 'idle' then equipMelee(w.now) end local faceAt = act.Face if not faceAt then local cam = Workspace.CurrentCamera if act.MoveDir and cam then faceAt = cam.CFrame.Position + act.MoveDir * 20 elseif act.Goal then faceAt = act.Goal end end if faceAt then faceTowards(faceAt, dt) end local direction, jump, status if act.MoveDir then direction = act.MoveDir direction, jump = Nav.Avoid(me.pos, direction) Nav.Goal = nil elseif act.Goal then direction, jump, status = Nav.Step(w, act.Goal) if status == 'stuck' or (Nav.NoPathSince and w.now - Nav.NoPathSince > 8 and Nav.StuckFor >= 2) then if act.GoalKey then s.blocked[act.GoalKey] = w.now + 25 elseif act.Target then s.ignore[act.Target.rec.Model] = w.now + 15 end Nav.Reset() end else Nav.Goal = nil end local hum = me.hum hum:Move(direction or Vector3.zero, false) if jump and hum.FloorMaterial ~= Enum.Material.Air then hum.Jump = true end if act.Attack and act.Target then tryAttack(w, act.Target) end end -- learns the real melee reach: 0.6 s after a swing, did the target lose health (or die)? function B.Calibrate(now) local p = s.pendingSwing if not p or now - p.T < 0.6 then return end s.pendingSwing = nil local record = Kit.MobOf(p.Model) local landed = record == nil or (p.HP ~= nil and record.HP ~= nil and record.HP < p.HP - 0.5) if landed then s.missStreak = 0 s.swingsLanded += 1 s.reachEst = math.min(s.itemReach - 0.5, math.max(s.reachEst, p.Dist + 0.2)) else s.swingsMissed += 1 s.missStreak += 1 if s.missStreak >= 2 and p.Dist <= s.reachEst + 0.5 then s.reachEst = math.max(3.5, p.Dist - 0.6) s.missStreak = 0 end end end -- ---------------------------------------------------------------- rage mode -- Moves by writing our own root part's CFrame (the same primitive the game uses for its own warps) -- instead of walking. It never sends the server any message about the move. Hop length adapts to -- whatever the server tolerates: any pull-back halves it, clean hops grow it, and repeated pull-backs -- at the smallest size switch the bot back to walking. local rage = Kit.Cfg.Rage local R = { lastMobAt = -10, target = nil, blocked = {}, tries = {}, lastHopAt = -10, maxHop = rage.MaxHop, expected = nil, expectedAt = -10, counted = nil, accepted = 0, rejected = 0, rejectTimes = {}, sinceGrow = 0, hops = 0, safeSpot = nil, fastOn = false, reachOn = false, gren = nil, grenadeAt = nil, grenadesThrown = 0, claimTries = {}, claimAt = -9, claiming = nil, shots = 0, lastShotAt = -1, gunEquipAt = -1, reloadAt = -9, boomOn = false, attackOn = false, slamOn = false, noclipConn = nil, freezeConn = nil, original = setmetatable({}, { __mode = 'k' }), partsAt = -10, parts = {}, } B.Rage = R local ANGLES = { 0, 45, -45, 90, -90, 135, -135, 180 } -- how many other mobs could reach a player standing at this point, closer ones counting more: -- zombies reach about 4.7 studs and walk ~1 stud per 0.1 s, so anything within ~7 arrives during a kill local function exposureAt(point, skip, all) local radius = rage.ExposureRadius or 7 local total = 0 for _, other in ipairs(all) do if other ~= skip then local d = (other.pos - point).Magnitude if d < radius then total += 1 + (radius - d) / radius end end end return total end -- (defined further down, after the swing helpers it uses) local swingExposureAt local shooterSightAt -- is there an explosive zombie within `range` of this point? (a drop beside one is not worth the hop: its blast is 50 and they go off together) local function boomNear(w, point, range) for _, m in ipairs(w.mobs) do if m.class.Explosive and (m.pos - point).Magnitude < range then return true end end return false end -- how many times the prompt of a weapon crate has been pressed (the decision memory in tests, the rage state in the game) local function claimTriesOf(mem, key) return math.max((mem.claimTries and mem.claimTries[key]) or 0, (R.claimTries and R.claimTries[key]) or 0) end -- a mob's announced swing, while it is still on its way (the announcement stays on the record after the hit) local function liveAttack(w, m) local a = m.attack if not a then return nil end local own = w.windupFor and w.windupFor(a.Id) or w.windup or { Max = 0.95 } if w.now - a.At <= (own.Max or 0.95) + (own.Slack or 0.15) then return a end return nil end local function facingOf(w, target) local look = target.look if not look or look.Magnitude < 0.1 then local away = flat(target.pos - w.me.pos) look = away.Magnitude > 0.1 and away.Unit or Vector3.new(0, 0, 1) end return look end -- how high to hover over a brute. BruteVertical is the setting; BruteLabHeights (live tests only) cycles through several -- heights every BruteLabStep seconds so one run measures which of them the brute's swing reaches (BRUTELAB lines) local function bruteVertical(w) local list = rage.BruteLabHeights if type(list) == 'table' and #list > 0 then return list[(math.floor(w.now / (rage.BruteLabStep or 1.4)) % #list) + 1] end return rage.BruteVertical or rage.VerticalOffset or 6 end -- where to stand next to this mob and how exposed that place is: the ring of candidate points around it, -- least exposed first, 'Behind' (or 'Beside') winning ties local function bestSpot(w, target, all) local mode = rage.Position local pos = target.pos -- a pinata (Chicken Trojan) is harmless and huge: stand right on top of it, inside its hitbox if target.class.Pinata then return pos + Vector3.new(0, 3 + (Kit.Lift or 0), 0), 0 end local d = math.min(rage.Distance, math.max(1.5, w.reach - 1)) -- a longer reach than the mobs' (about 4.7) is only worth something from near its far end if rage.Kite ~= false and w.reach >= 6.5 then d = math.max(d, w.reach - 1.2) end if mode == 'Under' or mode == 'Above' then local sign = mode == 'Above' and 1 or -1 local vertical = rage.VerticalOffset if target.class.CloseDodge and mode == 'Above' then vertical = bruteVertical(w) end -- a ghost is one swing (20-50 hp, the fists do 60): from 6 studs only half the swings land, from under 5.5 nearly all if target.class.Name == 'Ghost' and mode == 'Above' then vertical = rage.GhostVertical or 4.6 end local point = pos + Vector3.new(0, sign * vertical, 0) return point, (all and rage.SmartSpot ~= false) and exposureAt(point, target, all) or 0 end local look = facingOf(w, target) local base = -look if mode == 'Beside' then base = Vector3.new(-look.Z, 0, look.X) end if rage.SmartSpot == false or not all then return pos + base * d, 0 end local bestPoint, bestScore, bestExposure for index, angle in ipairs(ANGLES) do local direction = angle == 0 and base or rotateY(base, angle) local point = pos + direction * d local exposure = exposureAt(point, target, all) local score = exposure * 10 + index * 0.05 if not bestScore or score < bestScore then bestPoint, bestScore, bestExposure = point, score, exposure end end return bestPoint, bestExposure end R.Spot = bestSpot -- Standing still over a mob lets its attack line up on us (a ghost or reaper aims at where we were when it winds up; a zombie's hit area -- is in front of it). A player keeps circling the mob he is hitting, so the hit lands where he was. This is the next point on a small -- circle round the target, continuing in the direction we were going, drawn towards the side the mob is not facing, away from others. local function orbitSpot(w, mem, target, all) local base = target.pos local rel = flat(w.me.pos - base) local current = rel.Magnitude > 0.3 and math.atan2(rel.Z, rel.X) or math.random() * math.pi * 2 local look = target.look and flat(target.look) or Vector3.zero local backAngle = look.Magnitude > 0.1 and math.atan2(-look.Z, -look.X) or nil local radius = rage.OrbitRadius or 2.4 local vertical = target.class.Flyer and (rage.OrbitFlyerVertical or 3.6) or (rage.OrbitVertical or 4.8) local step = math.rad(rage.OrbitStep or 55) local state = mem.orbit if not state or state.model ~= target.rec.Model then state = { model = target.rec.Model, dir = (math.random() < 0.5) and 1 or -1 } mem.orbit = state end local TAU = math.pi * 2 local function wrap(angle) return ((angle + math.pi) % TAU) - math.pi end local best, bestScore, bestAngle local function consider(angle, bonus) local point = base + Vector3.new(math.cos(angle) * radius, vertical, math.sin(angle) * radius) if Kit.IsLethal(point) then return end local score = exposureAt(point, target, all) * 10 + swingExposureAt(w, point, target) * 25 + shooterSightAt(w, point) * 4 - bonus if math.abs(wrap(angle - current)) < math.rad(25) then score += 6 end if backAngle then score += math.abs(wrap(angle - backAngle)) * 1.2 end if not bestScore or score < bestScore then best, bestScore, bestAngle = point, score, angle end end consider(current + state.dir * step, 0.5) consider(current - state.dir * step, 0) consider(current + state.dir * 2 * step, 0) if backAngle then consider(backAngle, 0) end if best then local moved = wrap(bestAngle - current) if math.abs(moved) > 0.2 then state.dir = moved > 0 and 1 or -1 end end return best end -- returns the target, the spot to stand on, and how exposed that spot is local function pickRageTarget(w, pool, mem, all) local order = rage.TargetOrder -- the ones close to us come first, they are the ones that hurt. Among them the quickest to kill and the ones whose -- swing lands soonest, not the lone mob across the room that the pack trails behind local candidates, focused = pool, false -- a flyer hovering at the lava sheet is not chased there: the spot over it is inside the sheet's reach (it comes down to us anyway) do local safe = {} for _, m in ipairs(pool) do if not (m.class.Flyer and Kit.StayAboveSheet == false and Kit.LavaNear(m.pos)) then safe[#safe + 1] = m end end if #safe > 0 then candidates = safe pool = safe end end -- one mob at a time: the target stays the target until it is dead, then the next one is picked (user: 'until a mob is not dead it will -- focus it then when killed go to another'; it stops the jiggle between two mobs). No range, swing or crowd exception. The only way out of a -- lock besides the kill is the older 'is not taking damage' skip, which has already taken the mob out of the pool by the time we get here. if rage.TargetLock ~= false and mem.target then for _, m in ipairs(pool) do if m.rec.Model == mem.target then local lockedPoint, lockedExposure = bestSpot(w, m, all) Kit.LockHolds = (Kit.LockHolds or 0) + 1 return m, lockedPoint, lockedExposure end end end if rage.Focus ~= false then local near = {} for _, m in ipairs(pool) do if m.dist3 <= (rage.FocusRadius or 11) then near[#near + 1] = m end end -- kill order: flyers, then skeletons / brutes / the other big ones, then plain zombies. The first two groups are -- candidates from further out than the focus ring (the nearest few), so a ghost across the room is not -- ignored while zombies are close if rage.KillOrder ~= false then local inNear = {} for _, m in ipairs(near) do inNear[m] = true end local extras = {} -- hurt, the bot does not cross the room for them (life 43: two 40 m hops to skeletons at 50 hp with twenty mobs about) local radius = rage.TierRadius or 36 if w.me.hp / math.max(w.me.maxHp, 1) * 100 < (rage.TierLowHp or 55) then radius = rage.TierRadiusLow or 18 end for _, m in ipairs(pool) do if not inNear[m] and Kit.KillTier(m.class) <= 2 then -- a skeleton throws along its line: the hop across the room to it (life 47: 17 of 23 hops to skeletons ended in a dodge, -- nothing hit) goes through other skeletons' lines, so it is only worth it from a shorter distance local limit = (m.class.Shooter and math.min(radius, rage.TierShooterRadius or 22) or radius) + (mem.target and m.rec.Model == mem.target and (rage.TierKeep or 10) or 0) if m.dist3 <= limit then extras[#extras + 1] = m end end end table.sort(extras, function(a, b) return a.dist3 < b.dist3 end) for index = 1, math.min(#extras, rage.TierExtras or 6) do near[#near + 1] = extras[index] end end if #near > 0 then candidates, focused = near, true end end local best, bestScore, bestPoint, bestExposure local lowestTier = 3 for _, m in ipairs(candidates) do local score, point, exposure if focused then point, exposure = bestSpot(w, m, all) local hits = m.hp and math.max(1, math.ceil(m.hp / 30)) or 3 score = hits * 0.8 + m.dist3 * 0.3 - (m.class.Threat or 0) * 0.5 - (liveAttack(w, m) and 1.5 or 0) + (exposure or 0) * (rage.FocusExposure or 0.7) + swingExposureAt(w, point, m) * (rage.FocusSwing or 3) + shooterSightAt(w, point) * (rage.ShooterCoverWeight or 2.5) elseif order == 'Isolated' then -- the mob whose standing spot has the fewest others around it: the pack trails behind every hop point, exposure = bestSpot(w, m, all) score = exposure * 10 + m.dist3 * 0.04 + swingExposureAt(w, point, m) * 25 + shooterSightAt(w, point) * (rage.ShooterCoverWeight or 2.5) * 4 elseif order == 'LowestHP' then score = (m.hp or 100) + m.dist3 / 1000 elseif order == 'Threat' then score = -m.class.Threat * 1000 + m.dist3 else score = m.dist3 end if m.class.Name == 'Ghost' and m.dist3 > (rage.GhostFar or 20) then score += 40 end if mem.target and m.rec.Model == mem.target then score -= focused and 1 or 6 end if m.finishNow then score -= 1000 end if m.class.Ranged and rage.RangedFirst ~= false and (exposure == nil or exposure <= 3) then score -= 2000 end if rage.KillOrder ~= false then local tier = Kit.KillTier(m.class) if tier < lowestTier then lowestTier = tier end -- one that is far off and sits in the middle of a crowd waits its turn: hopping in there to reach it is how health goes if tier < 3 and m.dist3 > (rage.FocusRadius or 11) and exposure and exposure > (rage.TierExposure or 6) then tier = 3 end -- ...and so does one whose standing spot is next to another mob that is mid-swing (landing there is how the hits land) if tier < 3 and point and swingExposureAt(w, point, m) > 0 then tier = 3 end score += (tier - 1) * (rage.TierWeight or 3000) if m.class.Flyer and m.dist3 <= (rage.FlyerImminent or 20) then score -= 3000 end end if not bestScore or score < bestScore then best, bestScore, bestPoint, bestExposure = m, score, point, exposure end end if best and rage.KillOrder ~= false and mem.target ~= best.rec.Model then local picked = Kit.KillTier(best.class) Kit.TierPicks[picked] = (Kit.TierPicks[picked] or 0) + 1 -- a lower group was picked while a higher one was in the running (it was too deep in a crowd to reach) if picked > lowestTier then Kit.TierHeld += 1 end end if best and not bestPoint then bestPoint, bestExposure = bestSpot(w, best, all) end return best, bestPoint, bestExposure end -- where to be while attacks that already started land: on the standing spot of another target that is clear of -- every attacker (the hop doubles as the next kill), otherwise straight up out of everyone's reach -- how far an attacker is from a point for the purpose of its swing: area attacks ignore height local function swingDistance(attacker, point) if attacker.class.Area then return flat(attacker.pos - point).Magnitude end return (attacker.pos - point).Magnitude end -- how close to an attacker a spot has to be to count as inside its swing: the dodge range for most, the measured reach -- (brute: hits landed from 11 studs away, 5.8 above, and from further) plus a margin for the ones that have one local function swingReach(attacker, default) if attacker.class.AlwaysDodge and attacker.class.Flyer then return rage.FlyerLunge or 22 end if attacker.class.Area then return attacker.class.Area + 1 end if attacker.class.Reach then return attacker.class.Reach + 1 end return default end -- how many mobs that are in the middle of a swing could still reach a point by the time it lands (skip does not count). -- A flyer closes 5-14 studs while its swing winds up (measured: WINDUP MOVEMENT, never standing still), so it counts from -- much further away than a walker that plants itself. Most of the damage the bot takes lands within 0.2 s of a hop, on a -- spot next to somebody who had already announced a swing. -- shooters (skeletons) with a clear line to a point, announced or not: they throw from up to 90 studs, 25 a bone, and a pack of -- them took 311 of the 480 health life 12 lost on floor 12. Fewer is better; no line at all is cover. function shooterSightAt(w, point) if rage.ShooterCover == false then return 0 end local count, checked = 0, 0 for _, m in ipairs(w.mobs) do if m.class.Shooter and not m.class.Pinata and (m.pos - point).Magnitude <= (rage.ShooterSightRange or 85) then checked += 1 if checked > 6 then break end if not w.sight or w.sight(m.pos + Vector3.new(0, 1.2, 0), point + Vector3.new(0, 1.5, 0)) then count += 1 end end end return count end function swingExposureAt(w, point, skip) local total = 0 for _, m in ipairs(w.mobs) do if m ~= skip and not m.class.Pinata and liveAttack(w, m) then if m.class.Shooter then -- a shooter (skeleton: bones) reaches far, but only along a clear line if swingDistance(m, point) < (rage.ShooterRange or 90) and (not w.sight or w.sight(m.pos + Vector3.new(0, 1.2, 0), point + Vector3.new(0, 1.5, 0))) then total += 1 end else local reach = m.class.Flyer and (rage.FlyerLunge or 22) or swingReach(m, 9) if swingDistance(m, point) < reach then total += 1 end end end end return total end B.SwingExposure = swingExposureAt B.ShooterSight = shooterSightAt -- ---------------------------------------------------------- crowds and flocks -- how hard the surroundings press on us right now: what is within reach counts one, what is closing in half, a swing -- that was already announced one more, heavy hitters a bit extra. Mobs on another floor cannot reach us and do not count. local function pressureOf(w) local total, close = 0, 0 for _, m in ipairs(w.mobs) do if not m.class.Pinata and math.abs(m.pos.Y - w.me.pos.Y) <= 30 then local weight = 0 if m.dist3 <= 7 then weight, close = 1, close + 1 elseif m.dist3 <= (rage.PressureRadius or 13) then weight, close = 0.5, close + 1 end if m.dist3 <= 26 and liveAttack(w, m) then weight += 1 end -- (a ghost is one swing: a flock of them is fought where we stand, running only brings them along) if m.class.Name == 'Ghost' then weight *= (rage.GhostPressure or 0.5) end if weight > 0 and (m.class.Ranged or m.class.Explosive or m.class.Boss) then weight += 0.5 end total += weight end end return total, close end -- a step sideways out of a crowd or a flock: far from every mob, floor under it, to the side of the pack rather than -- straight back, so the chasers have to turn and string out behind each other and arrive one at a time. Going up -- achieves nothing against flyers (they follow) and little against the rest. local STEP_ANGLES = { 90, -90, 60, -60, 120, -120, 30, -30, 0 } -- where to wait while the horde comes out of a popped pinata: far from the pop point and a little up. (sideStep cannot be used: at the -- moment of the pop there are no mobs yet to step away from.) local function hordeWaitSpot(w, mem) local origin = mem.pinataPos or w.me.pos local away = flat(w.me.pos - origin) away = away.Magnitude > 1 and away.Unit or Vector3.new(1, 0, 0) local distance = rage.HordeWaitDistance or 50 local lift = Vector3.new(0, rage.HordeWaitLift or 8, 0) for _, d in ipairs({ distance, distance - 20, distance - 40, distance - 60, 40 }) do for _, angle in ipairs(STEP_ANGLES) do local point = origin + rotateY(away, angle) * d local floorPoint = Vector3.new(point.X, origin.Y, point.Z) if not Kit.IsLethal(point + lift) and not Kit.IsLethal(floorPoint) and (not w.ground or w.ground(floorPoint)) and (not w.spotFree or w.spotFree(point + lift)) then return point + lift end end end return nil end local function sideStep(w, opts) opts = opts or {} local here = w.me.pos local center, count = Vector3.zero, 0 for _, m in ipairs(w.mobs) do if not m.class.Pinata and m.dist3 <= 55 and math.abs(m.pos.Y - here.Y) <= 30 then center += m.pos count += 1 end end if count == 0 then return nil end center = center / count local away = flat(here - center) away = away.Magnitude > 1 and away.Unit or Vector3.new(1, 0, 0) local base = opts.distance or rage.StepDistance or 28 local clear = opts.clear or 11 local bestPoint, bestScore, bestCrowd -- floor under the new spot only matters while there is floor under us (up in the air there is none anywhere) local needGround = w.ground ~= nil and w.ground(here) -- a swarm leaves no spot that is 'clear' by the normal margin: the next best thing is the spot whose nearest mob is -- farthest away, as long as that is still outside a swing's reach local fallbackPoint, fallbackNearest, fallbackCrowd for _, distance in ipairs({ base, base + 12, base + 24, base + 40 }) do for _, angle in ipairs(STEP_ANGLES) do local point = here + rotateY(away, angle) * distance if not Kit.IsLethal(point) and (not needGround or w.ground(point)) then local nearest, crowd = math.huge, 0 for _, m in ipairs(w.mobs) do if not m.class.Pinata and math.abs(m.pos.Y - here.Y) <= 30 then local d = (m.pos - point).Magnitude nearest = math.min(nearest, d) if d < 18 then crowd += 1 end end end if nearest >= clear then local score = crowd * 10 - math.min(nearest, 40) * 0.6 + math.abs(math.abs(angle) - 80) * 0.02 + distance * 0.03 + swingExposureAt(w, point) * 25 if not bestScore or score < bestScore then bestPoint, bestScore, bestCrowd = point, score, crowd end elseif nearest >= (opts.minClear or 7.5) and (not fallbackNearest or nearest > fallbackNearest) then fallbackPoint, fallbackNearest, fallbackCrowd = point, nearest, crowd end end end end if not bestPoint and fallbackPoint then return fallbackPoint, fallbackCrowd end return bestPoint, bestCrowd end -- a spot well outside the blast of every explosive zombie (they run at ~15 studs/s and go off in chains; two blasts at 9 and 15 studs -- took 100 health in a tenth of a second): level with us, floor under it if there is floor under us, few mobs around it, no skeleton -- line, and as far from the explosives as the rings allow. Nil when nowhere is better than here. local function boomSpot(w, danger) local here = w.me.pos local booms, nearest = {}, nil for _, m in ipairs(w.mobs) do if m.class.Explosive and math.abs(m.pos.Y - here.Y) <= 60 then booms[#booms + 1] = m if not nearest or m.dist3 < nearest.dist3 then nearest = m end end end if not nearest then return nil end local function boomGap(point) local gap = math.huge for _, m in ipairs(booms) do gap = math.min(gap, (m.pos - point).Magnitude) end return gap end local hereGap = boomGap(here) local away = flat(here - nearest.pos) away = away.Magnitude > 0.5 and away.Unit or Vector3.new(1, 0, 0) local needGround = w.ground ~= nil and w.ground(here) local bestPoint, bestScore for _, distance in ipairs({ danger + 6, danger + 18, danger + 34, danger + 54 }) do for _, angle in ipairs(STEP_ANGLES) do local point = here + rotateY(away, angle) * distance if not Kit.IsLethal(point) and (not needGround or w.ground(point)) then local gap = boomGap(point) if gap > hereGap + 6 then local crowd = 0 for _, m in ipairs(w.mobs) do if not m.class.Pinata and not m.class.Explosive and (m.pos - point).Magnitude < 14 then crowd += 1 end end -- (a gap past the danger distance is what matters; beyond it only the crowd and the lines count) local score = crowd * 10 + swingExposureAt(w, point) * 25 + shooterSightAt(w, point) * 8 - math.min(gap, danger + 20) * 1.5 + distance * 0.03 if not bestScore or score < bestScore then bestPoint, bestScore = point, score end end end end end return bestPoint, nearest end -- where to shoot the Chicken Trojan from: in the air and about 34 studs off so the horde it releases has to climb and run, -- but only from a spot with a clear line to it (the arena has stalactites and the like that eat bullets), and not from -- a spot that already failed to damage it local PINATA_HOLDS = { 46, 38, 54, 30, 16, 0 } local PINATA_ANGLES = { 0, 40, -40, 80, -80, 120, -120, 160, -160 } local function pinataSpot(w, mem, boom) local away = flat(w.me.pos - boom.pos) away = away.Magnitude > 0.1 and away.Unit or Vector3.new(0, 0, 1) local target = boom.pos + Vector3.new(0, 1.2, 0) local heights = { rage.TrojanHeight or 22, 10, 30, 0 } -- a launcher's blast would reach us from the close spots (and from the one on top of it) local minHold = (w.gun and w.gun.Projectile) and ((w.gun.Projectile.Radius or 14) + 3) or 0 local bestPoint, bestScore for heightIndex, height in ipairs(heights) do for holdIndex, hold in ipairs(PINATA_HOLDS) do if hold < minHold then continue end for _, angle in ipairs(hold == 0 and { 0 } or PINATA_ANGLES) do local point = boom.pos + rotateY(away, angle) * hold + Vector3.new(0, height, 0) if not Kit.IsLethal(point) and (not w.sight or w.sight(point + Vector3.new(0, 1.5, 0), target)) then local score = exposureAt(point, boom, w.mobs) * 10 + (heightIndex - 1) * 4 + (holdIndex - 1) * 2 + math.abs(angle) * 0.01 -- (when it is about to pop, standing on it means the horde spawns on us: a close spot only if nothing farther can see it) if boom.hp and boom.hp <= (rage.PinataPopHp or 100) and hold < 30 then score += 30 end for _, bad in ipairs(mem.badShootSpots or {}) do if (bad - point).Magnitude < 10 then score += 40 end end if not bestScore or score < bestScore then bestPoint, bestScore = point, score end end end end -- a spot at the preferred height is good enough, the lower ones are only there for when it is all blocked if bestPoint and heightIndex == 1 then break end end return bestPoint end -- the plan R.Snipe fires: a launcher's shell needs its arc, a hitscan gun just a line local function snipePlan(w, target, safe) return { Target = target, Slot = w.gun.Slot, Item = w.gun.Item, Safe = safe or 0, Projectile = w.gun.Projectile, Fast = w.gun.Fast, } end -- Grenade Launcher: the pack to put a shell into. A shell does 45 on a direct hit and 45 more to everything within 14 -- studs of the blast, so the aim point is the spot, where the pack will be when the shell lands, that catches the most -- mobs, far enough from us (radius + 3) that the blast cannot reach us. local function bombardPlan(w, mem) local gun = w.gun if rage.Launcher == false or not gun or not gun.Projectile then return nil end local radius = gun.Projectile.Radius or 14 local safe = radius + 3 local reach = rage.LauncherRange or 70 local hpPct = w.me.hp / math.max(w.me.maxHp, 1) * 100 local crowd = 0 for _, other in ipairs(w.mobs) do if other.dist3 <= 45 and not other.class.Pinata then crowd += 1 end end local hurt = hpPct < (rage.LauncherLowHp or 50) local crowded = crowd >= (rage.LauncherCrowd or 10) local need = rage.LauncherMin or 4 -- hurt, or a crowd on top of us: the fists cost health, so a smaller pack is worth a shell too if hurt then need = math.min(need, 2) elseif crowded then need = math.min(need, 3) end -- the last shells are for the horde: until then they are only fired when hurt or in a crowd if (gun.Ammo or 0) + (gun.Reserve or 0) <= (rage.LauncherReserve or 8) and not hurt and not crowded then return nil end mem.skip = mem.skip or {} local best, bestValue, bestPoint local options = {} for _, candidate in ipairs(w.mobs) do local benched = (mem.skip[candidate.rec.Model] and mem.skip[candidate.rec.Model] > w.now) or (Kit.Reviving[candidate.rec.Model] and Kit.Reviving[candidate.rec.Model] > w.now) if not benched and not candidate.class.Pinata and not candidate.class.Avoid and candidate.dist3 >= safe and candidate.dist3 <= reach then local flight = candidate.dist3 / gun.Projectile.Speed local center = candidate.pos + Vector3.new(candidate.vel.X, 0, candidate.vel.Z) * flight local value = 0 for _, other in ipairs(w.mobs) do if not other.class.Pinata and not other.class.Avoid then local predicted = other.pos + Vector3.new(other.vel.X, 0, other.vel.Z) * (other.dist3 / gun.Projectile.Speed) if (predicted - center).Magnitude <= radius - 2 then -- the ones that hurt count double: the shell is worth most on them -- a brute is worth a shell on its own: every swing of it costs 40 health and the fists need nine hits -- (a skeleton too: its bones cost 25 a throw from 30-88 studs, killing the walkers around it does nothing about that) value += other.class.Boss and 8 or (other.class.CloseDodge and 4 or (other.class.Shooter and (rage.ShooterValue or 6) or ((other.class.Ranged or other.class.Explosive or (other.class.Threat or 0) >= 2) and 2 or 1))) end end end if value >= need then options[#options + 1] = { Mob = candidate, Value = value, Point = center } end end end if #options == 0 then return nil end table.sort(options, function(a, b) if a.Value ~= b.Value then return a.Value > b.Value end return a.Mob.dist3 < b.Mob.dist3 end) -- a pack behind a wall only gets the shell stopped by it (the shot is refused): of the best few, one with a clear line local pick = options[1] if w.shotLine then local from = w.me.pos + Vector3.new(0, 1.5, 0) for i = 1, math.min(#options, 4) do if w.shotLine(from, options[i].Point + Vector3.new(0, 1.2, 0)) then pick = options[i] break end end end best, bestValue, bestPoint = pick.Mob, pick.Value, pick.Point return best, { Aim = bestPoint + Vector3.new(0, 1.2, 0), Value = bestValue } end B.BombardPlan = bombardPlan -- the pinata being shot keeps the gun in hand and on target through dodges and hops: swapping to the fists and back -- costs a second and a shot every time, which is how the Trojan used to take forever to pop local function gunLockPlan(w, mem) if rage.GunLock == false or not mem.trojanGo or mem.pinataNoGun or not w.gun or (w.gun.Ammo or 0) + (w.gun.Reserve or 0) <= 0 then return nil end for _, m in ipairs(w.mobs) do if m.class.Pinata then return snipePlan(w, m, 0), m end end return nil end local function evadeSpot(w, dodging, dodgeSet) local range = rage.EvadeRange or 12 -- somebody with a longer swing (a brute) sets the distance the destination has to keep from everyone for _, attacker in ipairs(dodging) do if attacker.class.Reach then range = math.max(range, attacker.class.Reach + 2) end end -- a flock or a crowd swinging at once: the next target's spot is inside the pack and straight up is where flyers -- go anyway, so step sideways and let them string out if rage.Strafe ~= false then local flyers = 0 for _, attacker in ipairs(dodging) do if attacker.class.Flyer then flyers += 1 end end if flyers >= 2 or #dodging >= (rage.StrafeMin or 3) then local spot = sideStep(w, { clear = range }) if spot then return spot, 'sideways' end end end local bestPoint, bestScore for _, m in ipairs(w.mobs) do if not dodgeSet[m] and not m.class.Pinata and not (m.class.Explosive and rage.ExplosivePolicy ~= 'Normal') then local point, exposure = bestSpot(w, m, w.mobs) if point then local clear = true for _, attacker in ipairs(dodging) do -- (a brute walks toward us while it swings, about 6 studs in the 0.6 s before the hit) if swingDistance(attacker, point) < ( attacker.class.Area and attacker.class.Area + 2 or (range + (attacker.class.Reach and (rage.BruteWalkMargin or 6) or 0)) ) then clear = false break end end local movesEnough = true for _, attacker in ipairs(dodging) do if attacker.class.Shooter and (point - w.me.pos).Magnitude < (rage.ShooterMoveMin or 7) then movesEnough = false break end end if clear and movesEnough and (not w.ground or w.ground(point)) and not Kit.IsLethal(point) then local score = (exposure or 0) * 10 + (point - w.me.pos).Magnitude * 0.03 + swingExposureAt(w, point, m) * 25 + shooterSightAt(w, point) * 8 if not bestScore or score < bestScore then bestPoint, bestScore = point, score end end end end end if bestPoint then return bestPoint, 'to the next target' end local areaAttackers = {} for _, attacker in ipairs(dodging) do if attacker.class.Area then areaAttackers[#areaAttackers + 1] = attacker end end if #areaAttackers > 0 then local away = Vector3.zero for _, attacker in ipairs(areaAttackers) do away += flat(w.me.pos - attacker.pos) end away = away.Magnitude > 0.1 and away.Unit or Vector3.new(1, 0, 0) local need = 0 for _, attacker in ipairs(areaAttackers) do need = math.max(need, attacker.class.Area + 4) end local bestWay, bestWayScore for _, angle in ipairs({ 0, 40, -40, 80, -80, 120, -120, 160 }) do local direction = rotateY(away, angle) local point = w.me.pos + direction * need local safeFromAll = true for _, attacker in ipairs(areaAttackers) do if swingDistance(attacker, point) < attacker.class.Area + 2 then safeFromAll = false break end end if safeFromAll and (not w.ground or w.ground(point)) and not Kit.IsLethal(point) then local score = math.abs(angle) * 0.01 + exposureAt(point, nil, w.mobs) if not bestWayScore or score < bestWayScore then bestWay, bestWayScore = point, score end end end if bestWay then return bestWay, 'out of the blast' end end -- nowhere clear on this floor plane: a sideways step still beats going up, which flyers just follow (a lone walker -- cannot reach 13 studs up, so that stays the answer for it) local flying = false for _, attacker in ipairs(dodging) do if attacker.class.Flyer then flying = true break end end if rage.Strafe ~= false and (flying or #dodging >= 2) then local spot = sideStep(w, { clear = range - 4 }) if spot then return spot, 'sideways' end end -- a brute walks toward us while it swings, and the room above is often gone (the floor above, floor 11's lava sheet, which -- clamp a lift to nothing: a brute then hit three times in a row): a long step sideways keeps us out of its reach local walker = false for _, attacker in ipairs(dodging) do if attacker.class.Reach then walker = true break end end if walker and rage.Strafe ~= false then local spot = sideStep(w, { clear = range + (rage.BruteWalkMargin or 6) }) if spot then return spot, 'sideways' end end -- never climb out of the arena: with no floor under us already, 'up' only goes further from the fight if w.ground and not w.ground(w.me.pos) then return w.me.pos + Vector3.new(0, -(rage.EvadeLift or 13), 0), 'down' end -- a longer swing (brute) needs a higher lift than the usual 13 to end up outside its reach local lift = rage.EvadeLift or 13 for _, attacker in ipairs(dodging) do if attacker.class.Reach then local gap = flat(attacker.pos - w.me.pos).Magnitude lift = math.max(lift, math.min(22, math.sqrt(math.max(0, range * range - gap * gap)) + 0.5)) end end return w.me.pos + Vector3.new(0, lift, 0), 'up' end -- while nested, a dodge stays up there: any spot around the nest point that is clear of every attacker local function nestEvadeSpot(w, dodging, nest) local range = rage.EvadeRange or 12 local here = w.me.pos -- flyers follow a nest up, and a flock swings from every side at once: the sideways step works up here too if rage.Strafe ~= false then local flyers = 0 for _, attacker in ipairs(dodging) do if attacker.class.Flyer then flyers += 1 end end if flyers >= 2 or #dodging >= (rage.StrafeMin or 3) then local spot = sideStep(w, { clear = range }) if spot then return spot, 'sideways' end end end local candidates = { nest.point, nest.point + Vector3.new(0, 14, 0) } -- a point above us only while we are not already well above the nest point (no creeping up) if here.Y < nest.point.Y + 12 then candidates[#candidates + 1] = here + Vector3.new(0, rage.EvadeLift or 13, 0) end for _, angle in ipairs({ 0, 45, 90, 135, 180, -135, -90, -45 }) do local out = rotateY(Vector3.new(1, 0, 0), angle) candidates[#candidates + 1] = nest.point + out * 16 candidates[#candidates + 1] = nest.point + out * 16 + Vector3.new(0, 10, 0) end local bestPoint, bestScore for _, point in ipairs(candidates) do local nearest = math.huge for _, attacker in ipairs(dodging) do nearest = math.min(nearest, swingDistance(attacker, point)) end if nearest >= range and not Kit.IsLethal(point) then local score = exposureAt(point, nil, w.mobs) * 10 + (point - here).Magnitude * 0.03 if not bestScore or score < bestScore then bestPoint, bestScore = point, score end end end if bestPoint then return bestPoint, 'inside the nest' end -- no creeping upwards: already well above the nest point, the answer is the nest point itself if here.Y > nest.point.Y + 15 then return nest.point, 'back to the nest' end return here + Vector3.new(0, rage.EvadeLift or 13, 0), 'up' end -- ----------------------------------------------------------------- grenade -- 75 damage in a 16 stud radius kills every plain zombie it catches, every 30 s. The throw is aimed at where -- the pack will be when the fuse runs out (they run at us), far enough away that the blast cannot reach us. local THROW_SPEED, THROW_GRAVITY = 100, 196.2 * 0.375 -- pitch (radians) that drops a shell fired at `speed` with `gravity` on a point `distance` away horizontally and `rise` -- higher (the flat low arc); the defaults are the grenade's local function throwPitch(distance, rise, speed, gravity) speed, gravity = speed or THROW_SPEED, gravity or THROW_GRAVITY if gravity < 1e-6 then return distance >= 1 and math.atan2(rise, distance) or nil end local speedSquared = speed * speed local disc = speedSquared * speedSquared - gravity * (gravity * distance * distance + 2 * rise * speedSquared) if disc < 0 or distance < 1 then return nil end return math.atan((speedSquared - math.sqrt(disc)) / (gravity * distance)) end -- the unit direction to fire in from `origin` to land on `target`, nil when it is out of range local function launchDirection(origin, target, speed, gravity) local offset = target - origin local distance = flat(offset).Magnitude if distance < 1 then return nil end local pitch = throwPitch(distance, offset.Y, speed, gravity) if not pitch then return nil end return flat(offset).Unit * math.cos(pitch) + Vector3.new(0, math.sin(pitch), 0) end B.LaunchDirection = launchDirection local function grenadePlan(w, mem, fromNest) if rage.Grenade == false or not w.grenade then return nil end local info = w.grenade.Info or {} if w.now - (mem.grenadeAt or -100) < (info.Cooldown or 30) + 1 then return nil end local radius = (info.ExplosionRadius or 16) - 2 -- the throw is cooked so that the fuse runs out as the grenade lands: the pack only has to be predicted over the flight (and -- the aim follows it while the grenade cooks) local lead = rage.GrenadeLead or 1.0 -- nobody close enough to hurt us while we fiddle with the item for _, m in ipairs(w.mobs) do if m.dist3 < (rage.GrenadeSafe or 9) then return nil end end local best, bestCount, bestModels for _, candidate in ipairs(w.mobs) do local center = candidate.pos + Vector3.new(candidate.vel.X, 0, candidate.vel.Z) * lead local distance = fromNest and (center - w.me.pos).Magnitude or flat(center - w.me.pos).Magnitude if distance >= radius + 5 and distance <= 75 and (not fromNest or flat(center - w.me.pos).Magnitude >= 8) then local count = 0 local models = {} for _, other in ipairs(w.mobs) do local predicted = other.pos + Vector3.new(other.vel.X, 0, other.vel.Z) * lead if (predicted - center).Magnitude <= radius and not other.class.Boss then count += 1 models[other.rec.Model] = true end end local wanted = fromNest and (rage.NestGrenadeMin or 5) or (rage.GrenadeMin or 4) -- right after the pinata pops the horde is still coming out of it: the one grenade waits for a big cluster if mem.hordeAt and w.now - mem.hordeAt < (rage.HordeWindow or 7) then wanted = math.max(wanted, rage.HordeGrenadeMin or 8) end if count >= wanted and (not bestCount or count > bestCount) then best, bestCount, bestModels = center, count, models end end end if not best then return nil end return { Slot = w.grenade.Slot, Item = w.grenade.Item, Point = best, Count = bestCount, Models = bestModels, Fuse = info.DetonateDelay or 2, Cook = info.CanCook == true, Radius = info.ExplosionRadius or 16, } end B.GrenadePlan = grenadePlan -- ------------------------------------------------------------------- nest -- A horde, or being hurt in a crowd: go far above everything that walks. Walkers cannot reach that high and the -- ranged ones (brutes, skeletons) are out of range, so grenades and the gun do the killing from up there and the -- fists only deal with what flies up to us. local function nestOf(w, mem, hpPct) if rage.Nest == false then mem.nest = nil return nil end local near, grounded, sum = 0, 0, Vector3.zero local allSum = Vector3.zero local hpSum, reachers, flyersNear, shooters = 0, 0, 0, 0 for _, m in ipairs(w.mobs) do -- mobs on another floor cannot be reached from here and cannot reach us: they are no horde if m.dist3 <= 90 and not m.class.Pinata and math.abs(m.pos.Y - w.me.pos.Y) <= 50 then near += 1 allSum += m.pos hpSum += m.hp or 75 if m.class.Shooter and m.dist3 <= 70 then shooters += 1 end if m.class.Flyer and m.dist3 <= 60 then flyersNear += 1 end -- what can still hurt someone hovering over the pack: flyers, anything with a ranged or area attack if m.class.Flyer or m.class.Ranged or m.class.Explosive or m.class.Boss or m.class.Charger then reachers += 1 end if not m.class.Flyer then grounded += 1 sum += m.pos end end end local now = w.now if mem.nest then -- a pile of plain walkers cannot touch someone hovering 6 studs over them: the fists do that job from there, -- sitting 40 studs up only waits for a grenade if near <= (rage.NestExit or 8) or reachers < (rage.NestThreats or 3) or now - mem.nest.since > (rage.NestMax or 240) then mem.nest = nil return nil end -- a flock follows a nest up and swings at it from every side: it is no refuge from flyers, the sideways steps -- and the aura are -- bones reach a nest from 30 studs and more (floor 12: dead up there with seven skeletons around) if shooters > (rage.NestMaxShooters or 1) then mem.nest = nil mem.nestBanUntil = now + (rage.NestShooterBan or 40) return nil end if flyersNear > (rage.NestMaxFlyers or 3) + 2 then mem.nest = nil mem.nestBanUntil = now + (rage.NestFlyerBan or 25) return nil end -- sitting up there is only worth it while something dies: a nest that has not killed or hurt anything for a -- while is a stalemate, so leave it and fight (sideways steps and close-first targeting) for a minute local progress = mem.nest.progress if not progress or hpSum < progress.hp - 15 or near < progress.near then mem.nest.progress = { at = now, hp = hpSum, near = near } elseif now - progress.at > (rage.NestPatience or 20) then mem.nest = nil mem.nestBanUntil = now + (rage.NestBan or 75) Kit.Notify('nest', 'Rage', 'nothing dies up here: coming down to fight', 3, true) return nil end else local horde = near >= (rage.NestHorde or 16) local panic = hpPct < (rage.NestBelow or 45) and near >= (rage.NestPanicMobs or 7) if not (horde or panic) or grounded < 3 or reachers < (rage.NestThreats or 3) or (mem.nestBanUntil or 0) > now or flyersNear > (rage.NestMaxFlyers or 3) or shooters > (rage.NestMaxShooters or 1) then return nil end mem.nest = { since = now, reason = horde and 'horde' or 'low health', progress = { at = now, hp = hpSum, near = near }, } end local center = grounded > 0 and sum / grounded or (near > 0 and allSum / near or mem.nest.center or w.me.pos) mem.nest.center = center -- stay on the side we are already on, 20-odd studs off to the side and well above, but only where the bullets get -- through: 40 studs up is inside the next floor's slab, where nothing can be shot (the 'nothing dies up here' stalls) local side = flat(w.me.pos - center) side = side.Magnitude > 1 and side.Unit or Vector3.new(1, 0, 0) local aim = center + Vector3.new(0, 1.2, 0) local point for _, height in ipairs({ rage.NestHeight or 40, 30, 22, 15 }) do for _, offset in ipairs({ rage.NestOffset or 22, 12 }) do local candidate = center + side * offset + Vector3.new(0, height, 0) if (not w.ceilingY or candidate.Y <= w.ceilingY) and not Kit.IsLethal(candidate) and (not w.sight or w.sight(candidate + Vector3.new(0, 1.5, 0), aim)) then point = candidate break end end if point then break end end if not point then -- no spot up there with a line to the pack: a nest that cannot shoot is no nest mem.nest = nil mem.nestBanUntil = now + (rage.NestBlindBan or 20) return nil end mem.nest.point = point return { Center = center, Point = point, Near = near, Grounded = grounded } end -- pure decision for rage mode function B.DecideRage(w, mem) local act = { State = 'idle', Reason = '' } local me, now = w.me, w.now local hpPct = me.hp / math.max(me.maxHp, 1) * 100 -- attacks that already started: the hit lands about half a second later on whoever is in reach then local dodging, dodgeSet = {}, {} if rage.Evade ~= false then local windup = w.windup or { Min = 0.38, Max = 0.95 } local hurt = hpPct <= (rage.EvadeBelow or 85) -- the swings we can still land before each hit are shared out, earliest hit first: a weak mob is only worth -- finishing while there is time for everyone ahead of it in the queue too, the rest are dodged local queue = {} for _, m in ipairs(w.mobs) do local a = m.attack local own = a and w.windupFor and w.windupFor(a.Id) or windup -- below the health threshold everything is dodged; a brute's swing (40) is dodged at any health if a and (hurt or (m.class.CloseDodge and rage.DodgeBrutes ~= false) or (m.class.AlwaysDodge and rage.DodgeReapers ~= false)) and now - a.At <= own.Max + (own.Slack or 0.15) then if m.class.Name == 'Ghost' and m.dist3 <= (rage.GhostStand or 9) and (m.hp or 30) <= 60 then -- (hopping away from a ghost only brings it along: one swing kills it) m.finishNow = true else queue[#queue + 1] = { mob = m, deadline = a.At + own.Min } end end end table.sort(queue, function(x, y) return x.deadline < y.deadline end) local used = 0 for _, entry in ipairs(queue) do local m = entry.mob -- a ranged mob is hunted rather than dodged, except a brute near us. A brute is plain melee (measured frame by -- frame: it plants itself, the swing lands 0.6 s after the announcement on whoever is within about 6-7 studs, -- 40 damage, a swing every ~1.4 s; the 'hits from 19 studs' seen earlier were distances taken after the bot had -- already hopped away). Punching it point blank cost 120 health in two seconds, so its swing is dodged like any -- other and the bot steps back in as soon as the swing has landed (Windup.ById Slack 0.05). local closeBrute = m.class.CloseDodge and rage.DodgeBrutes ~= false and m.dist3 <= (rage.BruteDodgeRange or 24) if m.class.Ranged and rage.RangedFirst ~= false and not closeBrute and not m.class.Shooter and exposureAt(m.pos, m, w.mobs) <= 3 then m.finishNow = true continue end local budget = math.max(0, math.floor((entry.deadline - now - 0.12) / 0.2)) local need = m.hp and math.ceil(m.hp / 30) or math.huge if m.dist3 <= 70 and used + need <= budget then used += need m.finishNow = true else dodging[#dodging + 1] = m dodgeSet[m] = true end end end -- mobs standing inside an attacker's circle are not worth hopping to until its swing is over either local zone = {} if #dodging > 0 then local keepOut = (rage.EvadeRange or 12) - 2 for _, m in ipairs(w.mobs) do if not dodgeSet[m] and not (m.class.Ranged and rage.RangedFirst ~= false) then for _, attacker in ipairs(dodging) do if attacker.class.Area and flat(m.pos - attacker.pos).Magnitude < attacker.class.Area or (m.pos - attacker.pos).Magnitude < keepOut then zone[m] = true break end end end end end -- a fight that goes nowhere (life 26: a lone brute standing in the arena's player barrier, swinging at nothing, dodged for a minute -- while the kill count stood still): after StallShootAfter seconds without the mobs' health going down, the gun finishes the few -- that are left from range do local count, total = 0, 0 for _, m in ipairs(w.mobs) do if not m.class.Pinata and not m.class.Avoid then count += 1 total += m.hp or 75 end end local pr = mem.fightProgress if not pr or count == 0 or count < pr.count or total < pr.total - 5 then mem.fightProgress = { count = count, total = total, at = now } mem.stallShoot = false elseif count <= (rage.StallShootMax or 5) and now - pr.at > (rage.StallShootAfter or 20) then if not mem.stallShoot then Kit.Notify('stall', 'Rage', 'the fight is going nowhere: the gun takes over', 2, true) end mem.stallShoot = true end end -- Chicken Trojan: breaking it releases a horde, so be ready first (grenade back, health high), then break it from range local pinata, others = nil, 0 for _, m in ipairs(w.mobs) do if m.class.Pinata then pinata = m else others += 1 end end -- the moment it pops: the horde starts to come out of it if pinata then mem.pinataSeenAt = now mem.pinataPos = pinata.pos -- a second pinata (another floor or round): the horde bookkeeping starts again if mem.hordeAt and now - mem.hordeAt > 30 then mem.hordeAt, mem.hordeSpot, mem.hordeCount, mem.hordeGrowAt = nil, nil, nil, nil end elseif mem.pinataSeenAt and not mem.hordeAt and now - mem.pinataSeenAt < 3 and (mem.trojanGo or rage.HordeWait ~= false) then mem.hordeAt = mem.pinataSeenAt mem.hordeStepPending = rage.HordeWait == false Kit.Notify('horde', 'Rage', 'the pinata popped: the horde is out', 3, true) end -- the horde comes out of the pinata over several seconds (life: 10, 13, 18, 20 mobs in the first four) and a bot that attacks while it is -- still spawning is surrounded by the rest (user: 'wait for all the mobs to spawn then start attacking'). So after the pop nothing is -- attacked: the bot goes far from the pop point and a little up, and waits until the mob count has stopped growing for HordeSettle -- seconds (at least HordeMinWait after the pop, at most HordeWaitMax). A swing that has started (dodging) is still answered first. -- Only the ground horde waits: the flyers in it (reapers hit for 50, ghosts) are in the air within about three seconds and are fought as they -- arrive (a bot that stood and waited for them was dead four seconds after the pop). local hordeHold, hordeCount, hordeSince = false, 0, 0 if rage.HordeWait ~= false and mem.hordeAt and not pinata and #dodging == 0 then hordeSince = now - mem.hordeAt hordeCount = #w.mobs if hordeCount > (mem.hordeCount or 0) then mem.hordeCount, mem.hordeGrowAt = hordeCount, now end local settled = hordeSince >= (rage.HordeMinWait or 4) and now - (mem.hordeGrowAt or mem.hordeAt) >= (rage.HordeSettle or 2.5) hordeHold = hordeSince < (rage.HordeWaitMax or 20) and not settled if hordeHold and mem.hordeSpot == nil then mem.hordeSpot = hordeWaitSpot(w, mem) or false end end if mem.hordeStepPending then mem.hordeStepPending = nil if rage.HordeStep ~= false then local spot = sideStep( w, { distance = rage.HordeStepDistance or 44, clear = ((w.gun and w.gun.Projectile and w.gun.Projectile.Radius) or 10) + 4, } ) if spot then mem.stepUntil = now + (rage.StepHold or 1.2) + 1 act.State, act.Evade, act.Teleport = 'evade', true, spot act.Reason = 'the horde is out: moving to bombard range' act.Face = (w.mobs[1] and w.mobs[1].pos or spot) + Vector3.new(0, 1.2, 0) return act end end end -- explosive zombies: nothing is worth standing within reach of one, a plain zombie on top of us included (lives 18, 20 and 21 ended -- with 150, 150 and 100 health gone in under two seconds, each time among explosive zombies going off together). Only while there is a -- way to kill them from range: a gun with ammo, else they are fought by the old rules. if rage.ExplosiveFlee ~= false and rage.ExplosivePolicy ~= 'Normal' then local danger = rage.ExplosiveDanger or 28 local shooter = w.gun ~= nil and (w.gun.Ammo or 0) + (w.gun.Reserve or 0) > 0 local close if shooter then for _, m in ipairs(w.mobs) do if m.class.Explosive and m.dist3 < danger and math.abs(m.pos.Y - me.pos.Y) <= 60 then close = m break end end end if close then -- not for ever: five hops in eight seconds that did not thin them out leave it to the fight rules for a while local recent = 0 mem.boomFlees = mem.boomFlees or {} for i = #mem.boomFlees, 1, -1 do if now - mem.boomFlees[i] > 8 then table.remove(mem.boomFlees, i) else recent += 1 end end if now >= (mem.boomQuietUntil or 0) and now - (mem.boomFleeAt or -9) >= (rage.ExplosiveFleeGap or 0.25) then if recent >= 5 then mem.boomQuietUntil = now + 5 mem.boomFlees = {} else local spot = boomSpot(w, danger) if spot then mem.boomFleeAt = now mem.boomFlees[#mem.boomFlees + 1] = now mem.gren = nil Kit.BoomFlees = (Kit.BoomFlees or 0) + 1 act.State, act.Evade, act.Teleport = 'evade', true, spot act.Reason = string.format('explosive zombie %dm away: moving out of its blast', math.floor(close.dist3)) act.Face = close.pos + Vector3.new(0, 1.2, 0) return act end end end end end if pinata and others == 0 and not mem.trojanGo then local info = w.grenade and w.grenade.Info or nil local grenadeIn = 0 if w.grenade then grenadeIn = math.max(0, ((info and info.Cooldown) or 30) + 1 - (now - (mem.grenadeAt or -100))) end mem.trojanSince = mem.trojanSince or now local waited = now - mem.trojanSince local healKnown = false for _, drop in ipairs(w.drops or {}) do if drop.Label == '' or Kit.IsHealLabel(drop.Label) then healKnown = true end end if (grenadeIn <= 0 and hpPct >= (rage.TrojanMinHp or 80)) or waited >= (rage.TrojanWait or 75) then mem.trojanGo = true elseif not (healKnown and hpPct < 95) then act.State = 'wait' act.Reason = string.format( 'getting ready for the Chicken Trojan: grenade back in %ds, health %d%% (needs %d%%)', math.ceil(grenadeIn), math.floor(hpPct), rage.TrojanMinHp or 80 ) return act end end -- the Giant Chombie: its leap-slam lands on our position (SlamEffect announces where and when), and it lies on the ground for -- a moment after the third one. It is shot from range the rest of the time and punched only in that window. local bossWindow = false for _, slam in ipairs(w.slamLog or w.slams or {}) do if slam.Vulnerable and now >= slam.HitAt - 0.2 and now <= slam.HitAt + (rage.BossWindow or 6) then bossWindow = true end end mem.bossWindow = bossWindow local gunReady = rage.ExplosivePolicy == 'Snipe' and w.gun ~= nil and (w.gun.Ammo or 0) + (w.gun.Reserve or 0) > 0 and not mem.pinataNoGun local normal, explosive = {}, {} for _, m in ipairs(w.mobs) do if m.class.Pinata and not (mem.trojanGo and not gunReady) then -- held back: shot from range once we are ready, punched only when there is no gun elseif mem.nest and (not m.class.Flyer or m.dist3 > 18) then -- in the nest: walkers are left to the grenade and the gun elseif m.class.Boss and not bossWindow and w.gun and (w.gun.Ammo or 0) + (w.gun.Reserve or 0) > 0 then -- shot from range (the snipe step below), not punched elseif dodgeSet[m] or zone[m] then -- mid-swing, or next to someone mid-swing: left alone until the swing is over elseif m.class.Explosive then if rage.ExplosivePolicy ~= 'Skip' then explosive[#explosive + 1] = m end else normal[#normal + 1] = m end end if hordeHold then local fliers = {} for _, m in ipairs(normal) do if m.class.Flyer then fliers[#fliers + 1] = m end end normal, explosive = fliers, {} if #fliers == 0 then act.State = 'wait' act.Reason = string.format( 'horde: waiting for it to finish spawning (%d mobs, %ds)', hordeCount, math.floor(hordeSince) ) if mem.hordeSpot and (me.pos - mem.hordeSpot).Magnitude > 8 then act.Teleport, act.Force = mem.hordeSpot, true end act.Face = (mem.pinataPos or me.pos) + Vector3.new(0, 1.2, 0) return act end end for _, m in ipairs(w.mobs) do -- a pinata we are not ready for yet does not count as a fight going on if not (m.class.Pinata and (not mem.trojanGo or m.dist3 > 70)) then mem.lastMobAt = now break end end local pool = normal local explosivePhase = false if rage.ExplosivePolicy == 'Normal' then pool = {} for _, m in ipairs(w.mobs) do if not dodgeSet[m] and not zone[m] then pool[#pool + 1] = m end end elseif #pool == 0 and #explosive > 0 and ( hpPct >= rage.MinHpExplosive or (mem.explosiveWaitSince and now - mem.explosiveWaitSince > (rage.ExplosiveWaitMax or 25)) ) then pool = explosive explosivePhase = true end -- the wait for health only lasts so long (no gun, no heals: waiting for 80% never ends) if #explosive == 0 or #normal > 0 then mem.explosiveWaitSince = nil end -- remember a place no mob can reach (where the run started, away from every mob) to fall back to if not mem.safeSpot then local crowded = false for _, m in ipairs(w.mobs) do if m.dist3 < 60 then crowded = true break end end if not crowded then mem.safeSpot = me.pos end end -- low health: go back to the safe spot and stop; health does not come back if mem.safeSpot and rage.RetreatPct and rage.RetreatPct > 0 and hpPct < rage.RetreatPct then if (me.pos - mem.safeSpot).Magnitude > 4 then act.State, act.Reason, act.Teleport = 'retreat', string.format('health %d%%: back to the safe spot', math.floor(hpPct)), mem.safeSpot else act.State, act.Reason, act.Stop = 'stop', string.format('health %d%%: stopped on the safe spot', math.floor(hpPct)), true end return act end -- boss slams: the announcement carries the impact point and radius, so just be somewhere else when it lands for _, slam in ipairs(w.slams or {}) do local inside = flat(me.pos - slam.Pos).Magnitude < slam.Radius + 3 if inside and now <= slam.HitAt + 0.35 then local away = flat(me.pos - slam.Pos) away = away.Magnitude > 0.1 and away.Unit or Vector3.new(1, 0, 0) local clearance = slam.Radius + 7 local bestPoint, bestScore for _, angle in ipairs({ 0, 30, -30, 60, -60, 90, -90, 135, -135, 180 }) do local point = slam.Pos + rotateY(away, angle) * clearance + Vector3.new(0, me.pos.Y - slam.Pos.Y, 0) if (not w.ground or w.ground(point)) and not Kit.IsLethal(point) then local score = math.abs(angle) * 0.01 + (point - me.pos).Magnitude * 0.02 if not bestScore or score < bestScore then bestPoint, bestScore = point, score end end end mem.gren = nil act.State, act.Evade = 'evade', true act.Teleport = bestPoint or (me.pos + Vector3.new(0, 30, 0)) act.Reason = string.format( 'boss slam lands in %.1fs (radius %d)', math.max(0, slam.HitAt - now), math.floor(slam.Radius) ) return act end end -- too many on top of us: leave instead of trading blows. A step sideways, far from all of them, then only what -- arrives first is fought, so the crowd comes in strung out instead of all at once local lockPlan, lockMob = gunLockPlan(w, mem) mem.pressure = pressureOf(w) local escapeAt = rage.EscapeAt or 6 if hpPct < (rage.LowHpEscapePct or 45) then escapeAt = math.min(escapeAt, rage.LowHpEscapeAt or 2.5) end local escaping = rage.Escape ~= false and not mem.nest and mem.pressure >= escapeAt and now - (mem.escapeAt or -10) >= (rage.EscapeGap or 0.7) and (mem.escapeQuietUntil or 0) <= now local escapeTimes = mem.escapeTimes or {} mem.escapeTimes = escapeTimes if escaping then -- livelock guard: five escapes in eight seconds with nothing within 15 studs means the pressure comes from far -- away swings that stepping aside does not help against; stop stepping for a while and fight local recent = 0 for i = #escapeTimes, 1, -1 do if now - escapeTimes[i] > 8 then table.remove(escapeTimes, i) else recent += 1 end end -- ...and so does stepping away over and over while the crowd around us is not getting any smaller: that is not -- escaping, that is never fighting. Stand and fight (the grenade and the aura do the work) for a few seconds local crowdHp = 0 for _, m in ipairs(w.mobs) do if m.dist3 <= 40 then crowdHp += m.hp or 75 end end local progress = mem.escapeProgress if not progress or crowdHp < progress.hp - 20 then progress = { at = now, hp = crowdHp } mem.escapeProgress = progress end if (recent >= 5 and (w.mobs[1] == nil or w.mobs[1].dist3 > 15)) or (recent >= 4 and now - progress.at > 8) then mem.escapeQuietUntil = now + (rage.EscapeQuiet or 7) mem.escapeProgress = { at = now, hp = crowdHp } escaping = false end end if escaping then local spot = sideStep(w, { distance = rage.EscapeDistance or 30 }) if spot then mem.gren = nil escapeTimes[#escapeTimes + 1] = now mem.escapeAt, mem.stepUntil = now, now + (rage.StepHold or 1.2) Kit.Escapes = (Kit.Escapes or 0) + 1 act.State, act.Evade, act.Teleport = 'escape', true, spot act.Reason = string.format('pressure %.1f: stepping out of the crowd', mem.pressure) act.Face = (w.mobs[1] and w.mobs[1].pos or spot) + Vector3.new(0, 1.2, 0) if lockPlan then act.Snipe, act.Force, act.Face = lockPlan, true, lockMob.pos + Vector3.new(0, 1.2, 0) end return act end end if #dodging > 0 then local range = rage.EvadeRange or 12 local threatened = false for _, m in ipairs(dodging) do -- (a skeleton throws from up to 90 studs; 35 of 35 announced throws missed when the bot moved 6+ studs, 3 of 5 hit when it stood) if m.class.Shooter then if swingDistance(m, me.pos) < (rage.ShooterRange or 90) and (not w.sight or w.sight(m.pos + Vector3.new(0, 1.2, 0), me.pos + Vector3.new(0, 1.5, 0))) then threatened = true break end elseif swingDistance(m, me.pos) < swingReach(m, range) then threatened = true break end end local plural = #dodging == 1 and '' or 's' if threatened then mem.gren = nil local destination, how if mem.nest and mem.nest.point then destination, how = nestEvadeSpot(w, dodging, mem.nest) else destination, how = evadeSpot(w, dodging, dodgeSet) end act.State, act.Evade, act.Teleport = 'evade', true, destination act.Reason = string.format('dodging %d attack%s (%s)', #dodging, plural, how) act.Face = dodging[1].pos + Vector3.new(0, 1.2, 0) if how == 'sideways' then mem.stepUntil = now + (rage.StepHold or 1.2) end if lockPlan then act.Snipe, act.Force, act.Face = lockPlan, true, lockMob.pos + Vector3.new(0, 1.2, 0) end return act elseif #pool == 0 then act.State, act.Evade = 'evade', true act.Reason = string.format('letting %d attack%s pass', #dodging, plural) if lockPlan then act.Snipe, act.Force, act.Face = lockPlan, true, lockMob.pos + Vector3.new(0, 1.2, 0) end return act end end -- pickups that cannot wait, a drop is gone after 20 s: a heal when badly hurt, and a weapon crate while no rocket gun is held -- (every crate is another look at the Grenade Launcher / RPG). Not into the thick of the pack, unless there is nothing to lose. if rage.Collect and rage.Collect ~= 'Off' then local needHeal = hpPct < (rage.HealFirstBelow or 55) local hunting = rage.LauncherHunt ~= false and rage.ClaimWeapon ~= false and not (w.gun and w.gun.Projectile) if needHeal or hunting then local bestDrop, bestDistance for _, drop in ipairs(w.drops or {}) do local isCrate = drop.Label == 'Weapon' local wanted = (isCrate and hunting and drop.Prompt ~= nil) or (not isCrate and needHeal and (drop.Label == '' or Kit.IsHealLabel(drop.Label))) if wanted then local key = 'drop:' .. tostring(math.floor(drop.Pos.X)) .. ',' .. tostring(math.floor(drop.Pos.Z)) local blocked = mem.blocked[key] and mem.blocked[key] > now local distance = (drop.Pos - me.pos).Magnitude local tries = claimTriesOf(mem, key) local exposure = exposureAt(drop.Pos, nil, w.mobs) + swingExposureAt(w, drop.Pos) * 2 + shooterSightAt(w, drop.Pos) * (rage.CollectShooterWeight or 1.5) local boomClose = rage.ExplosiveFlee ~= false and boomNear(w, drop.Pos, rage.ExplosiveDanger or 28) if not blocked and not boomClose and (distance > 2.5 or (isCrate and tries < 3)) and distance <= (rage.CollectRange or 150) and (exposure <= 6 or hpPct < 25) and (not bestDistance or distance < bestDistance) then bestDrop, bestDistance = drop, distance drop.Key = key end end end if bestDrop then act.State = 'collect' if bestDrop.Label == 'Weapon' and bestDistance <= 6 then act.Claim = { Prompt = bestDrop.Prompt, Key = bestDrop.Key } act.Reason = 'claiming the weapon drop: before anything else' else act.Teleport, act.GoalKey = bestDrop.Pos, bestDrop.Key act.Reason = string.format( 'picking up %s (%dm), health %d%%: before anything else', bestDrop.Label ~= '' and bestDrop.Label or 'a heal', math.floor(bestDistance), math.floor(hpPct) ) end return act end end end -- a throw that is under way keeps going; otherwise look for a pack worth the grenade if mem.gren then act.State, act.Reason, act.Grenade = 'grenade', 'throwing the grenade', mem.gren.Plan return act end local nest = nestOf(w, mem, hpPct) if nest then -- 1. a pack worth the grenade, lobbed down from above local plan = grenadePlan(w, mem, true) if plan then mem.gren = { Stage = 'equip', T = now, Plan = plan } act.State, act.Reason, act.Grenade = 'grenade', string.format('nest: grenade on %d mobs', plan.Count), plan return act end -- anything flying at us is dealt with by the normal fight below local flyerClose = false for _, m in ipairs(w.mobs) do if m.class.Flyer and m.dist3 <= 14 then flyerClose = true break end end if not flyerClose then -- 2. the gun, ranged mobs first if w.gun and (w.gun.Ammo or 0) + (w.gun.Reserve or 0) > 0 then mem.skip = mem.skip or {} local pick, pickScore for _, m in ipairs(w.mobs) do local benched = (mem.skip[m.rec.Model] and mem.skip[m.rec.Model] > now) if not benched and not m.class.Pinata and not m.class.Flyer and m.dist3 <= 50 and (not w.gun.Projectile or m.dist3 >= (w.gun.Projectile.Radius or 14) + 3) then local score = m.dist3 - (m.class.Ranged and 100 or 0) - (m.class.Explosive and 40 or 0) if not pickScore or score < pickScore then pick, pickScore = m, score end end end if pick then local watch = mem.snipeWatch if not watch or watch.model ~= pick.rec.Model then mem.snipeWatch = { model = pick.rec.Model, hp = pick.hp, since = now } elseif pick.hp and watch.hp and pick.hp < watch.hp - 0.5 then watch.hp, watch.since = pick.hp, now elseif now - watch.since > 2.2 then mem.skip[pick.rec.Model] = now + 6 mem.snipeWatch = nil pick = nil end end if pick then act.State = 'snipe' act.Reason = string.format( 'nest (%s): shooting %s from %dm', mem.nest.reason or '?', pick.rec.Model.Name, math.floor(pick.dist3) ) act.Snipe = snipePlan(w, pick, 0) act.Face = pick.pos + Vector3.new(0, 1.2, 0) if (me.pos - nest.Point).Magnitude > 10 then act.Teleport = nest.Point end return act end end -- 3. nothing to throw or shoot: get to the nest and wait for the grenade to come back act.State = 'nest' act.Reason = string.format('nest (%s): %d mobs around, waiting for the grenade', mem.nest.reason or '?', nest.Near) if (me.pos - nest.Point).Magnitude > 10 then act.Teleport = nest.Point end return act end end -- kill order: flyers come first, the gun branch below waits while one is within FlyerFirstRange (life 45: two reapers, 50 each, hit -- while the launcher was shooting an explosive 44 m away). Not during the pinata phase, and not for longer than FlyerFirstMax in a row local flyerFirst = false if rage.KillOrder ~= false and not (pinata and mem.trojanGo) then mem.skip = mem.skip or {} local range = rage.FlyerFirstRange or 34 local found = false for _, m in ipairs(w.mobs) do if m.class.Flyer and m.dist3 <= range and not (mem.skip[m.rec.Model] and mem.skip[m.rec.Model] > now) then found = true break end end if not found then mem.flyerFirstSince = nil else mem.flyerFirstSince = mem.flyerFirstSince or now flyerFirst = now - mem.flyerFirstSince <= (rage.FlyerFirstMax or 15) end end -- explosive zombies: shoot them from outside their blast instead of punching them if rage.ExplosivePolicy == 'Snipe' and not flyerFirst and not w.gunBan and w.gun and (w.gun.Ammo or 0) + (w.gun.Reserve or 0) > 0 then mem.skip = mem.skip or {} local boom local relocate = false -- badly hurt: everything that is not on top of us is shot from a distance instead of punched (a punch costs health) local shootAll = rage.SnipeAll or mem.stallShoot or (rage.LowHpSnipe ~= false and not w.gun.Projectile and hpPct < (rage.LowHpSnipePct or 40)) or ( rage.HordeShoot ~= false and not w.gun.Projectile and mem.hordeAt ~= nil and now - mem.hordeAt < (rage.HordeShootFor or 300) ) for _, m in ipairs(w.mobs) do -- SnipeAll also shoots ordinary zombies (uses a lot of ammo: about 7 rounds each) local benched = (mem.skip[m.rec.Model] and mem.skip[m.rec.Model] > now) or (Kit.Reviving[m.rec.Model] and Kit.Reviving[m.rec.Model] > now) -- a brute costs 40 a swing (life 32 died hopping onto one at 75 hp, life 31 and 28 lost hp the same way): when hurt it is shot from range local hurtBrute = m.class.CloseDodge and hpPct < (rage.BruteGunBelow or 70) if ( m.class.Explosive or m.class.Snipe or m.class.Boss or hurtBrute or (m.class.Pinata and mem.trojanGo and not mem.pinataNoGun) or shootAll ) and not benched and not (m.class.Boss and bossWindow) then boom = m break end end -- nothing explosive or pinata to shoot: the launcher goes for the best pack local bombard if not boom and w.gun.Projectile then boom, bombard = bombardPlan(w, mem) end if boom then -- the one we shoot at has to keep losing health, or it is skipped for a few seconds local watch = mem.snipeWatch if not watch or watch.model ~= boom.rec.Model then mem.snipeWatch = { model = boom.rec.Model, hp = boom.hp, since = now } elseif boom.hp and watch.hp and boom.hp < watch.hp - 0.5 then watch.hp, watch.since = boom.hp, now elseif now - watch.since > (boom.class.Pinata and 4 or 2.2) and boom.dist3 < 60 then if boom.class.Pinata then -- the shots from here do nothing (something eats them): remember the spot and shoot from another mem.badShootSpots = mem.badShootSpots or {} mem.badShootSpots[#mem.badShootSpots + 1] = me.pos relocate = true mem.snipeWatch = { model = boom.rec.Model, hp = boom.hp, since = now } else mem.skip[boom.rec.Model] = now + 6 mem.snipeWatch = nil boom = nil end end end -- the Trojan takes a few shells and then often stops taking damage (the shells land somewhere it is not): after a few seconds -- without progress the gun is done with it, the fists pop it, and the shells that are left are there for the horde if boom and boom.class.Pinata and boom.hp then local gunTry = mem.pinataGun if not gunTry then gunTry = { BestHp = boom.hp, ProgressAt = now } mem.pinataGun = gunTry elseif boom.hp < gunTry.BestHp - 3 then gunTry.BestHp, gunTry.ProgressAt = boom.hp, now end if now - gunTry.ProgressAt > (rage.PinataGunPatience or 6) then mem.pinataNoGun = true Kit.Notify( 'pinata', 'Rage', 'the shells do nothing to the Trojan: the fists pop it, the shells are kept for the horde', 3, true ) boom = nil end end if boom and boom.dist3 <= (boom.class.Pinata and 600 or (rage.SnipeRange or 80)) then -- something plain already on top of us comes first. Not for the pinata: it is shot to the end, and anything -- that gets too close is stepped away from (escape and dodge keep the gun up while they hop) local plainClose = false for _, m in ipairs(w.mobs) do -- a swing the dodge step decided to beat by killing the attacker (finishNow) comes first too: the fight step does that, -- the gun would keep hopping and shooting while it lands local mustFinish = m.finishNow and (m.class.Flyer and m.dist3 <= (rage.FlyerLunge or 22) or m.dist3 <= 9) if not m.class.Explosive and m ~= boom and (m.dist3 < 6 or mustFinish) and not (boom.class.Pinata and rage.GunLock ~= false) then plainClose = true break end end if not plainClose then local safe = math.max(Kit.SnipeSafeDistance(), (boom.class.Area or 0) + 4) if boom.class.Boss then -- far enough that its leap-slam has to travel (it lands on our position; the dodge hops away while it is in the air) safe = math.max(safe, rage.BossHold or 34) end if w.gun.Projectile then safe = math.max(safe, (w.gun.Projectile.Radius or 14) + 3) end act.State = 'snipe' act.Reason = string.format( 'shooting the explosive zombie from %dm (blast %dm)', math.floor(boom.dist3), math.floor(Kit.BlastRadius()) ) act.Snipe = snipePlan(w, boom, safe) act.Face = bombard and bombard.Aim or (boom.pos + Vector3.new(0, 1.2, 0)) if bombard then -- the aim point already is where the pack will be: no extra lead act.Snipe.Lead = false act.Reason = string.format( 'grenade launcher: pack worth %d, %dm away', bombard.Value, math.floor(boom.dist3) ) end local lineClear = w.shotLine or w.sight local blocked = lineClear ~= nil and not lineClear(me.pos + Vector3.new(0, 1.5, 0), boom.pos + Vector3.new(0, 1.2, 0)) -- the shot itself was refused a moment ago (muzzle in a wall, arc into a wall): the same spot is no good if R.shotBlocked and now - R.shotBlocked.At < 1.5 and R.shotBlocked.Model == boom.rec.Model then blocked = true end -- standing still in a skeleton's line is how both lives ended (four bones in three seconds while sniping): with one in sight the -- shooting spot is changed every second, to a place out of their lines if there is one local exposedToShooters = rage.ShooterRelocate ~= false and shooterSightAt(w, me.pos) > 0 and now - (mem.relocateAt or -99) > (rage.ShooterRelocate or 0.9) if boom.class.Pinata then -- a spot with a clear line to it, high up and about 34 studs off (see pinataSpot) if boom.dist3 < safe + 8 or blocked or boom.dist3 > 55 or relocate then local spot = pinataSpot(w, mem, boom) if spot and (spot - me.pos).Magnitude > 3 then act.Teleport = spot elseif not spot and boom.dist3 > 55 then -- no spot passes the sight check at all (only decoration in the way): walk up to it anyway local away = flat(me.pos - boom.pos) away = away.Magnitude > 0.1 and away.Unit or Vector3.new(0, 0, 1) act.Teleport = boom.pos + away * PINATA_HOLDS[1] + Vector3.new(0, rage.TrojanHeight or 22, 0) end end elseif boom.dist3 < safe + 8 or blocked or exposedToShooters then -- too close or no clear line: go to a quiet point well outside the blast, with floor and a clear line local hold = math.min(46, safe + 18) local away = flat(me.pos - boom.pos) away = away.Magnitude > 0.1 and away.Unit or Vector3.new(0, 0, 1) local bestPoint, bestScore -- walls in the way (floors 7 and 8 are full of them): the search goes higher and closer until it finds a spot with a -- clear line; shooting down from above the walls is what usually works local heights = blocked and { 0, 12, 24, 36 } or { 0 } local holds = blocked and { hold, math.max(safe + 4, hold - 12) } or { hold } if not blocked or now - (mem.relocSearchAt or -9) >= (rage.RelocateGap or 0.25) then mem.relocSearchAt = now for _, lift in ipairs(heights) do for _, holdDistance in ipairs(holds) do for _, angle in ipairs({ 0, 40, -40, 80, -80, 120, -120, 160, -160 }) do local point = boom.pos + rotateY(away, angle) * holdDistance + Vector3.new(0, lift, 0) -- (never to the other side of a thin lava sheet: life 40 lifted a shooting spot over floor 11's sheet and touched it twice, 38 each) if not (w.ceilingY and point.Y > w.ceilingY) and not Kit.CrossesSheet(me.pos, point) and not Kit.LavaNear(point) then local okGround = lift > 0 or not w.ground or w.ground(point) local okSight = ( not lineClear or lineClear( point + Vector3.new(0, 1.5, 0), boom.pos + Vector3.new(0, 1.2, 0) ) ) and (not w.spotFree or w.spotFree(point)) local movesEnough = not exposedToShooters or (point - me.pos).Magnitude >= (rage.ShooterMoveMin or 7) if okGround and okSight and movesEnough then local score = exposureAt(point, boom, w.mobs) * 10 + math.abs(angle) * 0.01 + swingExposureAt(w, point, boom) * 25 + shooterSightAt(w, point) * 30 + lift * 0.15 if not bestScore or score < bestScore then bestPoint, bestScore = point, score end end end end end if bestPoint then break end end end act.Teleport = bestPoint if bestPoint and exposedToShooters then mem.relocateAt = now end if not bestPoint and blocked and boom.dist3 >= safe + 8 then -- nowhere with a clear line and it is not close yet: leave it for now act.State, act.Reason, act.Snipe, act.Face = 'idle', '', nil, nil end end if act.Snipe then return act end end end end local plan = (not w.spotFree or w.spotFree(me.pos + Vector3.new(0, 1.5, 0))) and grenadePlan(w, mem) or nil if plan then mem.gren = { Stage = 'equip', T = now, Plan = plan } act.State, act.Reason, act.Grenade = 'grenade', string.format('grenade on %d mobs', plan.Count), plan return act end -- heals the mobs leave behind: touching the drop collects it, and we can hop straight onto it mem.skip = mem.skip or {} if rage.Collect and rage.Collect ~= 'Off' then local hurt = hpPct < (rage.CollectBelow or 95) -- the launcher is the horde weapon: with nothing left in it an ammo drop is worth a hop even with the pack around -- (any gun: an assault rifle that has run dry in the horde is no reason to punch 50 mobs; its ammo drops are worth a hop too) local gunCapacity = (w.gun and type(w.gun.Info) == 'table') and ((tonumber(w.gun.Info.MaxAmmo) or 0) + (tonumber(w.gun.Info.MaxAmmoReserve) or 0)) or 0 local starvedAt = w.gun and (w.gun.Projectile and (rage.LauncherStarved or 3) or (gunCapacity ~= math.huge and math.max( 4, gunCapacity * (rage.GunStarvedFraction or 0.12) ) or 0)) or 0 local launcherStarved = w.gun ~= nil and (w.gun.Ammo or 0) + (w.gun.Reserve or 0) <= starvedAt if launcherStarved then mem.starvedSince = mem.starvedSince or now else mem.starvedSince = nil end local bestDrop, bestDistance for _, drop in ipairs(w.drops or {}) do -- heals only matter while hurt; ammo is taken any time in 'Heal + Ammo' and 'All' local isHeal = drop.Label == '' or Kit.IsHealLabel(drop.Label) local isAmmo = tostring(drop.Label):lower():find('ammo', 1, true) ~= nil -- anything that is not a heal waits until nobody is close: it is not worth being hit for local calm = true for _, m in ipairs(w.mobs) do if m.dist3 < 12 or (m.pos - drop.Pos).Magnitude < 8 then calm = false break end end -- ammo is only worth a hop while the gun is not already (nearly) full local needAmmo = false -- a new weapon replaces the gun we hold, so it is only worth taking while that gun is (nearly) empty or missing local gunLow = w.gun == nil -- the hunt: until the gun is the Grenade Launcher every weapon drop is worth a look local holdingLauncher = w.gun ~= nil and w.gun.Projectile ~= nil local hunting = rage.LauncherHunt ~= false and not holdingLauncher if w.gun and type(w.gun.Info) == 'table' then local capacity = (tonumber(w.gun.Info.MaxAmmo) or 0) + (tonumber(w.gun.Info.MaxAmmoReserve) or 0) needAmmo = capacity ~= math.huge and (capacity <= 0 or (w.gun.Ammo or 0) + (w.gun.Reserve or 0) < capacity * 0.85) gunLow = capacity ~= math.huge and ( capacity <= 0 or (w.gun.Ammo or 0) + (w.gun.Reserve or 0) < capacity * (rage.ClaimBelow or 0.6) ) end -- a launcher is only swapped away once it has nothing left to fire if holdingLauncher then gunLow = (w.gun.Ammo or 0) + (w.gun.Reserve or 0) <= 0 end local isWeapon = drop.Label == 'Weapon' -- ...but not into the thick of it, unless this has gone on for a while local starvedOk = launcherStarved and isAmmo and ( (exposureAt(drop.Pos, nil, w.mobs) <= 5 and swingExposureAt(w, drop.Pos) <= 1) or now - mem.starvedSince > 8 ) local boomClose = rage.ExplosiveFlee ~= false and boomNear(w, drop.Pos, rage.ExplosiveDanger or 28) local wanted = not boomClose and ( starvedOk or (isHeal and hurt and not isWeapon) or (calm and isAmmo and needAmmo and (rage.Collect == 'Heal + Ammo' or rage.Collect == 'All')) or (calm and isWeapon and rage.ClaimWeapon ~= false and rage.Collect ~= 'Off' and (gunLow or hunting)) or (calm and rage.Collect == 'All' and not isHeal and not isWeapon) ) local key = 'drop:' .. tostring(math.floor(drop.Pos.X)) .. ',' .. tostring(math.floor(drop.Pos.Z)) local blocked = mem.blocked[key] and mem.blocked[key] > now local distance = (drop.Pos - me.pos).Magnitude -- a weapon drop is claimed with its prompt: stay on it and press, a few tries at most local nearWeapon = isWeapon and drop.Prompt ~= nil and distance <= 6 and claimTriesOf(mem, key) < 3 if wanted and not blocked and (distance > 2.5 or nearWeapon) and distance <= (rage.CollectRange or 150) and (not bestDistance or distance < bestDistance) then bestDrop, bestDistance = drop, distance drop.Key = key end end if bestDrop then act.State = 'collect' act.Reason = string.format( 'picking up %s (%dm), health %d%%', bestDrop.Label ~= '' and bestDrop.Label or 'a drop', math.floor(bestDistance), math.floor(hpPct) ) if bestDrop.Label == 'Weapon' and bestDrop.Prompt and bestDistance <= 6 then act.Claim = { Prompt = bestDrop.Prompt, Key = bestDrop.Key } act.Reason = 'claiming the weapon drop' else act.Teleport, act.GoalKey = bestDrop.Pos, bestDrop.Key end return act end end -- skip mobs that stopped taking damage while we stand next to them (reviving, shielded, out of reach) local usable = {} for _, m in ipairs(pool) do local until_ = mem.skip[m.rec.Model] if until_ and until_ <= now then mem.skip[m.rec.Model] = nil until_ = nil end local reviving = Kit.Reviving[m.rec.Model] if reviving and reviving > now then until_ = reviving end if not until_ then usable[#usable + 1] = m end end if #usable > 0 then pool = usable end -- just stepped aside: only what comes to us is fought for a moment, the rest of the pack is given time to string out -- (not while a skeleton has a line on us: they never string out towards us, and the hold is how life 48 stood in their bones) if (mem.stepUntil or 0) > now and not mem.nest and #pool > 0 and now >= (mem.kiteBanUntil or 0) and not (rage.KiteShooters ~= false and shooterSightAt(w, me.pos) > 0) then local engage = rage.HoldEngage or 10 local arrived = {} for _, m in ipairs(pool) do if m.dist3 <= engage or (m.class.Ranged and m.dist3 <= 40 and liveAttack(w, m)) then arrived[#arrived + 1] = m end end if #arrived == 0 then mem.kiteSince = mem.kiteSince or now if now - mem.kiteSince <= (rage.KiteMax or 7) then act.State, act.Reason = 'kite', 'stepped aside: letting the pack string out' return act end -- nobody is coming (the pack stands where it spawned): a hold that long is a stall, so the fight goes on for a while mem.kiteSince, mem.stepUntil, mem.kiteBanUntil = nil, 0, now + (rage.KiteBan or 6) end if #arrived > 0 then mem.kiteSince = nil pool = arrived end end local target, spot, spotExposure = nil, nil, 0 if #pool > 0 then target, spot, spotExposure = pickRageTarget(w, pool, mem, w.mobs) end if target then local model = target.rec.Model local watch = mem.watch local reachable = (me.pos - target.pos).Magnitude <= w.reach + 0.8 if not watch or watch.model ~= model then mem.watch = { model = model, hp = target.hp, since = now } elseif target.hp and watch.hp and target.hp < watch.hp - 0.5 then watch.hp, watch.since = target.hp, now elseif not reachable then watch.since = now elseif now - watch.since > (rage.StuckTime or 2.2) then mem.skip[model] = now + 6 mem.watch = nil Kit.Notify('stuck', 'Rage', model.Name .. ' is not taking damage: skipping it for a few seconds', 3) end mem.target = model act.State = 'rage' act.Target = target act.Spot = spot act.Face = target.pos + Vector3.new(0, 1.2, 0) act.Reason = string.format( '%s %dm (%s)%s', target.rec.Model.Name, math.floor(target.dist3), rage.Position, explosivePhase and ', explosive last' or '' ) -- already within fist range: stay and swing. Chasing the exact spot makes us orbit a mob that is -- turning to face us, and every hop pauses the swings. Only move when out of range or in a crowd. local distance = (me.pos - target.pos).Magnitude local inRange = distance <= math.max(2, w.reach - 0.7) local myExposure = exposureAt(me.pos, target, w.mobs) local crowded = myExposure - (spotExposure or 0) >= (rage.FleeMargin or 2) -- kill aura: anything already inside fist range gets hit first, facing the direction that covers -- the most mobs inside the swing cone, without hopping around local close = {} if rage.Aura ~= false then local range = math.max(2, w.reach - 0.2) if rage.Position == 'Above' then range = math.max(range, (rage.VerticalOffset or 5.6) + 0.5) end for _, m in ipairs(w.mobs) do -- never punch explosives the policy keeps for later, nor mobs that stopped taking damage local held = m.class.Explosive and rage.ExplosivePolicy ~= 'Normal' held = held or (m.class.Pinata and not (mem.trojanGo and not gunReady)) local skipped = (mem.skip[m.rec.Model] and mem.skip[m.rec.Model] > now) or (Kit.Reviving[m.rec.Model] and Kit.Reviving[m.rec.Model] > now) -- a brute is hovered over higher (see bruteVertical), so its aura range is longer too local reachHere = range if m.class.CloseDodge and rage.Position == 'Above' then reachHere = math.max(range, bruteVertical(w) + 0.6) end if not held and not skipped and not dodgeSet[m] and (m.pos - me.pos).Magnitude <= reachHere then close[#close + 1] = m end end end if #close > 0 then mem.chase = nil local cone = math.cos(math.rad(rage.AuraCone or 55)) local bestMob, bestScore for _, candidate in ipairs(close) do local direction = flat(candidate.pos - me.pos) if direction.Magnitude > 0.1 then direction = direction.Unit -- the swing is aimed at the highest group in reach (flyers, then big ones, then zombies); the rest of the cone still gets hit local score = rage.KillOrder ~= false and (3 - Kit.KillTier(candidate.class)) * (rage.AuraTier or 2.5) or 0 -- the locked target is the one the swing is aimed at whenever it is in reach (the rest of the cone still gets hit) if rage.TargetLock ~= false and candidate == target then score += 50 end for _, other in ipairs(close) do local offset = flat(other.pos - me.pos) if offset.Magnitude < 0.1 or offset.Unit:Dot(direction) >= cone then score += 1 + (other == target and 0.4 or 0) + (1 - math.min((other.hp or 75) / 100, 1)) * 0.3 end end if not bestScore or score > bestScore then bestMob, bestScore = candidate, score end end end bestMob = bestMob or close[1] -- the mob we actually swing at must keep losing health, or it gets skipped for a few seconds local aura = mem.auraWatch if not aura or aura.model ~= bestMob.rec.Model then mem.auraWatch = { model = bestMob.rec.Model, hp = bestMob.hp, since = now } elseif bestMob.hp and aura.hp and bestMob.hp < aura.hp - 0.5 then aura.hp, aura.since = bestMob.hp, now elseif now - aura.since > (rage.StuckTime or 1.4) then mem.skip[bestMob.rec.Model] = now + 6 mem.auraWatch = nil Kit.Notify( 'stuck', 'Rage', bestMob.rec.Model.Name .. ' is not taking damage: skipping it for a few seconds', 3 ) end act.Face = bestMob.pos + Vector3.new(0, 1.2, 0) act.AuraTarget = bestMob act.AuraCount = #close act.Reason = string.format('aura on %d: %s', #close, bestMob.rec.Model.Name) -- stay and fight; only leave when a bigger pack is closing in and a quieter spot exists local danger = myExposure >= (rage.AuraMaxCrowd or 5) and myExposure - (spotExposure or 0) >= (rage.FleeMargin or 2) -- kiting: with a fist reach longer than the mobs' (about 4.7), step back out of theirs local kite = false if rage.Kite ~= false and w.reach >= 6.5 then for _, m in ipairs(w.mobs) do if (m.pos - me.pos).Magnitude < (rage.KiteDistance or 6) then kite = true break end end end if (danger or kite) and (me.pos - spot).Magnitude > rage.LockTolerance then act.Teleport = spot end -- ...otherwise keep moving round the mob we are hitting if rage.Orbit ~= false and rage.Position == 'Above' and not act.Teleport and not bestMob.class.Pinata and not bestMob.class.CloseDodge and now - (mem.orbitAt or -9) >= (rage.OrbitGap or 0.26) and (me.pos - bestMob.pos).Magnitude <= w.reach + 1.5 then local point = orbitSpot(w, mem, bestMob, w.mobs) if point then act.Teleport = point mem.orbitAt = now Kit.Orbits = (Kit.Orbits or 0) + 1 end end act.Attack = true return act end if (not inRange or crowded) and (me.pos - spot).Magnitude > rage.LockTolerance then act.Teleport = spot end -- a ghost close by is not hopped after every few tenths of a second (it drifts 5 studs while it winds up; each hop pauses the -- swings and it simply follows): after a hop to it we stay, face it and swing for a moment if act.Teleport and target.class.Name == 'Ghost' and rage.Orbit == false then if target.dist3 <= (rage.GhostStand or 9) and now < (mem.ghostHoldUntil or 0) then act.Teleport = nil else mem.ghostHoldUntil = now + (rage.GhostHold or 0.6) end end -- a chase that goes nowhere (the hop is turned back into where we already stand): after a few seconds land right beside it if not inRange then local chase = mem.chase if not chase or chase.model ~= model or (me.pos - chase.pos).Magnitude > 3 then mem.chase = { model = model, pos = me.pos, since = now } elseif now - chase.since > (rage.ChaseStall or 4) then act.Teleport = target.pos + Vector3.new(0, 3.5, 0) act.Reason = act.Reason .. ', chase stalled: landing beside it' if now - (mem.stallNoted or -99) > 5 then mem.stallNoted = now Kit.Notify( 'stall', 'Rage', model.Name .. ' is out of reach and the hop goes nowhere: landing beside it', 2, true ) end end else mem.chase = nil end act.Attack = distance <= w.reach + 0.8 return act elseif #explosive > 0 then mem.explosiveWaitSince = mem.explosiveWaitSince or now act.State, act.Reason = 'wait', string.format( 'only explosive zombies left and health is %d%% (needs %d%%)', math.floor(hpPct), rage.MinHpExplosive ) return act end mem.target = nil if now - mem.lastMobAt < rage.WaveDelay then act.State, act.Reason = 'wait', 'waiting for more spawns' return act end if cfg.WaveTriggers then local best, bestDistance for _, goal in ipairs(triggerCandidates(w, mem, now, rage.LockTolerance + 6)) do if not (mem.blocked[goal.Key] and mem.blocked[goal.Key] > now) then local d = (goal.Pos - me.pos).Magnitude if not bestDistance or d < bestDistance then best, bestDistance = goal, d end end end if best then act.State, act.Reason, act.GoalKey = 'trigger', string.format('wave trigger %dm away', math.floor(bestDistance)), best.Key mem.sitting = mem.sitting or {} if bestDistance > rage.LockTolerance then act.Teleport = best.Pos else -- standing right on it and nothing happens: step off and back on once, then give up on it local sit = mem.sitting[best.Key] if not sit then sit = { since = now } mem.sitting[best.Key] = sit end if now - sit.since > 3.2 then mem.blocked[best.Key] = now + 30 mem.sitting[best.Key] = nil elseif now - sit.since > 1.2 and not sit.nudged then sit.nudged = true act.Teleport = best.Pos + Vector3.new(0, 0, 7) end end return act end end if cfg.Advance and w.entrance and not (mem.blocked[w.entranceKey] and mem.blocked[w.entranceKey] > now) then act.State, act.Reason, act.GoalKey = 'advance', string.format('to floor %d', w.entranceFloor or ((w.floor or 0) + 1)), w.entranceKey if (w.entrance - me.pos).Magnitude > rage.LockTolerance then act.Teleport = w.entrance end return act end act.State, act.Reason = 'idle', 'nothing left to do here' return act end -- ------------------------------------------------------------- bones -- A bone (see Kit.Bones) is dodged by stepping out of its line, and no hop is allowed to end on one. Wrapped around the decision so -- the fight, the gun and the grenade rules need not know: a hop planned onto a bone's path is moved off it, a position a bone is -- about to reach gets a hop, and a grenade that is being cooked there is dropped. local function boneSafeSpot(w, origin, bones, bone) local flatVel = flat(bone.Vel) local side = flatVel.Magnitude > 0.5 and Vector3.new(-flatVel.Z, 0, flatVel.X).Unit or Vector3.new(1, 0, 0) local needGround = w.ground ~= nil and w.ground(w.me.pos) local best, bestScore for _, distance in ipairs({ 7, 10, 14 }) do for _, angle in ipairs({ 0, 180, 35, -35, 145, -145 }) do local candidate = origin + rotateY(side, angle) * distance if Kit.BoneThreat(bones, candidate, (rage.BoneHorizon or 1.8) + 0.4, rage.BoneRadius or 4.5) == nil and (not w.spotFree or w.spotFree(candidate)) and not Kit.IsLethal(candidate) and (not needGround or w.ground(candidate)) then local score = distance * 0.15 + exposureAt(candidate, nil, w.mobs) * 3 + swingExposureAt(w, candidate) * 10 + shooterSightAt(w, candidate) * 1.5 if not bestScore or score < bestScore then best, bestScore = candidate, score end end end end return best end -- ------------------------------------------------------ one weapon at a time -- The decision flipped between the gun and the fists several times a second (a ghost drifting in and out of 6 studs, a target -- appearing and going, a line that is clear one frame and not the next; weapon swaps ran at 13-44 a minute). Every flip is a swap: -- the gun takes half a second to draw, a reload is lost when the gun is put away, and the next shot finds an empty magazine. -- So: a mode is kept for a moment (ArmDwell), a change has to be wanted for a while (GunHold / FistHold) before it happens, an empty -- magazine is refilled before the gun goes away (unless something is on top of us), and quiet moments top the magazine up. local function armOf(act) if act.Stop or act.Grenade or act.Evade or act.Reload then return nil end if act.State == 'snipe' and act.Snipe then return 'gun' end if act.State == 'rage' then return 'fists' end return nil end local function recordOf(w, model) for _, m in ipairs(w.mobs) do if m.rec.Model == model then return m end end return nil end -- the last act of a mode, brought up to date, for holding the mode a little longer (nil when its target is gone) local function heldAct(w, mem, mode) local saved = mem.armActs and mem.armActs[mode] if not saved or w.now - saved.at > 1.2 then return nil end local old = saved.act if mode == 'gun' then local gun = w.gun local plan = old.Snipe local oldTarget = plan and plan.Target local model = oldTarget and oldTarget.rec and oldTarget.rec.Model local fresh = model and recordOf(w, model) if not gun or not fresh or (gun.Ammo or 0) + (gun.Reserve or 0) <= 0 then return nil end local act = { State = 'snipe', Reason = (old.Reason or '') .. ' (keeping the gun)' } act.Snipe = { Target = fresh, Slot = gun.Slot, Item = gun.Item, Safe = plan.Safe, Projectile = gun.Projectile, Fast = gun.Fast, Lead = plan.Lead, } act.Face = old.Face + (fresh.pos - oldTarget.pos) return act end local oldTarget = old.Target local model = oldTarget and oldTarget.rec and oldTarget.rec.Model local fresh = model and recordOf(w, model) if not fresh then return nil end local aura = old.AuraTarget and old.AuraTarget.rec and recordOf(w, old.AuraTarget.rec.Model) or nil local inReach = (w.me.pos - fresh.pos).Magnitude <= w.reach + 0.8 if not inReach and not aura then return nil end local act = { State = 'rage', Reason = (old.Reason or '') .. ' (keeping the fists)', Target = fresh, Spot = old.Spot } act.Face = fresh.pos + Vector3.new(0, 1.2, 0) act.AuraTarget, act.AuraCount = aura, old.AuraCount act.Attack = true return act end local function reloadAct(w, why) local gun = w.gun local cam = Workspace.CurrentCamera local act = { State = 'reload', Reload = true, Reason = why } act.Snipe = { Slot = gun.Slot, Item = gun.Item, Safe = 0, Projectile = gun.Projectile } act.Face = (cam and cam.CFrame.Position + cam.CFrame.LookVector * 20) or w.me.pos return act end -- ----------------------------------------------------- above the lava sheet -- (see Kit.StayAboveSheet) On a floor with a thin lava sheet (floor 11) the arena is above it. The bot stays above: when it finds itself -- under or in it, the first thing it does is climb; mobs that are under the sheet are not part of the fight (hidden from the decision) and -- are waited for. A wait that goes nowhere (a zombie that fell into the pit and stays there) ends after SheetWait seconds with the old -- under-the-sheet behaviour for SheetUnlock seconds. local decideRageRaw = B.DecideRage -- a gun that does not fire: life 48 spent 15 s alternating 'kite' and 'launcher: pack 40 m away' (the arc into the pack was walled off, the -- ammo stayed 2/24) while four skeletons threw bones. After GunDryMax seconds of snipe acts without a single shot the gun branch is off for -- GunDryBan seconds and the fists fight (the gun is still held and reloaded, only the branch is skipped) local function decideRageCore(w, mem) if rage.GunDry == false then return decideRageRaw(w, mem) end local now, gun = w.now, w.gun local total = gun and ((gun.Ammo or 0) + (gun.Reserve or 0)) or nil local name = gun and type(gun.Info) == 'table' and gun.Info.Name or nil if mem.dryGun ~= name or (total and mem.dryTotal and total < mem.dryTotal) or now - (mem.dryLast or -99) > 2.5 then mem.drySince = nil end mem.dryGun, mem.dryTotal = name, total if now < (mem.gunBanUntil or 0) then w.gunBan = true end local act = decideRageRaw(w, mem) -- (only the launcher's pack bombard counts: an explosive zombie that cannot be hit is fled from, not punched) if act.State == 'snipe' and not w.gunBan and tostring(act.Reason):find('grenade launcher', 1, true) then mem.drySince = mem.drySince or now mem.dryLast = now local boss = false for _, m in ipairs(w.mobs) do if m.class.Boss or m.class.Pinata then boss = true break end end if not boss and now - mem.drySince > (rage.GunDryMax or 8) then mem.gunBanUntil, mem.drySince, mem.snipeWatch = now + (rage.GunDryBan or 15), nil, nil Kit.GunDryBans = (Kit.GunDryBans or 0) + 1 Kit.Notify( 'dry', 'Rage', 'the gun has not fired for ' .. tostring(rage.GunDryMax or 8) .. ' s: the fists take over for a while', 2, true ) w.gunBan = true act = decideRageRaw(w, mem) end end return act end local function sheetDecide(w, mem) local now = w.now if mem.sheetUnlockUntil then if now >= mem.sheetUnlockUntil then mem.sheetUnlockUntil = nil Kit.StayAboveSheet = true else Kit.StayAboveSheet = false end end if Kit.StayAboveSheet == false then return decideRageCore(w, mem) end local side, sheet = Kit.SheetSide(w.me.pos) if not side then mem.sheetWaitSince = nil return decideRageCore(w, mem) end if side ~= 'above' then -- the entrance level, or knocked into the band: up to the arena before anything else local top = sheet.Position.Y + sheet.Size.Y / 2 mem.sheetWaitSince = nil Kit.SheetClimbs = (Kit.SheetClimbs or 0) + 1 return { State = 'evade', Evade = true, Reason = 'climbing above the lava', Teleport = Vector3.new(w.me.pos.X, top + Kit.SheetAbove, w.me.pos.Z), Face = w.me.pos + Vector3.new(0, 0, -1), } end local kept, hidden = {}, 0 for _, m in ipairs(w.mobs) do if not m.class.Pinata and Kit.SheetSide(m.pos) == 'below' then hidden += 1 else kept[#kept + 1] = m end end if hidden == 0 then mem.sheetWaitSince = nil return decideRageCore(w, mem) end local act = decideRageCore(setmetatable({ mobs = kept }, { __index = w }), mem) if act.State == 'idle' or act.State == 'wait' or act.State == 'advance' then mem.sheetWaitSince = mem.sheetWaitSince or now if now - mem.sheetWaitSince > (rage.SheetWait or 30) then mem.sheetUnlockUntil = now + (rage.SheetUnlock or 45) mem.sheetWaitSince = nil Kit.StayAboveSheet = false Kit.Notify('sheet', 'Rage', 'the mobs under the lava do not come up: going down for them', 3, true) return decideRageCore(w, mem) end return { State = 'wait', Reason = string.format( '%d mob%s under the lava: waiting for them to come up (%ds)', hidden, hidden == 1 and '' or 's', math.floor(now - mem.sheetWaitSince) ), } end mem.sheetWaitSince = nil return act end local decideRageBase = sheetDecide local function armDecide(w, mem) local act = decideRageBase(w, mem) if rage.ArmHold == false then return act end local now, gun = w.now, w.gun local want = armOf(act) local hpPct = w.me.hp / math.max(w.me.maxHp, 1) * 100 local nearest = w.mobs[1] and w.mobs[1].dist3 or math.huge local ammo = gun and (gun.Ammo or 0) or 0 local reserve = gun and (gun.Reserve or 0) or 0 local maxAmmo = gun and type(gun.Info) == 'table' and tonumber(gun.Info.MaxAmmo) or 0 -- how long the magazine has been empty (with ammo left to fill it) if gun and ammo <= 0 and reserve > 0 then mem.emptySince = mem.emptySince or now else mem.emptySince = nil end if not want then -- a quiet moment (nobody within 30, nothing to do but wait): fill the magazine local lull = (act.State == 'wait' or act.State == 'idle' or (act.State == 'trigger' and not act.Teleport)) and nearest > 30 if lull and gun and reserve > 0 and maxAmmo > 0 and ammo < maxAmmo * (rage.ReloadBelow or 0.5) and now >= (mem.lullReloadBan or 0) then mem.lullReload = mem.lullReload or { At = now, Ammo = ammo } if now - mem.lullReload.At > (rage.ReloadPatience or 3.5) and ammo <= mem.lullReload.Ammo then mem.lullReloadBan, mem.lullReload = now + 15, nil else return reloadAct(w, string.format('quiet: reloading (%d/%d)', ammo, maxAmmo)) end else mem.lullReload = nil end return act end -- an empty magazine is refilled before the gun goes away if want == 'fists' and mem.arm == 'gun' and mem.emptySince and now - mem.emptySince < (rage.ReloadHold or 2.4) and nearest > 3.5 and hpPct > 25 then Kit.ArmHolds = (Kit.ArmHolds or 0) + 1 return reloadAct(w, 'reloading before the fists come out') end local cur = mem.arm if not cur or now - (mem.armSeen or -99) > 3 then mem.arm, mem.armAt, mem.armWantSince = want, now, nil elseif want == cur then mem.armWantSince = nil else mem.armWantSince = mem.armWantSince or now local hold = cur == 'gun' and (rage.GunHold or 0.7) or (rage.FistHold or 0.5) if now - mem.armWantSince < hold or now - mem.armAt < (rage.ArmDwell or 0.9) then local keep = heldAct(w, mem, cur) if keep then Kit.ArmHolds = (Kit.ArmHolds or 0) + 1 mem.armSeen = now return keep end end mem.arm, mem.armAt, mem.armWantSince = want, now, nil Kit.ArmSwitches = (Kit.ArmSwitches or 0) + 1 end mem.armSeen = now mem.armActs = mem.armActs or {} mem.armActs[want] = { act = act, at = now } return act end local decideRageInner = armDecide function B.DecideRage(w, mem) local act = decideRageInner(w, mem) local bones = w.bones if rage.BoneDodge == false or not bones or #bones == 0 or act.Stop then return act end local horizon = rage.BoneHorizon or 1.8 local radius = rage.BoneRadius or 4.5 local here = w.me.pos local destination = act.Teleport if destination then -- a hop onto a bone's path: the same hop, moved off the path local arrives, bone = Kit.BoneThreat(bones, destination, horizon, radius) if arrives then local alternative = boneSafeSpot(w, destination, bones, bone) if alternative then act.Teleport = alternative act.Reason = (act.Reason or '') .. ' (off a bone path)' Kit.BoneNudges = (Kit.BoneNudges or 0) + 1 end end return act end -- staying put: is a bone about to reach us? local arrives, bone = Kit.BoneThreat(bones, here, rage.BoneLead or 1.0, radius) if not arrives then return act end local spot = boneSafeSpot(w, here, bones, bone) if not spot then return act end Kit.BoneDodges = (Kit.BoneDodges or 0) + 1 if act.Grenade or act.Reload then -- cooking a grenade or filling a magazine in a bone's path: dropped mem.gren = nil act = { State = 'evade', Evade = true, Reason = '' } end act.Teleport = spot act.Force = true act.Face = act.Face or (bone.Pos + Vector3.new(0, 1.2, 0)) if act.State == 'idle' or act.State == 'wait' or act.State == 'trigger' or act.State == 'advance' then act.State = 'evade' end act.Evade = act.Evade or (act.State == 'evade') act.Reason = string.format('a bone reaches us in %.1fs: stepping off its path', arrives) .. (act.Reason ~= '' and (' | ' .. act.Reason) or '') return act end local function collectParts() local t = Kit.Now() if t - R.partsAt < 0.5 then return R.parts end R.partsAt = t local list = {} local character = LP.Character for _, inst in ipairs(character and character:GetDescendants() or {}) do if inst:IsA('BasePart') then list[#list + 1] = inst end end R.parts = list return list end -- hold the character where we put it: no collisions, no drifting function R.Hold(on) if on and not R.freezeConn then R.freezeConn = RunService.Stepped:Connect(function() local root = Kit.MyRoot() if root then root.AssemblyLinearVelocity = Vector3.zero root.AssemblyAngularVelocity = Vector3.zero end end) elseif not on and R.freezeConn then R.freezeConn:Disconnect() R.freezeConn = nil end if on and rage.Noclip and not R.noclipConn then R.noclipConn = RunService.Stepped:Connect(function() for _, part in ipairs(collectParts()) do if part.Parent then if R.original[part] == nil then R.original[part] = part.CanCollide end part.CanCollide = false end end end) elseif (not on or not rage.Noclip) and R.noclipConn then R.noclipConn:Disconnect() R.noclipConn = nil for part, value in pairs(R.original) do if part.Parent then part.CanCollide = value end end R.original = setmetatable({}, { __mode = 'k' }) end end function R.Hop(w, destination, faceAt, force, guardWalls) local root = w.me.root local now = w.now if not force and now - R.lastHopAt < rage.HopCooldown then return false end local from = root.Position local delta = destination - from local distance = delta.Magnitude if distance < 0.05 then return false end local step = distance > R.maxHop and (from + delta.Unit * R.maxHop) or destination -- never above the top of the tower (the dodges used to climb out of it, a few steps at a time) if w.ceilingY and step.Y > w.ceilingY and step.Y > from.Y then step = Vector3.new(step.X, math.max(from.Y, w.ceilingY), step.Z) end -- big vertical jumps are taken in pieces: the server pulls part of a long climb back and the drop that follows hurts local climb = math.abs(step.Y - from.Y) local climbLimit = rage.MaxVerticalHop or 22 if climb > climbLimit then step = from + (step - from) * (climbLimit / climb) end -- a hop cut short by the distance, ceiling or climb limits is only a step on the way (it crosses slabs on the way up or down) local partial = (step - destination).Magnitude > 1 step = Kit.LavaSafe(step, from) if not step then R.refusedLethal = (R.refusedLethal or 0) + 1 return false end -- a hop made to SHOOT from never ends inside a wall or a pillar: every shot from there goes into the wall. A spot just beside is -- used instead; with none within 10 studs the hop only goes ahead when it is a dodge (force). (Fighting with the fists from inside -- a wall is harmless, and a brute standing in the arena's player barrier made the bot hop on the spot for a minute.) if guardWalls and rage.WallGuard ~= false and not partial and not Kit.SpotFree(step) then local free = Kit.NearestFree(step) if free then step = free R.wallNudges = (R.wallNudges or 0) + 1 elseif not force then R.wallRefused = (R.wallRefused or 0) + 1 return false end end if Kit.IsLethal(step) then R.refusedLethal = (R.refusedLethal or 0) + 1 R.lastRefused = { To = step, At = now } return false end local lookAt = faceAt and Vector3.new(faceAt.X, step.Y, faceAt.Z) or (step + root.CFrame.LookVector) if (lookAt - step).Magnitude < 0.05 then lookAt = step + root.CFrame.LookVector end R.lastHop = { Dist = (step - from).Magnitude, Dy = step.Y - from.Y, At = now, To = step } root.CFrame = CFrame.lookAt(step, lookAt) root.AssemblyLinearVelocity = Vector3.zero root.AssemblyAngularVelocity = Vector3.zero R.lastHopAt = now R.expected, R.expectedAt, R.counted = step, now, nil R.expectedHp = w.me.hp R.hops += 1 return true end function R.OnReject(w, deviation) R.rejected += 1 R.rejectTimes[#R.rejectTimes + 1] = w.now R.maxHop = math.max(rage.MinHop, R.maxHop * 0.5) rage.HopCooldown = math.min(0.5, rage.HopCooldown * 1.25) R.sinceGrow = 0 Kit.Notify( 'rage', 'Rage', string.format('the server moved me back %.0f studs; hops are now %.0f studs', deviation, R.maxHop), 2, true ) local recent = 0 for _, t in ipairs(R.rejectTimes) do if w.now - t < 10 then recent += 1 end end if recent >= 4 and R.maxHop <= rage.MinHop + 0.1 then cfg.Mode = 'Legit' Kit.Notify('rage-off', 'Rage', 'the server keeps undoing teleports: switched back to walking mode', 1, true) if Options and Options.TWBotMode and Options.TWBotMode.Value ~= 'Legit' then Options.TWBotMode:SetValue('Legit') elseif not (Options and Options.TWBotMode) and Toggles and Toggles.TWBot and Toggles.TWBot.Value then -- the hub has no walking mode any more: the farm stops instead Kit.Notify('rage-stop', 'Auto Tower', 'the server keeps undoing teleports: Auto Tower stopped', 1, true) Toggles.TWBot:SetValue(false) end end end -- did the server keep us where we put ourselves? function R.Verify(w) local expected = R.expected if not expected then return end local age = w.now - R.expectedAt local deviation = (w.me.pos - expected).Magnitude if age > 0.03 and age < 0.6 and deviation > 7 then R.expected = nil if R.expectedHp and w.me.hp and w.me.hp < R.expectedHp - 4 then -- a hit with knockback (lava throws us 20 studs up, 38 hp): not the server undoing the teleport, and not a reason to shrink the hops R.knockbacks = (R.knockbacks or 0) + 1 return end R.OnReject(w, deviation) elseif age >= 0.2 and deviation < 3 and R.counted ~= R.expectedAt then R.counted = R.expectedAt R.accepted += 1 R.sinceGrow += 1 if R.sinceGrow >= 25 and R.maxHop < rage.HopCap then R.maxHop = math.min(rage.HopCap, R.maxHop * 1.25) R.sinceGrow = 0 end end end local function pressKey(code) local manager = Instance.new('VirtualInputManager') manager:SendKeyEvent(true, code, false, game) task.delay(0.05, function() manager:SendKeyEvent(false, code, false, game) end) end -- one frame of shooting an explosive zombie: back off if told to, equip the gun, aim, fire, reload function R.Snipe(w, act, dt) local plan = act.Snipe w.me.hum:Move(Vector3.zero, false) R.Verify(w) if act.Teleport and R.Hop(w, act.Teleport, act.Face, act.Force, true) and act.Evade then Kit.Dodges += 1 Kit.LastDodgeAt = w.now end local fighter = ResolveLocalFighter() local equipped = fighter and fighter.EquippedItem if equipped ~= plan.Item then if fighter and w.now - R.gunEquipAt > 0.4 then R.gunEquipAt = w.now pcall(function() fighter:EquipItem(plan.Slot) end) end return end local projectile = plan.Projectile local aimDirection if projectile then -- the shell is aimed on its arc at where the target will be when it lands local camNow = Workspace.CurrentCamera local controller = RivalsModsState.ResolveCameraController() if camNow and controller and type(controller.SetRotation) == 'function' then local origin = camNow.CFrame.Position local point = act.Face if plan.Lead ~= false and plan.Target and plan.Target.vel then local flight = (point - origin).Magnitude / projectile.Speed point = point + Vector3.new(plan.Target.vel.X, 0, plan.Target.vel.Z) * flight end aimDirection = B.LaunchDirection(origin, point, projectile.Speed, projectile.Gravity) R.aimPoint = point if aimDirection then local lookPitch, lookYaw = CFrame.lookAt(origin, origin + aimDirection):ToOrientation() controller:SetRotation(Vector2.new(lookPitch, lookYaw)) end end else -- a fast projectile gun (Paintball Gun) leads the target by its flight time; a hitscan gun does not local aimAt = act.Face local camLead = Workspace.CurrentCamera if plan.Fast and plan.Target and plan.Target.vel and camLead then local flight = (aimAt - camLead.CFrame.Position).Magnitude / plan.Fast.Speed aimAt = aimAt + Vector3.new(plan.Target.vel.X, 0, plan.Target.vel.Z) * flight end R.hitscanAim = aimAt faceTowards(aimAt, dt, 1) end local item = plan.Item local reloadUntil = tonumber(rawget(item, '_reload_cooldown')) or 0 if tick() < reloadUntil then return end local okAmmo, ammo = pcall(function() return item:Get('Ammo') end) if (okAmmo and tonumber(ammo) or 0) <= 0 then -- (a key pressed while the gun is still being drawn is lost: wait for the equip, then reload and stay with the gun until it is done) if w.now - R.reloadAt > 1.2 and (tonumber(rawget(item, '_equip_cooldown')) or 0) <= tick() then R.reloadAt = w.now R.reloads = (R.reloads or 0) + 1 pressKey(Enum.KeyCode.R) end return end local cam = Workspace.CurrentCamera if not cam then return end local toTarget = (not projectile and R.hitscanAim or act.Face) - cam.CFrame.Position local ready = (tonumber(rawget(item, '_shoot_cooldown')) or 0) <= tick() and (tonumber(rawget(item, '_equip_cooldown')) or 0) <= tick() local aligned if projectile then aligned = aimDirection ~= nil and cam.CFrame.LookVector:Dot(aimDirection) > 0.996 else aligned = toTarget.Magnitude > 0.1 and cam.CFrame.LookVector:Dot(toTarget.Unit) > 0.998 end if ready and w.now - R.lastShotAt >= (projectile and (projectile.Cooldown or 0.6) or 0.11) and aligned then -- not into a wall: the muzzle has to be in open air and the line (or the shell's arc) to the target free if rage.WallGuard ~= false then local origin = cam.CFrame.Position local clear, blocker = Kit.SpotFree(origin, Vector3.new(1.4, 1.4, 1.4)) local cause = not clear and 'muzzle inside' or nil if clear then if projectile and aimDirection then clear, blocker = Kit.ArcClear( origin, aimDirection, projectile.Speed, projectile.Gravity, flat(R.aimPoint - origin).Magnitude, (projectile.Radius or 14) * 0.7 ) cause = not clear and 'arc' or nil blocker = blocker and blocker.Instance elseif not projectile then clear, blocker = Kit.LineClear(origin, act.Face, 2.5) cause = not clear and 'line' or nil blocker = blocker and blocker.Instance end end if not clear then R.blockedShots = (R.blockedShots or 0) + 1 R.shotBlocked = { At = w.now, Model = plan.Target and plan.Target.rec and plan.Target.rec.Model } -- what is it that blocks (for the end-of-life summary: false alarms show up as the same few parts again and again) R.blockCauses = R.blockCauses or {} local label = blocker and (blocker:GetFullName():gsub('Workspace%.', ''):sub(1, 70) .. ' cc=' .. tostring( blocker.CanCollide ) .. ' size=' .. string.format( '%.0fx%.0fx%.0f', blocker.Size.X, blocker.Size.Y, blocker.Size.Z )) or '?' local key = tostring(cause) .. ' | ' .. label .. ' | dist ' .. string.format('%d', math.floor((act.Face - origin).Magnitude / 10) * 10) R.blockCauses[key] = (R.blockCauses[key] or 0) + 1 return end end if click() then R.lastShotAt = w.now R.shots += 1 if projectile then R.launches = R.launches or {} if #R.launches < 80 then local origin = cam.CFrame.Position R.launches[#R.launches + 1] = { T = w.now, Dist = R.aimPoint and (R.aimPoint - origin).Magnitude or -1, Hp = w.me.hum.Health, HpMin = w.me.hum.Health, } end end end end end -- a quiet moment: hold the gun and fill the magazine (act.Reload). Nothing is aimed or fired. function R.Reload(w, act) w.me.hum:Move(Vector3.zero, false) R.Verify(w) local plan = act.Snipe local fighter = ResolveLocalFighter() local equipped = fighter and fighter.EquippedItem if equipped ~= plan.Item then if fighter and w.now - R.gunEquipAt > 0.4 then R.gunEquipAt = w.now pcall(function() fighter:EquipItem(plan.Slot) end) end return end local item = plan.Item if tick() < (tonumber(rawget(item, '_reload_cooldown')) or 0) or (tonumber(rawget(item, '_equip_cooldown')) or 0) > tick() then return end local okAmmo, ammo = pcall(function() return item:Get('Ammo') end) local maxAmmo = type(item.Info) == 'table' and tonumber(item.Info.MaxAmmo) or 0 if okAmmo and tonumber(ammo) and maxAmmo > 0 and tonumber(ammo) < maxAmmo and w.now - R.reloadAt > 1.5 then R.reloadAt = w.now R.reloads = (R.reloads or 0) + 1 pressKey(Enum.KeyCode.R) end end -- one frame of a grenade throw: equip it, aim the arc, throw once, then let the fists come back -- one frame of a grenade throw: equip it, aim the arc, cook it so that the fuse runs out as it lands, throw, then let the fists -- come back. Thrown at once the grenade lands in half a second and then sits for the rest of its two-second fuse while the pack -- walks off: the old throws landed on empty floor. function R.AbortCook() if cookDown then cookDown = false forceMelee() releaseHold() end if R.gren and R.gren.Stage == 'cook' then R.gren = nil R.grenadeAt = (Kit.Now() or 0) - 20 end end function R.Throw(w) local g = R.gren if not g then R.AbortCook() return end local fighter = ResolveLocalFighter() local equipped = fighter and fighter.EquippedItem local plan = g.Plan w.me.hum:Move(Vector3.zero, false) -- something got too close: forget it (taking the fists out drops a cooked grenade), the fists take over again for _, m in ipairs(w.mobs) do if m.dist3 < 5 then R.AbortCook() R.gren = nil Kit.Notify('grenade', 'Rage', 'grenade cancelled: a mob got too close', 2, true) return end end if g.Stage == 'equip' then if equipped == plan.Item then g.Stage, g.T = 'aim', w.now else if fighter and w.now - (g.EquipAt or -1) > 0.5 then g.EquipAt = w.now pcall(function() fighter:EquipItem(plan.Slot) end) end if w.now - g.T > 4 then R.gren = nil R.grenadeAt = w.now - 20 end return end end if g.Stage == 'cook' and not cookDown then R.gren = nil return end if g.Stage == 'aim' or g.Stage == 'cook' then local cam = Workspace.CurrentCamera if not cam then return end -- while the grenade cooks the aim point follows the pack: the same mobs, where they will be when it lands if g.Stage == 'cook' and plan.Models then local sum, n = Vector3.zero, 0 for _, m in ipairs(w.mobs) do if plan.Models[m.rec.Model] then sum += m.pos + Vector3.new(m.vel.X, 0, m.vel.Z) * (g.Flight or 0.5) n += 1 end end if n >= math.max(2, math.ceil((plan.Count or 4) * 0.4)) then plan.Point = sum / n end end local origin = cam.CFrame.Position local offset = plan.Point - origin local distance = flat(offset).Magnitude local pitch = distance > 1 and throwPitch(distance, offset.Y - 1) or nil -- out of range, or the pack came so close that the blast would reach us: drop it if not pitch or distance < (plan.Radius or 16) - 2 then R.AbortCook() R.gren = nil R.grenadeAt = w.now - 20 return end g.Flight = distance / (THROW_SPEED * math.cos(pitch)) local direction = flat(offset).Unit * math.cos(pitch) + Vector3.new(0, math.sin(pitch), 0) local controller = RivalsModsState.ResolveCameraController() if controller and type(controller.SetRotation) == 'function' then local lookPitch, lookYaw = CFrame.lookAt(origin, origin + direction):ToOrientation() controller:SetRotation(Vector2.new(lookPitch, lookYaw)) end local aligned = cam.CFrame.LookVector:Dot(direction) > 0.995 if rage.WallGuard ~= false and (g.Stage == 'aim' or w.now - (g.ArcCheckAt or -9) > 0.3) then g.ArcCheckAt = w.now local clear = Kit.SpotFree(origin, Vector3.new(1.4, 1.4, 1.4)) and Kit.ArcClear(origin, direction, THROW_SPEED, THROW_GRAVITY, distance, (plan.Radius or 16) * 0.5) if not clear then R.blockedThrows = (R.blockedThrows or 0) + 1 R.AbortCook() R.gren = nil R.grenadeAt = w.now - 24 Kit.Notify('grenade', 'Rage', 'grenade cancelled: a wall is in the way', 2, true) return end end if g.Stage == 'aim' then -- the item needs its equip time before it will throw if w.now - g.T >= 0.7 and aligned then if plan.Cook then if pressHold() then cookDown = true g.Stage, g.T = 'cook', w.now end elseif click() then g.Stage, g.T = 'after', w.now R.grenadeAt = w.now R.grenadesThrown = (R.grenadesThrown or 0) + 1 Kit.Notify( 'grenade', 'Rage', string.format('grenade thrown at %d mobs, %d studs away', plan.Count, math.floor(distance)), 1, true ) end elseif w.now - g.T > 3 then R.gren = nil R.grenadeAt = w.now - 20 end return end -- cooking: let go so that the fuse that is left is the flight time (a little over, it goes off as it lands). Never hold it -- to the end of the fuse: it goes off in the hand. local fuse = plan.Fuse or 2 local held = w.now - g.T local releaseAfter = math.min(math.max(0, fuse - g.Flight - 0.08), fuse - 0.35) if held >= releaseAfter and (aligned or held >= fuse - 0.35) then releaseHold() cookDown = false g.Stage, g.T = 'after', w.now R.grenadeAt = w.now R.grenadesThrown = (R.grenadesThrown or 0) + 1 R.grenadeLog = R.grenadeLog or {} R.grenadeLog[#R.grenadeLog + 1] = { At = w.now, Models = plan.Models, Count = plan.Count, Flight = g.Flight, Held = held, Distance = distance, } Kit.Notify( 'grenade', 'Rage', string.format( 'grenade thrown at %d mobs, %d studs away (cooked %.1fs, flight %.1fs)', plan.Count, math.floor(distance), held, g.Flight ), 1, true ) end return end if g.Stage == 'after' and w.now - g.T > 0.8 then R.gren = nil end end function R.Exec(w, act, dt) if cookDown and not (act.Grenade and R.gren) then R.AbortCook() end for index = #(R.launches or {}), math.max(1, #(R.launches or {}) - 3), -1 do local sample = R.launches[index] if sample and w.now - sample.T <= 1.5 then sample.HpMin = math.min(sample.HpMin, w.me.hum.Health) end end R.Hold(true) do local rootPos = w.me.root.Position if Kit.SheetSide(rootPos) == 'above' then R.aboveSheetAt = w.now end local guard = Kit.SheetGuard(rootPos, w.now - (R.aboveSheetAt or -9) < 1.5) if guard and R.Hop(w, guard, nil, true) then R.sheetGuards = (R.sheetGuards or 0) + 1 end end if not R.boomOn then R.boomOn = Kit.WatchExplosions(true) end if act.Grenade then R.Throw(w) return end if act.Reload and act.Snipe then R.Reload(w, act) return end if act.Snipe then R.Snipe(w, act, dt) return end if rage.Evade ~= false and not R.attackOn then R.attackOn = Kit.WatchAttacks(true) end if rage.Evade ~= false and not R.slamOn then R.slamOn = Kit.WatchSlams(true) end if act.Evade then -- no weapon swap here: a dodge between two shots of the gun must not cost the gun, the next fight step -- equips the fists if they are what it needs R.Verify(w) if act.Teleport and R.Hop(w, act.Teleport, act.Face, true) then Kit.Dodges += 1 Kit.LastDodgeAt = w.now end w.me.hum:Move(Vector3.zero, false) return end -- press the prompt of a weapon drop (the weapon choice window that opens is handled by the bot's pick step) if act.Claim and w.now - R.claimAt >= 1.2 then R.claimAt, R.claiming = w.now, w.now R.claimTries[act.Claim.Key] = (R.claimTries[act.Claim.Key] or 0) + 1 pcall(fireproximityprompt, act.Claim.Prompt) Kit.Notify('claim', 'Rage', 'claiming the weapon drop', 2, true) end equipMelee(w.now) if rage.FastMelee ~= false and not R.fastOn then R.fastOn = Kit.SetMeleeSpeed(true, rage.SpeedBoost) ~= false elseif rage.FastMelee == false and R.fastOn then Kit.SetMeleeSpeed(false) R.fastOn = false end -- long reach: the hub's Knife Extender is switched on for the run, and the hover heights follow the extra reach if (rage.LongReach or 0) > 0 and Kit.ExtenderAvailable() then if not R.extOn or R.extRange ~= rage.LongReach then R.extOn = Kit.SetExtender(true, rage.LongReach) R.extRange = rage.LongReach end elseif R.extOn then Kit.SetExtender(false) R.extOn, R.extRange = false, nil end R.ApplyLift(Kit.ExtraReach()) local reachScale = rage.ReachScale or 1 if reachScale > 1.01 and not R.reachOn then R.reachOn = Kit.SetMeleeReach(true, reachScale) if R.reachOn then -- start from the longer reach; the swing log still shrinks it if the server does not honour it s.reachEst = math.max(s.reachEst, 4.8 * reachScale) end elseif reachScale <= 1.01 and R.reachOn then Kit.SetMeleeReach(false) R.reachOn = false end R.Verify(w) if act.Teleport then if R.Hop(w, act.Teleport, act.Face) and act.GoalKey then -- a hop cut short by the distance or climb limits is a step on the way, not a failed attempt; a hop that was moved -- away from the goal (lava pushed it back) and gets us no closer is one, or the goal is tried for ever if R.lastHop then local arrived = (R.lastHop.To - act.Teleport).Magnitude < 2 local closer = (act.Teleport - R.lastHop.To).Magnitude < (act.Teleport - w.me.pos).Magnitude - 1.5 if arrived or not closer then R.tries[act.GoalKey] = (R.tries[act.GoalKey] or 0) + 1 if R.tries[act.GoalKey] >= 4 then R.blocked[act.GoalKey] = w.now + 20 R.tries[act.GoalKey] = 0 end end end end end if act.Face then faceTowards(act.Face, dt, 1) end w.me.hum:Move(Vector3.zero, false) if act.Attack and (act.AuraTarget or act.Target) and w.now - R.lastHopAt >= rage.SettleTime then tryAttack(w, act.AuraTarget or act.Target) end end -- every height the farm hovers at grows with the extra reach, so it stands as far from the mobs as the longer fists allow: -- the walkers cannot touch it, the blast of an explosive zombie does not reach it. The lift keeps LiftMargin studs inside the reach. local LIFT_DEFAULTS = { VerticalOffset = 6.0, GhostVertical = 4.6, OrbitVertical = 4.8, OrbitFlyerVertical = 3.6 } function R.ApplyLift(extra) local lift = math.min(math.max((extra or 0) - (rage.LiftMargin or 2.5), 0), rage.MaxLift or 14) if lift == (R.lift or 0) then return end if not R.liftBase then R.liftBase = {} for key, default in pairs(LIFT_DEFAULTS) do R.liftBase[key] = rage[key] or default end end R.lift = lift for key, base in pairs(R.liftBase) do rage[key] = base + lift end Kit.Lift = lift end function R.Stop() R.AbortCook() R.Hold(false) R.expected = nil R.gren = nil if R.boomOn then pcall(Kit.WatchExplosions, false) R.boomOn = false end if R.attackOn then pcall(Kit.WatchAttacks, false) R.attackOn = false end if R.slamOn then pcall(Kit.WatchSlams, false) R.slamOn = false end if R.fastOn then pcall(Kit.SetMeleeSpeed, false) R.fastOn = false end if R.reachOn then pcall(Kit.SetMeleeReach, false) R.reachOn = false end if R.extOn then pcall(Kit.SetExtender, false) R.extOn, R.extRange = false, nil end -- the hover heights go back to what they were if R.liftBase then for key, base in pairs(R.liftBase) do rage[key] = base end R.liftBase, R.lift = nil, 0 end end -- --------------------------------------------------------------- main step local function record(state, reason) if state ~= s.lastState then s.lastState = state if #B.Trace >= 40 then table.remove(B.Trace, 1) end B.Trace[#B.Trace + 1] = string.format('%.1f %s: %s', Kit.Now(), state, reason) end end function B.Step(dt) if not cfg.Enabled then return end local w = B.Snapshot() if not w then B.State, B.Reason, B.Plan = 'dead', 'waiting for a character', nil return end if cfg.PauseOnInput and userIsSteering() then s.pausedUntil = w.now + 1.2 end if w.now < s.pausedUntil then B.State, B.Reason, B.Plan = 'paused', 'you are steering', nil return end -- no weapon picked yet: nothing we swing would count, so choose one first (the tower asks at the start) local claimOpen = R.claiming ~= nil and w.now - R.claiming < 10 and Kit.PickWindow() ~= nil if cfg.AutoPick ~= false and Kit.FindTower() and (claimOpen or not Kit.HasLoadout()) then if Kit.PickWindow() then Kit.PickWeapon(claimOpen and Kit.ClaimRanking or nil) end B.State, B.Reason, B.Plan = 'loadout', claimOpen and 'choosing the claimed weapon' or 'picking a weapon', nil return end B.Calibrate(w.now) local rageOn = cfg.Mode == 'Rage' local act = rageOn and B.DecideRage(w, R) or B.Decide(w, s) B.Plan, B.State, B.Reason = act, act.State, act.Reason record(act.State, act.Reason) if act.Stop then Kit.Notify('botstop', 'Auto Tower', 'stopped: ' .. act.Reason, 1, true) B.SetEnabled(false) if Toggles and Toggles.TWBot and Toggles.TWBot.Value then Toggles.TWBot:SetValue(false) end return end if cfg.DryRun then return end if rageOn then R.Exec(w, act, dt) else R.Stop() B.Exec(w, act, dt) end end function B.Driving() return cfg.Enabled and not cfg.DryRun and B.State ~= 'off' and B.State ~= 'dead' and B.State ~= 'paused' end function B.Status() if not cfg.Enabled then return 'Auto Tower: off' end if cfg.Mode == 'Rage' then return string.format( 'Auto Tower RAGE%s: %s - %s [hop %.0f, ok %d, pulled back %d]', cfg.DryRun and ' (dry run)' or '', B.State, B.Reason, R.maxHop, R.accepted, R.rejected ) end return string.format( 'Auto Tower%s: %s - %s [reach %.1f, hits %d/%d]', cfg.DryRun and ' (dry run)' or '', B.State, B.Reason, s.reachEst, s.swingsLanded, s.swingsLanded + s.swingsMissed ) end local STATE_COLORS = { fight = Color3.fromRGB(255, 90, 90), dodge = Color3.fromRGB(255, 200, 60), avoid = Color3.fromRGB(255, 150, 40), retreat = Color3.fromRGB(255, 150, 40), trigger = Color3.fromRGB(255, 120, 40), advance = Color3.fromRGB(90, 220, 255), wait = Color3.fromRGB(200, 200, 200), hold = Color3.fromRGB(200, 200, 200), recover = Color3.fromRGB(255, 150, 40), paused = Color3.fromRGB(160, 160, 160), rage = Color3.fromRGB(255, 60, 60), } function B.Overlay(cam) if not (cfg.Enabled or cfg.DryRun) then return end local viewport = cam.ViewportSize local color = STATE_COLORS[B.State] or Color3.fromRGB(235, 235, 235) G.label(Vector2.new(viewport.X / 2, viewport.Y * 0.1), B.Status(), color, 16) local plan = B.Plan if plan and plan.Goal then local p = G.project(cam, plan.Goal) if p then G.ring(cam, plan.Goal - Vector3.new(0, 2.8, 0), 2.5, color, 2) G.seg(Vector2.new(viewport.X / 2, viewport.Y * 0.55), p, color, 1) end end if plan and plan.Spot then local p = G.project(cam, plan.Spot) if p then G.ring(cam, plan.Spot - Vector3.new(0, 2.8, 0), 1.5, color, 2) end end if plan and plan.Target then local p = G.project(cam, plan.Target.pos) if p then G.label(p - Vector2.new(0, 38), 'BOT TARGET', color, 12) end end end function B.SetEnabled(on) on = on == true cfg.Enabled = on pcall(function() RunService:UnbindFromRenderStep('TowerKitBot') end) if on then Nav.Reset() R.safeSpot = nil R.gren, R.grenadeAt = nil, nil s.lastMobAt = -10 s.pausedUntil = 0 B.State, B.Reason = 'starting', '' RunService:BindToRenderStep('TowerKitBot', Enum.RenderPriority.Input.Value + 1, B.Step) Kit.Notify('bot', 'Auto Tower', cfg.DryRun and 'dry run started (no input is sent)' or 'started', 1, true) else R.Stop() B.State, B.Reason, B.Plan = 'off', '', nil local hum = Kit.MyHum() if hum and not cfg.DryRun then hum:Move(Vector3.zero, false) end Kit.Notify('bot', 'Auto Tower', 'stopped', 1, true) end end function B.Destroy() R.Stop() cfg.Enabled = false pcall(function() RunService:UnbindFromRenderStep('TowerKitBot') end) B.State, B.Plan = 'off', nil end return B end)(RivalsRuntimeBridge.TowerKit) -- ===== Tower kit v2 :: Event tab UI ===== RivalsRuntimeBridge.TowerKit.Ui = (function(Kit) local UiApi = {} local cfg = Kit.Cfg local PRESETS = { ['Off'] = { EventEspEnabled = false, TWBot = false, TWAlerts = false }, ['ESP only'] = { EventEspEnabled = true, TWBot = false, TWAlerts = true }, ['Rage Tower'] = { EventEspEnabled = true, TWBot = true, TWAlerts = true }, } UiApi.PresetOrder = { 'Custom', 'Off', 'ESP only', 'Rage Tower' } local function applyPreset(name) local preset = PRESETS[name] if not preset then return end for id, value in pairs(preset._options or {}) do local option = Options[id] if option and option.Value ~= value then option:SetValue(value) end end for id, value in pairs(preset) do local toggle = id ~= '_options' and Toggles[id] if toggle and toggle.Value ~= value then toggle:SetValue(value) end end Kit.Notify('preset', 'Event', 'Preset applied: ' .. name, 1, true) end UiApi.ApplyPreset = applyPreset -- legacy: { State = EventZombieState, TestFire = fn, DumpStructure = fn, DumpEnum = fn } function UiApi.Build(tab, legacy) local state = legacy and legacy.State local esp, bot, rage = cfg.Esp, cfg.Bot, cfg.Rage cfg.QuietNotify = true local function toggle(group, id, text, default, tooltip, apply) local t = group:AddToggle(id, { Text = text, Default = default, Tooltip = tooltip }) t:OnChanged(GuardRivalsCallback('Tower_' .. id, apply)) return t end local function slider(group, id, text, default, min, max, suffix, apply, rounding) local s = group:AddSlider( id, { Text = text, Default = default, Min = min, Max = max, Rounding = rounding or 0, Suffix = suffix or '', Compact = true, } ) s:OnChanged(GuardRivalsCallback('Tower_' .. id, apply)) return s end local function dropdown(group, id, text, values, default, tooltip, apply) local d = group:AddDropdown(id, { Text = text, Values = values, Default = default, Multi = false, Tooltip = tooltip }) d:OnChanged(GuardRivalsCallback('Tower_' .. id, apply)) return d end -- Only the controls a normal user needs are here. Everything else (hover height, dodge tuning, standing side, pack sizes, diagnostics...) -- keeps its tuned default from core.lua; the full control set is kept in ui_full_backup.lua next to this file. -- ------------------------------------------------------------ quick start local quick = tab:AddLeftGroupbox('Quick Start') dropdown( quick, 'TWPreset', 'Preset', UiApi.PresetOrder, 'Custom', 'One click setups. They only flip the switches below, so you can tweak afterwards.', function(v) if v ~= 'Custom' then applyPreset(v) end end ) -- ------------------------------------------------------------ auto tower local botBox = tab:AddLeftGroupbox('Auto Tower') toggle( botBox, 'TWBot', 'Auto Tower', false, 'Fights the tower for you: hops to mobs, kills them, steps into wave triggers and moves on to the next floor. Press W/A/S/D to take over at any time. If the server keeps pulling you back from your hops, it switches itself off.', function(v) bot.Mode = 'Rage' Kit.Bot.SetEnabled(v) -- the bot never clicks while this menu is open (the click would land on the menu): the weapon pick sat there for minutes if v and Library and Library.Toggled then pcall(function() Library:Toggle(false) end) Kit.Notify( 'menu', 'Auto Tower', 'menu closed: the bot clicks only while it is closed (RightControl opens it again)', 1, true ) end end ) -- (there is only one mode now: the farm is always Rage) bot.Mode = 'Rage' slider(botBox, 'TWReach', 'Long Reach', 12, 0, 40, ' studs', function(v) rage.LongReach = v end) -- (the slider's default is also what the farm starts with; 12 studs = a hover 15.5 above the mobs, the measured limit is about 16) rage.LongReach = 12 toggle( botBox, 'TWRageFast', 'Max Fist Speed', true, "Turns on the hub's Weapon Attack Speed while the farm runs and puts it back afterwards.", function(v) rage.FastMelee = v end ) toggle( botBox, 'TWRageKillOrder', 'Kill In Order', true, 'Rage mode: ghosts and reapers first, then skeletons, brutes and the other big zombies, then plain zombies. Off = nearest first.', function(v) rage.KillOrder = v end ) toggle( botBox, 'TWRageLock', 'Focus One Mob Until Dead', true, 'Once a mob is picked it is fought until it is dead, then the next one. Stops the bot jumping back and forth between two mobs.', function(v) rage.TargetLock = v end ) toggle( botBox, 'TWRageClaim', 'Claim Weapon Crates', true, 'When a zombie drops a "Claim | New Weapon" crate, takes it and picks the best gun offered (Grenade Launcher first). Keeps claiming until the Grenade Launcher is in hand.', function(v) rage.ClaimWeapon = v rage.LauncherHunt = v end ) toggle( botBox, 'TWRageLauncher', 'Use Launcher And Grenades', true, 'Fires the Grenade Launcher into packs from a safe distance and throws grenades at packs that are far enough away.', function(v) rage.Launcher = v rage.Grenade = v end ) toggle( botBox, 'TWBotPause', 'Pause When I Steer', true, 'The bot stands down for a moment whenever you press W/A/S/D.', function(v) bot.PauseOnInput = v end ) -- (no stop switches any more: the farm never stops for a boss or for low health, it ends when you switch it off or the round does) bot.StopAtBoss = false bot.RetreatPct, rage.RetreatPct = 0, 0 -- --------------------------------------------------------------- queue local queue = tab:AddLeftGroupbox('Tower Queue') dropdown( queue, 'TWQueueMode', 'Tower', Kit.QueueModeOrder, cfg.QueueMode, 'Zombie Tower is the easier one: only zombies, and it has a finish. Gargoyle Tower has Explosive Zombies, Chargers, Ghosts and bosses on floors 10 and 17.', function(v) cfg.QueueMode = v end ) queue:AddButton({ Text = 'Join Tower Queue', Func = GuardRivalsCallback('Tower_Join', Kit.JoinQueue), Tooltip = 'Same call the Play page makes. Works from the lobby.', }) queue:AddButton({ Text = 'Play Again', Func = GuardRivalsCallback('Tower_Again', Kit.PlayAgain), Tooltip = "The results screen's Play Again button. A new round starts in a new server, so turn Auto Tower on again there.", }) queue:AddButton({ Text = 'Leave Match', Func = GuardRivalsCallback('Tower_LeaveMatch', Kit.LeaveMatch), Tooltip = 'Same as the Leave button on the results screen; returns you to the lobby.', }) -- --------------------------------------------------------------- mob esp local espBox = tab:AddRightGroupbox('Mob ESP') toggle( espBox, 'EventEspEnabled', 'Mob ESP', false, 'Boxes, health bars and threat tags on every mob.', function(v) esp.Enabled = v end ) dropdown( espBox, 'TWEspStyle', 'Box Style', { 'Corners', 'Box', 'Off' }, 'Corners', nil, function(v) esp.Style = v end ) toggle(espBox, 'EventEspName', 'Names + Threat Tags', true, nil, function(v) esp.Name = v end) toggle(espBox, 'EventEspHealth', 'Health Bar', true, nil, function(v) esp.Health = v end) slider(espBox, 'EventEspMaxDistance', 'Max Distance', 500, 50, 1500, ' studs', function(v) esp.MaxDistance = v end) toggle( espBox, 'TWHud', 'Tower HUD', false, 'On-screen floor, mobs alive, boss health, bot state and event progress.', function(v) cfg.Hud = v end ) toggle( espBox, 'TWAlerts', 'Attack Warnings', false, 'Ground ring and countdown for slams, charges, leaps and zaps.', function(v) Kit.SetAlerts(v) end ) -- ------------------------------------------------------------ aim assist local aimBox = tab:AddRightGroupbox('Aim') toggle( aimBox, 'EventZombieIncludeInAimbot', 'Silent Aim On Mobs', false, 'Your own shots go to mobs too. Needs the Combat > Aimbot to be enabled.', function() end ) -- A saved config never switches a tower feature on: these controls are left out of config saves and loads, so every load starts with -- Auto Tower, Mob ESP, Tower HUD, Attack Warnings and Silent Aim On Mobs off (and a changed switch is not written into the user's config either). if SaveManager and type(SaveManager.SetIgnoreIndexes) == 'function' then SaveManager:SetIgnoreIndexes({ 'TWPreset', 'TWBot', 'TWReach', 'TWRageFast', 'TWRageKillOrder', 'TWRageLock', 'TWRageClaim', 'TWRageLauncher', 'TWBotPause', 'TWQueueMode', 'EventEspEnabled', 'TWEspStyle', 'EventEspName', 'EventEspHealth', 'EventEspMaxDistance', 'TWHud', 'TWAlerts', 'EventZombieIncludeInAimbot', }) end end return UiApi end)(RivalsRuntimeBridge.TowerKit) local function EventZombieCleanup() EventZombieState.Running = false if RivalsRuntimeBridge.TowerKit then pcall(RivalsRuntimeBridge.TowerKit.Destroy) end EventZombieClearEsp() if EventZombieState.FovCircle then pcall(function() EventZombieState.FovCircle:Remove() end) end if EventZombieState.TargetDot then pcall(function() EventZombieState.TargetDot:Remove() end) end EventZombieState.FovCircle = nil EventZombieState.TargetDot = nil end local BuildRivalsAntihitUi = nil local BuildRivalsModsUi = nil local BuildRivalsVisualsUi = nil local BuildRivalsRagebotUi = nil local BuildRivalsCosmeticsUi = nil local BuildRivalsPickupUi = nil local BuildRivalsEventUi = nil local BuildRivalsMiscUi = nil local BuildRivalsUi = nil -- ====================================================================== -- Kill-seat rage (RivalsRagebot) -- ====================================================================== local RivalsRagebot = { RespawnRequestInterval = 1, RespawnBurstCount = 5, RespawnBurstDelay = 0.1, RadiusIn = 10, RadiusOut = 35, InOutToggleHz = 15, WeaponRanges = { ['Shotgun'] = { In = 5, Out = 5 }, }, HeightBase = 3, HeightWobbleAmp = 2, HeightWobbleRateHz = 2.7, OOBBufferDistance = 4, KillSeatBufferDistance = 1.5, KillSeatScoreRayLength = 8, KillSeatRefreshIntervalSec = 0.4, KillSeatBuildTtlSec = 60, KillSeatMaxKept = 24, KillSeatScoreDistanceWeight = 0.05, KillSeatRefineStep = 0.05, HostileTeleportDeltaStuds = 50, HostileTeleportMinSpeed = 150, HostileTeleportWindowSec = 5, HostileTeleportMinCount = 1, HostileTrackerSampleMinDt = 0.05, HoldCFrameStaleTtlSec = 0.15, BarrierEvictionThresholdSec = 2, BaitTrapOffsetSamples = 5, BaitTrapMinSamples = 1, BaitTrapHysteresisSec = 0.6, BaitTrapMaxCandidateDist = 60, LosRaycastParams = RaycastParams.new(), } RivalsRagebot.LosRaycastParams.FilterType = Enum.RaycastFilterType.Exclude RivalsRagebot.LosRaycastParams.IgnoreWater = true RivalsRagebot.LosRaycastParams.RespectCanCollide = false local RivalsRagebotState = { Target = nil, HoldCFrame = nil, LastHoldUpdateAt = 0, LastRespawnRequestAt = 0, PhaseClockSec = 0, KillSeats = nil, KillSeatsBuiltAt = 0, CurrentKillSeat = nil, LastKillSeatPickAt = 0, LastKnownTargetPos = nil, HostileTeleports = {}, LastWeaponName = nil, CachedRanges = nil, LastInsideBarrierAt = 0, LastBaitTrapPos = nil, LastBaitTrapPickAt = 0, } local RivalsAutoLoadoutState = { HasRunThisOpen = false, OpenNonce = 0, BoundPageControllers = {}, BoundPageFrames = {}, Profiles = {}, ConfigName = 'RIVALS Default', RefreshingDropdownValues = 0, } local RivalsCosmeticsState = { PlayerDataHooked = false, WeaponInventoryUnlockCache = setmetatable({}, { __mode = 'k' }), WeaponUnlockOriginals = {}, RefreshingDropdownValues = 0, PreviewWeaponName = nil, SelectedEditorWeaponName = nil, SelectedSkinByWeapon = {}, SelectedWrapByWeapon = {}, SelectedCharmByWeapon = {}, SelectedFinisherByWeapon = {}, SelectedWrapInvertedByWeapon = {}, StableRandomSelectionByWeapon = {}, FavoritedCosmeticsByWeapon = {}, OnlyUseFavoritesByWeapon = {}, RankCharmOverridesBySeason = {}, SelectedRankCharmSeason = nil, PendingWrapSelectionByWeapon = {}, PendingSkinSelectionByWeapon = {}, PendingCharmSelectionByWeapon = {}, PendingFinisherSelectionByWeapon = {}, PendingApplyToken = 0, RuntimeApplyRetryPending = false, PreviewViewportModel = nil, CosmeticsUiLoaded = false, CosmeticsUiLoading = false, CosmeticsConfigName = 'RIVALS Default', BoundFirstPersonModels = false, PlayerDataController = nil, PlayerDataRestore = nil, PlayerDataAddedConnection = nil, PlayerDataUtility = nil, PlayerDataUtilityGetWeaponData = nil, PlayerDataGetWeaponDataFunction = nil, PlayerDataGetWeaponDataConstantIndex = nil, PlayerDataGetWeaponDataConstantValue = nil, OriginalWrapProperties = setmetatable({}, { __mode = 'k' }), OriginalInjectedItemCosmetics = setmetatable({}, { __mode = 'k' }), WorldTripmineEntries = setmetatable({}, { __mode = 'k' }), WorldTripmineApplyTokens = setmetatable({}, { __mode = 'k' }), SpawnedWorldCosmeticEntries = setmetatable({}, { __mode = 'k' }), SpawnedWorldCosmeticApplyTokens = setmetatable({}, { __mode = 'k' }), SpawnedWorldCosmeticRetagging = setmetatable({}, { __mode = 'k' }), SavedConfigCosmeticObjects = nil, CosmeticPresetsLoaded = false, CosmeticPresets = {}, CosmeticPresetSettings = {}, CosmeticPresetAutoLoadApplied = false, CosmeticPresetSaveToken = 0, ApplyingCosmeticPreset = false, ClientViewModelHooked = false, ClientItemConstructor = nil, ClientItemPrototype = nil, ClientItemPrototypeIndex = nil, ClientItemRestoreData = {}, ViewModelSelectionSignatureByItem = setmetatable({}, { __mode = 'k' }), ClientEntityFinisherHooked = false, HookedClientEntityClass = nil, OriginalClientEntityPlayFinisher = nil, ClientEntityPlayFinisherWrapper = nil, OriginalEquippedEmotes = nil, EmoteControllerHooked = false, HookedEmoteController = nil, OriginalEmoteControllerEquipEmote = nil, OriginalEmoteControllerUseEmoteByName = nil, NativeUnlockedInventory = nil, NativeFavoritesSeeded = false, NativeUiBound = false, NativeCosmeticSlotConstructor = nil, NativeCosmeticSlotPrototype = nil, WeaponCardSkinHooked = false, NativeEquipmentStateRestore = nil, NativeInvertedValue = false, NativeFavoriteConnection = nil, NativeOnlyUseFavoritesConnection = nil, NativeSaveToken = 0, } do local TWO_PI = math.pi * 2 function RivalsRagebot.IsEnabled() return Toggles.P8S4T1 and Toggles.P8S4T1.Value == true end function RivalsRagebot.IsRespawnEnabled() return Toggles.P8S4T2 and Toggles.P8S4T2.Value == true end function RivalsRagebot.ResolveWeaponRanges() local equipped = AimbotBridge.ResolveAimbotEquippedItem() local name = equipped and equipped.Name or nil if RivalsRagebotState.CachedRanges and name == RivalsRagebotState.LastWeaponName then return RivalsRagebotState.CachedRanges end local equippedInfo = equipped and equipped.Info or nil local equippedIsMelee = equippedInfo and (equippedInfo.Class == 'Melee' or equippedInfo.Type == 'Melee') and true or false local ranges if name and RivalsRagebot.WeaponRanges[name] then ranges = RivalsRagebot.WeaponRanges[name] elseif equippedIsMelee then ranges = { In = MeleeFastFire.RagebotStuds, Out = MeleeFastFire.RagebotStuds } else ranges = { In = RivalsRagebot.RadiusIn, Out = RivalsRagebot.RadiusOut } end RivalsRagebotState.LastWeaponName = name RivalsRagebotState.CachedRanges = ranges return ranges end function RivalsRagebot.ResolveRadius() local ranges = RivalsRagebot.ResolveWeaponRanges() local toggle = math.floor(tick() * RivalsRagebot.InOutToggleHz) % 2 == 0 return toggle and ranges.In or ranges.Out end function RivalsRagebot.ResolveHeightOffset(clockSec) local t = clockSec or 0 return RivalsRagebot.HeightBase + math.sin(2 * math.pi * RivalsRagebot.HeightWobbleRateHz * t) * RivalsRagebot.HeightWobbleAmp end function RivalsRagebot.ResolveRagebotCFrame(targetRootPart, radius, heightOffset, phaseRad) local localOffset = Vector3.new(math.sin(phaseRad) * radius, heightOffset, math.cos(phaseRad) * radius) local worldPos = targetRootPart.CFrame:PointToWorldSpace(localOffset) return CFrame.lookAt(worldPos, targetRootPart.Position) end function RivalsRagebot.IsCandidatePositionInBounds(candidatePos, subject) local CollectionService = game:GetService('CollectionService') local insideSafePart = false for _, safe in ipairs(CollectionService:GetTagged('OutOfBoundsSafePart')) do if safe:IsA('BasePart') then local rel = safe.CFrame:PointToObjectSpace(candidatePos) local hx, hy, hz = safe.Size.X * 0.5, safe.Size.Y * 0.5, safe.Size.Z * 0.5 if math.abs(rel.X) <= hx and math.abs(rel.Y) <= hy and math.abs(rel.Z) <= hz then insideSafePart = true break end end end if not insideSafePart then local oobBufferSq = RivalsRagebot.OOBBufferDistance * RivalsRagebot.OOBBufferDistance for _, oob in ipairs(CollectionService:GetTagged('OutOfBoundsPart')) do if oob:IsA('BasePart') then local rel = oob.CFrame:PointToObjectSpace(candidatePos) local hx, hy, hz = oob.Size.X * 0.5, oob.Size.Y * 0.5, oob.Size.Z * 0.5 local dx = math.max(math.abs(rel.X) - hx, 0) local dy = math.max(math.abs(rel.Y) - hy, 0) local dz = math.max(math.abs(rel.Z) - hz, 0) if (dx * dx + dy * dy + dz * dz) < oobBufferSq then return false end end end end return true end local function IsPointInOOBVolume(pos) local CollectionService = game:GetService('CollectionService') for _, safe in ipairs(CollectionService:GetTagged('OutOfBoundsSafePart')) do if safe:IsA('BasePart') then local rel = safe.CFrame:PointToObjectSpace(pos) local hx, hy, hz = safe.Size.X * 0.5, safe.Size.Y * 0.5, safe.Size.Z * 0.5 if math.abs(rel.X) <= hx and math.abs(rel.Y) <= hy and math.abs(rel.Z) <= hz then return false end end end for _, oob in ipairs(CollectionService:GetTagged('OutOfBoundsPart')) do if oob:IsA('BasePart') then local rel = oob.CFrame:PointToObjectSpace(pos) local hx, hy, hz = oob.Size.X * 0.5, oob.Size.Y * 0.5, oob.Size.Z * 0.5 if math.abs(rel.X) <= hx and math.abs(rel.Y) <= hy and math.abs(rel.Z) <= hz then return true end end end return false end local function GetOOBPartAt(pos) local CollectionService = game:GetService('CollectionService') for _, safe in ipairs(CollectionService:GetTagged('OutOfBoundsSafePart')) do if safe:IsA('BasePart') then local rel = safe.CFrame:PointToObjectSpace(pos) local hx, hy, hz = safe.Size.X * 0.5, safe.Size.Y * 0.5, safe.Size.Z * 0.5 if math.abs(rel.X) <= hx and math.abs(rel.Y) <= hy and math.abs(rel.Z) <= hz then return nil end end end for _, oob in ipairs(CollectionService:GetTagged('OutOfBoundsPart')) do if oob:IsA('BasePart') then local rel = oob.CFrame:PointToObjectSpace(pos) local hx, hy, hz = oob.Size.X * 0.5, oob.Size.Y * 0.5, oob.Size.Z * 0.5 if math.abs(rel.X) <= hx and math.abs(rel.Y) <= hy and math.abs(rel.Z) <= hz then return oob end end end return nil end local KILL_SEAT_SAMPLE_DIRECTIONS = (function() local d = {} local axes = { Vector3.new(1, 0, 0), Vector3.new(-1, 0, 0), Vector3.new(0, 1, 0), Vector3.new(0, -1, 0), Vector3.new(0, 0, 1), Vector3.new(0, 0, -1), Vector3.new(1, 1, 0), Vector3.new(1, -1, 0), Vector3.new(-1, 1, 0), Vector3.new(-1, -1, 0), Vector3.new(1, 0, 1), Vector3.new(1, 0, -1), Vector3.new(-1, 0, 1), Vector3.new(-1, 0, -1), Vector3.new(0, 1, 1), Vector3.new(0, 1, -1), Vector3.new(0, -1, 1), Vector3.new(0, -1, -1), Vector3.new(1, 1, 1), Vector3.new(1, 1, -1), Vector3.new(1, -1, 1), Vector3.new(1, -1, -1), Vector3.new(-1, 1, 1), Vector3.new(-1, 1, -1), Vector3.new(-1, -1, 1), Vector3.new(-1, -1, -1), } for _, v in ipairs(axes) do d[#d + 1] = v.Unit end return d end)() local KILL_SEAT_AXIS_OFFSETS = { Vector3.new(1, 0, 0), Vector3.new(-1, 0, 0), Vector3.new(0, 1, 0), Vector3.new(0, -1, 0), Vector3.new(0, 0, 1), Vector3.new(0, 0, -1), Vector3.new(1, 1, 0), Vector3.new(1, -1, 0), Vector3.new(-1, 1, 0), Vector3.new(-1, -1, 0), Vector3.new(1, 0, 1), Vector3.new(1, 0, -1), Vector3.new(-1, 0, 1), Vector3.new(-1, 0, -1), Vector3.new(0, 1, 1), Vector3.new(0, 1, -1), Vector3.new(0, -1, 1), Vector3.new(0, -1, -1), Vector3.new(1, 1, 1), Vector3.new(1, 1, -1), Vector3.new(1, -1, 1), Vector3.new(1, -1, -1), Vector3.new(-1, 1, 1), Vector3.new(-1, 1, -1), Vector3.new(-1, -1, 1), Vector3.new(-1, -1, -1), } local KILL_SEAT_REFINE_LATTICE = (function() local lattice = {} for x = -1, 1 do for y = -1, 1 do for z = -1, 1 do if not (x == 0 and y == 0 and z == 0) then lattice[#lattice + 1] = Vector3.new(x, y, z) end end end end return lattice end)() function RivalsRagebot.ScoreSeatPosition(pos) if IsPointInOOBVolume(pos) then return -1 end local distinctParts = {} local distinctCount = 0 local hitCount = 0 local cardinalHits = 0 local rayLen = RivalsRagebot.KillSeatScoreRayLength for _, dir in ipairs(KILL_SEAT_SAMPLE_DIRECTIONS) do local samplePos = pos + dir * rayLen local hitPart = GetOOBPartAt(samplePos) if hitPart then hitCount = hitCount + 1 if not distinctParts[hitPart] then distinctParts[hitPart] = true distinctCount = distinctCount + 1 end if (math.abs(dir.X) + math.abs(dir.Y) + math.abs(dir.Z)) < 1.05 then cardinalHits = cardinalHits + 1 end end end if hitCount == 0 then return 0 end return cardinalHits * 10000 + distinctCount * 1000 + hitCount end function RivalsRagebot.RefineKillSeatPosition(seatPos) local step = RivalsRagebot.KillSeatRefineStep or 1.0 local bestPos = seatPos local bestScore = RivalsRagebot.ScoreSeatPosition(seatPos) for _, offset in ipairs(KILL_SEAT_REFINE_LATTICE) do local candidatePos = seatPos + offset * step local s = RivalsRagebot.ScoreSeatPosition(candidatePos) if s > bestScore then bestScore = s bestPos = candidatePos end end return bestPos end function RivalsRagebot.BuildKillSeats(force) local now = tick() if not force and RivalsRagebotState.KillSeats and (now - RivalsRagebotState.KillSeatsBuiltAt) < RivalsRagebot.KillSeatBuildTtlSec then return RivalsRagebotState.KillSeats end local CollectionService = game:GetService('CollectionService') local oobParts = CollectionService:GetTagged('OutOfBoundsPart') if not oobParts or #oobParts == 0 then RivalsRagebotState.KillSeats = {} RivalsRagebotState.KillSeatsBuiltAt = now return RivalsRagebotState.KillSeats end local buffer = RivalsRagebot.KillSeatBufferDistance local rayLen = RivalsRagebot.KillSeatScoreRayLength local candidates = {} for _, oob in ipairs(oobParts) do if oob:IsA('BasePart') then local size = oob.Size local cframe = oob.CFrame local hx, hy, hz = size.X * 0.5, size.Y * 0.5, size.Z * 0.5 for _, axis in ipairs(KILL_SEAT_AXIS_OFFSETS) do local localOffset = Vector3.new(axis.X * (hx + buffer), axis.Y * (hy + buffer), axis.Z * (hz + buffer)) local seatPos = cframe:PointToWorldSpace(localOffset) if not IsPointInOOBVolume(seatPos) then local distinctParts = {} local distinctCount = 0 local hitCount = 0 local cardinalHits = 0 local openSum = Vector3.zero for _, dir in ipairs(KILL_SEAT_SAMPLE_DIRECTIONS) do local samplePos = seatPos + dir * rayLen local hitPart = GetOOBPartAt(samplePos) if hitPart then hitCount = hitCount + 1 if not distinctParts[hitPart] then distinctParts[hitPart] = true distinctCount = distinctCount + 1 end if (math.abs(dir.X) + math.abs(dir.Y) + math.abs(dir.Z)) < 1.05 then cardinalHits = cardinalHits + 1 end else openSum = openSum + dir end end if hitCount > 0 then local openDir = (openSum.Magnitude > 0.001) and openSum.Unit or Vector3.new(0, 1, 0) table.insert(candidates, { Position = seatPos, Score = cardinalHits * 10000 + distinctCount * 1000 + hitCount, CardinalHits = cardinalHits, DistinctParts = distinctCount, HitCount = hitCount, OpenDirection = openDir, }) end end end end end table.sort(candidates, function(a, b) return a.Score > b.Score end) local maxKept = RivalsRagebot.KillSeatMaxKept if maxKept and #candidates > maxKept then for i = #candidates, maxKept + 1, -1 do candidates[i] = nil end end RivalsRagebotState.KillSeats = candidates RivalsRagebotState.KillSeatsBuiltAt = now return candidates end function RivalsRagebot.PickKillSeatNearestTarget(referencePos) local now = tick() if RivalsRagebotState.CurrentKillSeat and (now - RivalsRagebotState.LastKillSeatPickAt) < RivalsRagebot.KillSeatRefreshIntervalSec then return RivalsRagebotState.CurrentKillSeat end local seats = RivalsRagebot.BuildKillSeats(false) if not seats or #seats == 0 then RivalsRagebotState.CurrentKillSeat = nil RivalsRagebotState.LastKillSeatPickAt = now return nil end local bestSeat = nil local bestComposite = math.huge local distanceWeight = RivalsRagebot.KillSeatScoreDistanceWeight for _, seat in ipairs(seats) do local dist = referencePos and (seat.Position - referencePos).Magnitude or 0 local composite = -seat.Score + dist * distanceWeight if composite < bestComposite then bestComposite = composite bestSeat = seat end end local picked = nil if bestSeat then if not bestSeat.RefinedPosition then bestSeat.RefinedPosition = RivalsRagebot.RefineKillSeatPosition(bestSeat.Position) end picked = bestSeat.RefinedPosition end RivalsRagebotState.CurrentKillSeat = picked RivalsRagebotState.LastKillSeatPickAt = now return picked end function RivalsRagebot.IsBaitModeEnabled() return Toggles.P8S4T3 and Toggles.P8S4T3.Value == true end function RivalsRagebot.UpdateHostileTeleportTracker(now) local minDt = RivalsRagebot.HostileTrackerSampleMinDt if (now - (RivalsRagebotState.LastHostileTrackerSampleAt or 0)) < minDt then return end RivalsRagebotState.LastHostileTrackerSampleAt = now local hostileTeleports = RivalsRagebotState.HostileTeleports local fighters = LocalFighterController and LocalFighterController.Objects or {} for _, fighter in ipairs(fighters) do local player = fighter and fighter.Player if player and player ~= LP and player.Parent == Players and RivalsRagebot.IsCombatTarget(player) then local char = player.Character local hrp = char and char:FindFirstChild('HumanoidRootPart') if hrp and hrp.Parent then local entry = hostileTeleports[player] if not entry then entry = { LastPos = hrp.Position, LastCheckedAt = now, Teleports = {}, Offsets = {} } end hostileTeleports[player] = entry entry.SeenAt = now local dt = now - entry.LastCheckedAt if dt >= minDt then local dist = (hrp.Position - entry.LastPos).Magnitude if dist > RivalsRagebot.HostileTeleportDeltaStuds and (dist / dt) > RivalsRagebot.HostileTeleportMinSpeed then table.insert(entry.Teleports, now) local localChar = LP and LP.Character local localHrp = localChar and localChar:FindFirstChild('HumanoidRootPart') if localHrp then table.insert(entry.Offsets, { Time = now, Offset = hrp.Position - localHrp.Position }) end end entry.LastPos = hrp.Position entry.LastCheckedAt = now end local windowSec = RivalsRagebot.HostileTeleportWindowSec for index = #entry.Teleports, 1, -1 do if (now - entry.Teleports[index]) >= windowSec then table.remove(entry.Teleports, index) end end for index = #entry.Offsets, 1, -1 do if (now - entry.Offsets[index].Time) >= windowSec then table.remove(entry.Offsets, index) end end end end end for player, entry in pairs(hostileTeleports) do if entry.SeenAt ~= now then hostileTeleports[player] = nil end end end function RivalsRagebot.IsHostileCheating() local minCount = RivalsRagebot.HostileTeleportMinCount local hostileTeleports = RivalsRagebotState.HostileTeleports if not hostileTeleports then return false end for _, entry in pairs(hostileTeleports) do if entry.Teleports and #entry.Teleports >= minCount then return true end end return false end function RivalsRagebot.GetMostActiveCheater() local hostileTeleports = RivalsRagebotState.HostileTeleports if not hostileTeleports then return nil end local bestPlayer, bestCount = nil, 0 for player, entry in pairs(hostileTeleports) do local count = entry.Teleports and #entry.Teleports or 0 if count > bestCount then bestCount = count bestPlayer = player end end return bestPlayer, bestCount end function RivalsRagebot.GetCheaterOffsetPrediction(player) local entry = player and RivalsRagebotState.HostileTeleports and RivalsRagebotState.HostileTeleports[player] if not entry or not entry.Offsets then return nil end local samples = RivalsRagebot.BaitTrapOffsetSamples local minSamples = RivalsRagebot.BaitTrapMinSamples local total = #entry.Offsets if total < minSamples then return nil end local startIdx = math.max(1, total - samples + 1) local sum = Vector3.zero local count = 0 for i = startIdx, total do sum = sum + entry.Offsets[i].Offset count = count + 1 end if count == 0 then return nil end return sum / count end function RivalsRagebot.FindBaitTrapPosition(predictedOffset) if not predictedOffset then return nil end local seats = RivalsRagebot.BuildKillSeats(false) if not seats or #seats == 0 then return nil end local localChar = LP and LP.Character local localHrp = localChar and localChar:FindFirstChild('HumanoidRootPart') local refPos = localHrp and localHrp.Position or nil local maxDist = RivalsRagebot.BaitTrapMaxCandidateDist local bestSeat, bestComposite = nil, math.huge for _, seat in ipairs(seats) do if seat.Position then local landingPos = seat.Position + predictedOffset if IsPointInOOBVolume(landingPos) then local dist = refPos and (seat.Position - refPos).Magnitude or 0 if not refPos or dist <= maxDist then local composite = dist - (seat.Score * 0.0001) if composite < bestComposite then bestComposite = composite bestSeat = seat end end end end end if not bestSeat then return nil end if not bestSeat.RefinedPosition then bestSeat.RefinedPosition = RivalsRagebot.RefineKillSeatPosition(bestSeat.Position) end return bestSeat.RefinedPosition or bestSeat.Position end function RivalsRagebot.PivotToBaitTrapOrSnug() local now = tick() if RivalsRagebotState.LastBaitTrapPos and (now - (RivalsRagebotState.LastBaitTrapPickAt or 0)) < RivalsRagebot.BaitTrapHysteresisSec then local activeChar = RivalsRuntimeBridge.AcquireActiveCharacter() if activeChar then local cur = activeChar:GetPivot() activeChar:PivotTo(CFrame.new(RivalsRagebotState.LastBaitTrapPos) * (cur - cur.Position)) return true end end local cheater = RivalsRagebot.GetMostActiveCheater() local predicted = cheater and RivalsRagebot.GetCheaterOffsetPrediction(cheater) or nil local trapPos = predicted and RivalsRagebot.FindBaitTrapPosition(predicted) or nil if not trapPos then local seats = RivalsRagebot.BuildKillSeats(false) if seats and #seats > 0 then local snug = seats[1] if not snug.RefinedPosition then snug.RefinedPosition = RivalsRagebot.RefineKillSeatPosition(snug.Position) end trapPos = snug.RefinedPosition or snug.Position end end if not trapPos then return false end RivalsRagebotState.LastBaitTrapPos = trapPos RivalsRagebotState.LastBaitTrapPickAt = now local activeChar = RivalsRuntimeBridge.AcquireActiveCharacter() if not activeChar then return false end local cur = activeChar:GetPivot() activeChar:PivotTo(CFrame.new(trapPos) * (cur - cur.Position)) return true end function RivalsRagebot.ResolveRespawnRemote() local remotes = ReplicatedStorage:FindFirstChild('Remotes') local duels = remotes and remotes:FindFirstChild('Duels') local respawnNow = duels and duels:FindFirstChild('RespawnNow') if respawnNow and respawnNow:IsA('RemoteEvent') then return respawnNow end return nil end function RivalsRagebot.TryRespawn() if not RivalsRagebot.IsRespawnEnabled() then return false end local now = tick() if (now - RivalsRagebotState.LastRespawnRequestAt) < RivalsRagebot.RespawnRequestInterval then return false end local respawnNow = RivalsRagebot.ResolveRespawnRemote() if not respawnNow then return false end RivalsRagebotState.LastRespawnRequestAt = now task.spawn(GuardRivalsCallback('AutoRespawn_Burst', function() for requestIndex = 1, RivalsRagebot.RespawnBurstCount do safe_fire_server(respawnNow) if requestIndex < RivalsRagebot.RespawnBurstCount then task.wait(RivalsRagebot.RespawnBurstDelay) end end end)) return true end function RivalsRagebot.QueueRespawn() task.defer(function() RivalsRagebot.TryRespawn() end) end function RivalsRagebot.RegisterRespawnSignals() local binding = RivalsRagebotState.RespawnBinding if not binding then binding = RivalsRuntimeBridge.ReplicateHook.Reserve({ 'RespawnNowAnimation' }) if not binding then return end binding:SetHandler(function(packet) if packet.enumName ~= 'RespawnNowAnimation' or packet.args[1] ~= LP then return end RivalsRagebot.QueueRespawn() end) RivalsRagebotState.RespawnBinding = binding end binding:SetEnabled(RivalsRagebot.IsRespawnEnabled()) RivalsRuntimeBridge.ReplicateHook.RevertIfIdle() end function RivalsRagebot.IsCombatTarget(player) return IsPlayerInLocalEnvironment(player) end local SHIELD_CLAMP_RAD = math.rad(45) function RivalsRagebot.ResolveTarget(rawPhase) EnsureAimbotTargetTracking() local camera = Workspace.CurrentCamera if not camera then return nil end local mousePosition = Vector2.new(camera.ViewportSize.X * 0.5, camera.ViewportSize.Y * 0.5) local radius = RivalsRagebot.ResolveRadius() local heightOffset = RivalsRagebot.ResolveHeightOffset(RivalsRagebotState.PhaseClockSec) local observerPosition = HumanoidRootPart and HumanoidRootPart.Position local bestTarget = nil local bestHoverCFrame = nil for _, fighter in ipairs(LocalFighterController and LocalFighterController.Objects or {}) do local player = fighter and fighter.Player if player and player ~= LP and player.Parent == Players and ShouldShow(player, false) and RivalsRagebot.IsCombatTarget(player) then local entity = fighter.Entity local subject = entity and entity.Model if subject then local targetInfo = RivalsRuntimeBridge.BuildAimbotTargetInfo( subject, 'Players', 'Auto', mousePosition, player, math.huge, nil, true, camera, 'Ragebot', true ) if targetInfo then local targetRootPart = subject:FindFirstChild('HumanoidRootPart') if targetRootPart and targetRootPart.Parent then local effectivePhase = rawPhase or 0 local shieldState = EvaluateRivalsRiotShieldChecker(player, subject or player.Character, observerPosition) if shieldState and shieldState.IsShieldEquipped == true then effectivePhase = math.clamp(effectivePhase, -SHIELD_CLAMP_RAD, SHIELD_CLAMP_RAD) elseif shieldState and shieldState.HasShieldInLoadout == true then effectivePhase = math.pi + math.clamp(effectivePhase, -SHIELD_CLAMP_RAD, SHIELD_CLAMP_RAD) end local hoverCFrame = RivalsRagebot.ResolveRagebotCFrame(targetRootPart, radius, heightOffset, effectivePhase) if RivalsRagebot.IsCandidatePositionInBounds(hoverCFrame.Position, subject) then local clearLine = shieldState and shieldState.IsShieldEquipped == true if not clearLine then local rayOrigin = hoverCFrame.Position local rayDir = targetRootPart.Position - rayOrigin if rayDir.Magnitude > 0 then RivalsRagebot.LosRaycastParams.FilterDescendantsInstances = { Char, subject } clearLine = not Workspace:Raycast(rayOrigin, rayDir, RivalsRagebot.LosRaycastParams) end end if clearLine and (not bestTarget or targetInfo.worldDistance < bestTarget.worldDistance) then bestTarget = targetInfo bestHoverCFrame = hoverCFrame RivalsRagebotState.LastKnownTargetPos = targetRootPart.Position end end end end end end end return bestTarget, bestHoverCFrame end function RivalsRagebot.Update(deltaTime) local now = tick() if RivalsRagebot.IsBaitModeEnabled() then RivalsRagebot.UpdateHostileTeleportTracker(now) if RivalsRagebot.IsHostileCheating() then if RivalsRuntimeBridge.ResetZkhHubRagebot then RivalsRuntimeBridge.ResetZkhHubRagebot() end local item = AimbotBridge.ResolveAimbotEquippedItem() local shotReady = AimbotBridge.IsShotImmediatelyReady(item) if shotReady then RivalsRagebotState.PhaseClockSec = RivalsRagebotState.PhaseClockSec + (deltaTime or 0) local rawPhase = math.random() * TWO_PI local targetInfo, hoverCFrame = RivalsRagebot.ResolveTarget(rawPhase) if hoverCFrame then RivalsRagebotState.HoldCFrame = hoverCFrame RivalsRagebotState.LastHoldUpdateAt = now end local stale = (now - (RivalsRagebotState.LastHoldUpdateAt or 0)) > RivalsRagebot.HoldCFrameStaleTtlSec local holdCFrame = hoverCFrame or (not stale and RivalsRagebotState.HoldCFrame) or nil if stale then RivalsRagebotState.HoldCFrame = nil end if holdCFrame and not RivalsRagebot.IsCandidatePositionInBounds(holdCFrame.Position, nil) then RivalsRagebotState.HoldCFrame = nil holdCFrame = nil end RivalsRagebotState.Target = targetInfo or nil if holdCFrame then local activeChar = RivalsRuntimeBridge.AcquireActiveCharacter() if activeChar then local cur = activeChar:GetPivot() activeChar:PivotTo(CFrame.new(holdCFrame.Position) * (cur - cur.Position)) end return end end RivalsRagebotState.Target = nil RivalsRagebotState.HoldCFrame = nil RivalsRagebotState.LastHoldUpdateAt = 0 RivalsRagebot.PivotToBaitTrapOrSnug() return end end local enabled = RivalsRagebot.IsEnabled() if not enabled then RivalsRagebotState.Target = nil RivalsRagebotState.HoldCFrame = nil RivalsRagebotState.LastHoldUpdateAt = 0 RivalsRagebotState.PhaseClockSec = 0 RivalsRuntimeBridge.RecordCharacterLoadingCheckpoint('Ragebot', false, false) if RivalsRuntimeBridge.UpdateZkhHubRagebot then RivalsRuntimeBridge.UpdateZkhHubRagebot(deltaTime) end return end local readyToFight = RivalsRuntimeBridge.IsReadyToFight() RivalsRuntimeBridge.RecordCharacterLoadingCheckpoint('Ragebot', readyToFight, true) if RivalsRuntimeBridge.UpdateZkhHubRagebot then RivalsRuntimeBridge.UpdateZkhHubRagebot(deltaTime) end end RivalsRagebot.UpdateClassic = RivalsRagebot.Update function RivalsRagebot.Update(deltaTime) RivalsRagebot.UpdateClassic(deltaTime) if RivalsRuntimeBridge.ZHubRagebotSync then RivalsRuntimeBridge.ZHubRagebotSync(deltaTime) end if RivalsRuntimeBridge.ZHubRagebotActive ~= nil and RivalsRuntimeBridge.ZHubRagebotActive() == true then RivalsRuntimeBridge.RecordCharacterLoadingCheckpoint( 'Ragebot', RivalsRuntimeBridge.IsReadyToFight() == true, true ) end end RivalsRuntimeBridge.UpdateRagebot = RivalsRagebot.Update RivalsRuntimeBridge.RegisterAutoRespawnSignals = RivalsRagebot.RegisterRespawnSignals end RequireSharedModule('rivals_ragebot_kernel').init({ RivalsRuntimeBridge = RivalsRuntimeBridge, Options = Options, Toggles = Toggles, FighterDataCache = FighterDataCache, GetChar = function() return Char end, GetRoot = function() return HumanoidRootPart end, Genv = __ZkhHub_genv, }) do -- ====================================================================== -- Auto loadout -- ====================================================================== local RivalsAutoLoadout = { PageNames = { 'PickWeapons', 'PickWeaponsList' }, SlotOrder = { { Name = 'Primary', OptionId = 'P8S5D1', SlotIndex = 1 }, { Name = 'Secondary', OptionId = 'P8S5D2', SlotIndex = 2 }, { Name = 'Melee', OptionId = 'P8S5D3', SlotIndex = 3 }, { Name = 'Utility', OptionId = 'P8S5D4', SlotIndex = 4 }, }, OpenRetryDelay = 0.05, OpenRetryLimit = 5, Unselected = 'Unselected', Priorities = { '1', '2', '3' }, AllPriorities = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '10' }, MaxLoadouts = 10, ConfigMetadataId = 'ZkxHub_RivalsAutoLoadoutProfiles', Catalog = {}, LiveValuesBySlot = {}, BoundKeys = {}, ExcludedWeaponNames = { ['MISSING_WEAPON'] = true, }, ExcludedWeaponsBySlot = { Melee = { ['Glast Shard'] = true, }, Utility = { ['Elixir'] = true, ['RNG Dice'] = true, }, }, } function RivalsAutoLoadout.IsUnselectedAllowedForEditing(mapKey, priority) if not (Toggles and Toggles.P8S5T4 and Toggles.P8S5T4.Value == true) then return false end return mapKey ~= 'Default' or priority ~= '1' end function RivalsAutoLoadout.ShouldPreserveUnselectedInProfile() return Toggles and Toggles.P8S5T4 and Toggles.P8S5T4.Value == true end function RivalsAutoLoadout.IsEnabled() return Toggles.P8S5T1 and Toggles.P8S5T1.Value == true end function RivalsAutoLoadout.ResolveItemLibrary() local cachedItemLibrary = RivalsAutoLoadout.Catalog.ItemLibrary if cachedItemLibrary then return cachedItemLibrary end local modules = ReplicatedStorage:FindFirstChild('Modules') local itemLibraryModule = modules and modules:FindFirstChild('ItemLibrary') if not itemLibraryModule then return nil end local ok, itemLibrary = pcall(require, itemLibraryModule) if not ok or type(itemLibrary) ~= 'table' then return nil end RivalsAutoLoadout.Catalog.ItemLibrary = itemLibrary return itemLibrary end function RivalsAutoLoadout.IsAllowedWeaponName(slotName, weaponName) if type(weaponName) ~= 'string' or weaponName == '' then return false end if RivalsAutoLoadout.ExcludedWeaponNames[weaponName] == true then return false end local slotExclusions = RivalsAutoLoadout.ExcludedWeaponsBySlot[slotName] return not slotExclusions or slotExclusions[weaponName] ~= true end function RivalsAutoLoadout.ResolveSeedWeaponValues(slotName) local cacheKey = string.format('SeedWeaponValues_%s', slotName) local cachedValues = RivalsAutoLoadout.Catalog[cacheKey] if cachedValues then return cachedValues end local values = {} local slotIndex = nil for _, slotConfig in ipairs(RivalsAutoLoadout.SlotOrder) do if slotConfig.Name == slotName then slotIndex = slotConfig.SlotIndex break end end local itemLibrary = RivalsAutoLoadout.ResolveItemLibrary() local slotToClass = itemLibrary and itemLibrary.SlotToClass or nil local itemsAlphabetized = itemLibrary and itemLibrary.ItemsAlphabetized or nil local items = itemLibrary and itemLibrary.Items or nil local classData = type(slotToClass) == 'table' and slotIndex and slotToClass[slotIndex] or nil local className = classData and classData.Name or slotName if type(itemsAlphabetized) == 'table' and type(items) == 'table' then for _, weaponName in ipairs(itemsAlphabetized) do local weaponData = items[weaponName] if weaponData and weaponData.Class == className and RivalsAutoLoadout.IsAllowedWeaponName(slotName, weaponName) then values[#values + 1] = weaponName end end end if #values > 0 then RivalsAutoLoadout.Catalog[cacheKey] = values end return values end function RivalsAutoLoadout.ResolveWeaponValues(slotName) return RivalsAutoLoadout.ResolveSeedWeaponValues(slotName) end function RivalsAutoLoadout.ResolveMapValues() local cachedValues = RivalsAutoLoadout.Catalog.MapValues if cachedValues then return cachedValues end local values = {} local modules = ReplicatedStorage:FindFirstChild('Modules') local duelLibraryModule = modules and modules:FindFirstChild('DuelLibrary') if duelLibraryModule then local ok, duelLibrary = pcall(require, duelLibraryModule) if ok and type(duelLibrary) == 'table' and type(duelLibrary.MapOrder) == 'table' then for _, mapName in ipairs(duelLibrary.MapOrder) do if type(mapName) == 'string' and mapName ~= '' then values[#values + 1] = mapName end end end end if #values > 0 then RivalsAutoLoadout.Catalog.MapValues = values end return values end function RivalsAutoLoadout.ResolveDefaultWeapons() local cachedDefaults = RivalsAutoLoadout.Catalog.DefaultWeapons if cachedDefaults then return cachedDefaults end local defaults = {} local modules = ReplicatedStorage:FindFirstChild('Modules') local constantsModule = modules and (modules:FindFirstChild('CONSTANTS') or modules:FindFirstChild('Constants')) if constantsModule then local ok, constants = pcall(require, constantsModule) local configuredDefaults = ok and type(constants) == 'table' and constants.DEFAULT_WEAPONS or nil if type(configuredDefaults) == 'table' then for _, slotConfig in ipairs(RivalsAutoLoadout.SlotOrder) do local weaponName = configuredDefaults[slotConfig.SlotIndex] or configuredDefaults[slotConfig.Name] if RivalsAutoLoadout.IsAllowedWeaponName(slotConfig.Name, weaponName) then defaults[slotConfig.Name] = weaponName end end end end for _, slotConfig in ipairs(RivalsAutoLoadout.SlotOrder) do if not defaults[slotConfig.Name] then defaults[slotConfig.Name] = RivalsAutoLoadout.ResolveWeaponValues(slotConfig.Name)[1] or RivalsAutoLoadout.Unselected end end local complete = true for _, slotConfig in ipairs(RivalsAutoLoadout.SlotOrder) do if defaults[slotConfig.Name] == RivalsAutoLoadout.Unselected then complete = false end end if complete then RivalsAutoLoadout.Catalog.DefaultWeapons = defaults end return defaults end function RivalsAutoLoadout.ResolveActiveProfileKeys() local mapKey = 'Default' if Toggles.P8S5T3 and Toggles.P8S5T3.Value == true then local mapOption = Options.P8S5D7 if mapOption and type(mapOption.Value) == 'string' and mapOption.Value ~= '' then mapKey = mapOption.Value end end local priority = '1' local priorityOption = Options.P8S5D8 if priorityOption and table.find(RivalsAutoLoadout.Priorities, priorityOption.Value) then priority = priorityOption.Value end return mapKey, priority end function RivalsAutoLoadout.ResolveProfile(mapKey, priority, create) mapKey = type(mapKey) == 'string' and mapKey ~= '' and mapKey or 'Default' priority = table.find(RivalsAutoLoadout.AllPriorities, priority) and priority or '1' local mapProfiles = RivalsAutoLoadoutState.Profiles[mapKey] if not mapProfiles and create then mapProfiles = {} RivalsAutoLoadoutState.Profiles[mapKey] = mapProfiles end local profile = mapProfiles and mapProfiles[priority] or nil if not profile and create then profile = {} local defaultWeapons = RivalsAutoLoadout.ResolveDefaultWeapons() for _, slotConfig in ipairs(RivalsAutoLoadout.SlotOrder) do profile[slotConfig.Name] = mapKey == 'Default' and priority == '1' and defaultWeapons[slotConfig.Name] or RivalsAutoLoadout.Unselected end mapProfiles[priority] = profile end return profile end function RivalsAutoLoadout.ResolveActiveProfile(create) local mapKey, priority = RivalsAutoLoadout.ResolveActiveProfileKeys() return RivalsAutoLoadout.ResolveProfile(mapKey, priority, create), mapKey, priority end function RivalsAutoLoadout.NormalizeSelection(slotConfig, selection, allowUnselected) local values = RivalsAutoLoadout.ResolveWeaponValues(slotConfig.Name) if type(selection) == 'string' then if allowUnselected and selection == RivalsAutoLoadout.Unselected then return selection end if table.find(values, selection) then return selection end elseif type(selection) == 'table' then for _, weaponName in ipairs(values) do if selection[weaponName] == true or table.find(selection, weaponName) then return weaponName end end end if allowUnselected then return RivalsAutoLoadout.Unselected end return RivalsAutoLoadout.ResolveDefaultWeapons()[slotConfig.Name] or values[1] or RivalsAutoLoadout.Unselected end function RivalsAutoLoadout.ResetState() RivalsAutoLoadoutState.HasRunThisOpen = false RivalsAutoLoadoutState.OpenNonce = RivalsAutoLoadoutState.OpenNonce + 1 end function RivalsAutoLoadout.ResolvePagesFrame() local playerGui = LP:FindFirstChild('PlayerGui') local mainGui = playerGui and playerGui:FindFirstChild('MainGui') local mainFrame = mainGui and mainGui:FindFirstChild('MainFrame') return mainFrame and mainFrame:FindFirstChild('Pages') or nil end function RivalsAutoLoadout.ResolvePageFrame(pageName) if type(pageName) ~= 'string' or pageName == '' then return nil end local pagesFrame = RivalsAutoLoadout.ResolvePagesFrame() local pageFrame = pagesFrame and pagesFrame:FindFirstChild(pageName) if pageFrame and pageFrame:IsA('GuiObject') then return pageFrame end return nil end function RivalsAutoLoadout.IsPageFrameVisible(pageFrame) return pageFrame and pageFrame.Visible == true and pageFrame.AbsoluteSize.X > 0 and pageFrame.AbsoluteSize.Y > 0 end function RivalsAutoLoadout.ResolveSavedSelection(slotConfig) if not slotConfig then return nil end local profile, mapKey, priority = RivalsAutoLoadout.ResolveActiveProfile(true) local allowUnselected = RivalsAutoLoadout.IsUnselectedAllowedForEditing(mapKey, priority) local stored = profile[slotConfig.Name] local savedSelection = RivalsAutoLoadout.NormalizeSelection(slotConfig, stored, allowUnselected) if stored ~= RivalsAutoLoadout.Unselected then profile[slotConfig.Name] = savedSelection end return savedSelection end function RivalsAutoLoadout.SyncSavedSelection(slotConfig) if not slotConfig then return end local option = Options[slotConfig.OptionId] if not option then return end local profile, mapKey, priority = RivalsAutoLoadout.ResolveActiveProfile(true) if RivalsAutoLoadout.BoundKeys[slotConfig.Name] ~= mapKey .. '|' .. priority then return end local allowUnselected = RivalsAutoLoadout.IsUnselectedAllowedForEditing(mapKey, priority) profile[slotConfig.Name] = RivalsAutoLoadout.NormalizeSelection(slotConfig, option.Value, allowUnselected) end function RivalsAutoLoadout.ResolveVisibleSelectionValues(slotConfig, values) local savedSelection = RivalsAutoLoadout.ResolveSavedSelection(slotConfig) if table.find(values or {}, savedSelection) then return savedSelection end return (values or {})[1] end function RivalsAutoLoadout.ResolvePageController(pageName) if type(pageName) ~= 'string' or pageName == '' then return nil end local cachedPageController = RivalsAutoLoadout.Catalog[pageName] if cachedPageController then return cachedPageController end local playerScripts = LP:FindFirstChild('PlayerScripts') local modules = playerScripts and playerScripts:FindFirstChild('Modules') local pages = modules and modules:FindFirstChild('Pages') local pageModule = pages and pages:FindFirstChild(pageName) if not pageModule then return nil end local ok, pageController = pcall(require, pageModule) if not ok or type(pageController) ~= 'table' then return nil end RivalsAutoLoadout.Catalog[pageName] = pageController return pageController end function RivalsAutoLoadout.ResolveOpenPageController() for _, pageName in ipairs(RivalsAutoLoadout.PageNames) do local pageFrame = RivalsAutoLoadout.ResolvePageFrame(pageName) if RivalsAutoLoadout.IsPageFrameVisible(pageFrame) then local pageController = RivalsAutoLoadout.ResolvePageController(pageName) if pageController then return pageName, pageController end end end return nil, nil end function RivalsAutoLoadout.ReadVisibleWeaponValues(pageController) if not pageController then return nil, nil end local slotConfig = RivalsAutoLoadout.SlotOrder[pageController._current_slot] if not slotConfig then return nil, nil end local listFrame = pageController.List local container = listFrame and listFrame:FindFirstChild('Container') if not container then return slotConfig, nil end local orderedValues = {} for _, child in ipairs(container:GetChildren()) do if child.Name == 'WeaponSlot' and child.Visible ~= false then local button = child:FindFirstChild('Button') local title = button and button:FindFirstChild('Title') local locked = button and button:FindFirstChild('Locked') local banned = button and button:FindFirstChild('Banned') local weaponName = title and title.Text or nil if type(weaponName) == 'string' then weaponName = weaponName:match('^%s*(.-)%s*$') end if RivalsAutoLoadout.IsAllowedWeaponName(slotConfig.Name, weaponName) and (not locked or locked.Visible ~= true) and (not banned or banned.Visible ~= true) then orderedValues[#orderedValues + 1] = { Name = weaponName, Order = child.LayoutOrder, } end end end table.sort(orderedValues, function(left, right) if left.Order == right.Order then return left.Name < right.Name end return left.Order < right.Order end) local seen = {} local values = {} for _, entry in ipairs(orderedValues) do if seen[entry.Name] ~= true then seen[entry.Name] = true values[#values + 1] = entry.Name end end return slotConfig, values end function RivalsAutoLoadout.RefreshDropdownOption(slotConfig) if not slotConfig then return end local option = Options[slotConfig.OptionId] if not option or type(option.SetValues) ~= 'function' then return end local sourceValues = RivalsAutoLoadout.ResolveWeaponValues(slotConfig.Name) local _, mapKey, priority = RivalsAutoLoadout.ResolveActiveProfile(true) local values = {} if RivalsAutoLoadout.IsUnselectedAllowedForEditing(mapKey, priority) then values[#values + 1] = RivalsAutoLoadout.Unselected end for _, weaponName in ipairs(sourceValues) do values[#values + 1] = weaponName end local visibleSelectionValue = RivalsAutoLoadout.ResolveVisibleSelectionValues(slotConfig, values) RivalsAutoLoadoutState.RefreshingDropdownValues = RivalsAutoLoadoutState.RefreshingDropdownValues + 1 pcall(function() option:SetValues(values) end) if type(option.SetValue) == 'function' then pcall(function() option:SetValue(visibleSelectionValue) end) end RivalsAutoLoadout.BoundKeys[slotConfig.Name] = mapKey .. '|' .. priority RivalsAutoLoadoutState.RefreshingDropdownValues = RivalsAutoLoadoutState.RefreshingDropdownValues - 1 end function RivalsAutoLoadout.RefreshDropdownOptions() for _, slotConfig in ipairs(RivalsAutoLoadout.SlotOrder) do RivalsAutoLoadout.RefreshDropdownOption(slotConfig) end end function RivalsAutoLoadout.CapturePageWeaponValues(pageController) local slotConfig, values = RivalsAutoLoadout.ReadVisibleWeaponValues(pageController) if not slotConfig or type(values) ~= 'table' or #values <= 0 then return false end local previousValues = RivalsAutoLoadout.LiveValuesBySlot[slotConfig.Name] local hasChanged = type(previousValues) ~= 'table' or #previousValues ~= #values if not hasChanged and previousValues then for index, weaponName in ipairs(values) do if previousValues[index] ~= weaponName then hasChanged = true break end end end if not hasChanged then return false end RivalsAutoLoadout.LiveValuesBySlot[slotConfig.Name] = values RivalsAutoLoadout.RefreshDropdownOption(slotConfig) return true end function RivalsAutoLoadout.BindChosenSlotInputs(pageName, pageController) local chosenWeaponSlots = pageController and pageController._chosen_weapon_slots if type(chosenWeaponSlots) ~= 'table' then return end for slotIndex, chosenSlot in ipairs(chosenWeaponSlots) do local button = chosenSlot and chosenSlot:FindFirstChild('Button') if button and button:IsA('GuiButton') then Connections:register( string.format('AutoLoadout_%s_ChosenSlot_%d', pageName, slotIndex), button.Activated:Connect( GuardRivalsCallback(string.format('AutoLoadout_%s_ChosenSlot_%d', pageName, slotIndex), function() task.defer(function() RivalsAutoLoadout.CapturePageWeaponValues(pageController) end) end) ) ) end end end function RivalsAutoLoadout.ResolveSlotSelection(slotConfig) if not slotConfig then return nil end local selectedWeaponName = RivalsAutoLoadout.ResolveSavedSelection(slotConfig) return selectedWeaponName ~= RivalsAutoLoadout.Unselected and selectedWeaponName or nil end function RivalsAutoLoadout.IsWeaponSelected(slotConfig, weaponName) if not slotConfig or type(weaponName) ~= 'string' or weaponName == '' then return false end local savedSelection = RivalsAutoLoadout.ResolveSavedSelection(slotConfig) return savedSelection == weaponName end function RivalsAutoLoadout.ResolveCurrentMapName() local playerScripts = LP:FindFirstChild('PlayerScripts') local controllers = playerScripts and playerScripts:FindFirstChild('Controllers') local duelControllerModule = controllers and controllers:FindFirstChild('DuelController') if duelControllerModule and IsReplicatedStateReady() then local ok, duelController = pcall(require, duelControllerModule) local duel = ok and type(duelController) == 'table' and duelController.CurrentObject or nil if duel then if type(duel.GetMapName) == 'function' then local mapOk, mapName = pcall(duel.GetMapName, duel) if mapOk and type(mapName) == 'string' and mapName ~= '' then return mapName end end if type(duel.Get) == 'function' then for _, key in ipairs({ 'MapName', 'Map' }) do local mapOk, mapName = pcall(duel.Get, duel, key) if mapOk and type(mapName) == 'string' and mapName ~= '' then return mapName end end end local mapName = duel.MapName or duel.Map if type(mapName) == 'string' and mapName ~= '' then return mapName end end end for _, attributeName in ipairs({ 'MapName', 'MatchMap' }) do local mapName = Workspace:GetAttribute(attributeName) if type(mapName) == 'string' and mapName ~= '' then return mapName end end return nil end function RivalsAutoLoadout.ResolveRuntimeMapKey() if Toggles.P8S5T3 and Toggles.P8S5T3.Value == true then return RivalsAutoLoadout.ResolveCurrentMapName() end return 'Default' end function RivalsAutoLoadout.IsRuntimeSelectionAvailable(slotConfig, weaponName) if not slotConfig or type(weaponName) ~= 'string' or weaponName == '' or weaponName == RivalsAutoLoadout.Unselected then return false end local liveValues = RivalsAutoLoadout.LiveValuesBySlot[slotConfig.Name] return type(liveValues) ~= 'table' or #liveValues == 0 or table.find(liveValues, weaponName) ~= nil end function RivalsAutoLoadout.ResolveSelectedWeaponName(slotConfig, profile) if not slotConfig or type(profile) ~= 'table' then return nil end local allowUnselected = RivalsAutoLoadout.ShouldPreserveUnselectedInProfile() local selectedWeaponName = RivalsAutoLoadout.NormalizeSelection(slotConfig, profile[slotConfig.Name], allowUnselected) return RivalsAutoLoadout.IsRuntimeSelectionAvailable(slotConfig, selectedWeaponName) and selectedWeaponName or nil end function RivalsAutoLoadout.BuildChosenWeapons(maxSlots) local mapKey = RivalsAutoLoadout.ResolveRuntimeMapKey() if not mapKey then return nil end local chosenWeapons = {} if Toggles.P8S5T4 and Toggles.P8S5T4.Value == true then for priorityIndex = #RivalsAutoLoadout.Priorities, 1, -1 do local priority = RivalsAutoLoadout.Priorities[priorityIndex] local profile = RivalsAutoLoadout.ResolveProfile(mapKey, priority, true) for slotIndex = 1, maxSlots do local slotConfig = RivalsAutoLoadout.SlotOrder[slotIndex] local selectedWeaponName = RivalsAutoLoadout.ResolveSelectedWeaponName(slotConfig, profile) if selectedWeaponName then chosenWeapons[slotConfig.SlotIndex] = selectedWeaponName end end end else local priority = '1' local priorityOption = Options.P8S5D8 if priorityOption and table.find(RivalsAutoLoadout.Priorities, priorityOption.Value) then priority = priorityOption.Value end local profile = RivalsAutoLoadout.ResolveProfile(mapKey, priority, true) for slotIndex = 1, maxSlots do local slotConfig = RivalsAutoLoadout.SlotOrder[slotIndex] local selectedWeaponName = RivalsAutoLoadout.ResolveSelectedWeaponName(slotConfig, profile) if not selectedWeaponName then return nil end chosenWeapons[slotConfig.SlotIndex] = selectedWeaponName end end for slotIndex = 1, maxSlots do local slotConfig = RivalsAutoLoadout.SlotOrder[slotIndex] if not chosenWeapons[slotConfig.SlotIndex] then return nil end end return chosenWeapons end function RivalsAutoLoadout.ApplyPageController(pageController) if not pageController or pageController._is_open ~= true then return nil end local pageFrame = pageController.PageFrame if not pageFrame or pageFrame.Visible ~= true or pageFrame.AbsoluteSize.X <= 0 or pageFrame.AbsoluteSize.Y <= 0 then return nil end local chosenWeaponSlots = pageController._chosen_weapon_slots if type(chosenWeaponSlots) ~= 'table' then return nil end local maxSlots = math.min(#pageController._chosen_weapon_slots, #RivalsAutoLoadout.SlotOrder) if maxSlots <= 0 then return nil end RivalsAutoLoadout.CapturePageWeaponValues(pageController) local chosenWeapons = RivalsAutoLoadout.BuildChosenWeapons(maxSlots) if not chosenWeapons then return false end pageController._chosen_weapons = chosenWeapons local ok, finishError = pcall(pageController.Finish, pageController) if not ok then ReportRivalsRuntimeIssue('auto_loadout_finish', finishError) end return ok end function RivalsAutoLoadout.ScheduleSubmit(pageName, pageController, openNonce, attempt) if not pageController then return end if openNonce ~= RivalsAutoLoadoutState.OpenNonce then return end if not RivalsAutoLoadout.IsEnabled() or RivalsAutoLoadoutState.HasRunThisOpen then return end local appliedPage = RivalsAutoLoadout.ApplyPageController(pageController) if appliedPage == true then RivalsAutoLoadoutState.HasRunThisOpen = true return end if attempt >= RivalsAutoLoadout.OpenRetryLimit then return end task.delay( RivalsAutoLoadout.OpenRetryDelay, GuardRivalsCallback( string.format('AutoLoadout_%s_Retry_%d', pageName, attempt), function() RivalsAutoLoadout.ScheduleSubmit(pageName, pageController, openNonce, attempt + 1) end ) ) end function RivalsAutoLoadout.QueuePageControllerSubmit(pageName, pageController) if not pageController or not RivalsAutoLoadout.IsEnabled() then return end -- Manual Loadout (Ranked): the choice is yours in ranked matches if RivalsRuntimeBridge.IsManualLoadoutActive() then return end local openNonce = RivalsAutoLoadoutState.OpenNonce + 1 RivalsAutoLoadoutState.OpenNonce = openNonce RivalsAutoLoadoutState.HasRunThisOpen = false task.defer( GuardRivalsCallback( string.format('AutoLoadout_%s_Open', pageName), function() RivalsAutoLoadout.ScheduleSubmit(pageName, pageController, openNonce, 1) end ) ) end function RivalsAutoLoadout.QueueVisiblePageSubmit() if not RivalsAutoLoadout.IsEnabled() then return end local pageName, pageController = RivalsAutoLoadout.ResolveOpenPageController() if not pageController then return end RivalsAutoLoadout.QueuePageControllerSubmit(pageName, pageController) end function RivalsAutoLoadout.BindPageController(pageName, pageController) local boundPageControllers = RivalsAutoLoadoutState.BoundPageControllers if boundPageControllers[pageName] == true then return end pageController = pageController or RivalsAutoLoadout.ResolvePageController(pageName) if not pageController then return end boundPageControllers[pageName] = true RivalsAutoLoadout.BindChosenSlotInputs(pageName, pageController) end function RivalsAutoLoadout.HandlePageFrameVisibility(pageName) local pageFrame = RivalsAutoLoadout.ResolvePageFrame(pageName) if not RivalsAutoLoadout.IsPageFrameVisible(pageFrame) then RivalsAutoLoadout.ResetState() return end local pageController = RivalsAutoLoadout.ResolvePageController(pageName) if not pageController then return end RivalsAutoLoadout.BindPageController(pageName, pageController) RivalsAutoLoadout.CapturePageWeaponValues(pageController) RivalsAutoLoadout.QueuePageControllerSubmit(pageName, pageController) end function RivalsAutoLoadout.BindPageFrame(pageName, pageFrame) local boundPageFrames = RivalsAutoLoadoutState.BoundPageFrames if boundPageFrames[pageName] == true then return end pageFrame = pageFrame or RivalsAutoLoadout.ResolvePageFrame(pageName) if not pageFrame then return end boundPageFrames[pageName] = true Connections:register( string.format('AutoLoadout_%s_Visible', pageName), pageFrame:GetPropertyChangedSignal('Visible'):Connect( GuardRivalsCallback( string.format('AutoLoadout_%s_Visible', pageName), function() RivalsAutoLoadout.HandlePageFrameVisibility(pageName) end ) ) ) if RivalsAutoLoadout.IsPageFrameVisible(pageFrame) then RivalsAutoLoadout.HandlePageFrameVisibility(pageName) end end function RivalsAutoLoadout.BindPageControllers() task.spawn(GuardRivalsCallback('AutoLoadout_BindControllers', function() local playerGui = LP:WaitForChild('PlayerGui') local mainGui = playerGui:WaitForChild('MainGui') local mainFrame = mainGui:WaitForChild('MainFrame') local pagesFrame = mainFrame:WaitForChild('Pages') Connections:register( 'AutoLoadout_PagesChildAdded', pagesFrame.ChildAdded:Connect(GuardRivalsCallback('AutoLoadout_PagesChildAdded', function(child) if not child or not child:IsA('GuiObject') then return end local pageFrame = child for _, pageName in ipairs(RivalsAutoLoadout.PageNames) do if pageName == pageFrame.Name then RivalsAutoLoadout.BindPageFrame(pageName, pageFrame) break end end end)) ) for _, pageName in ipairs(RivalsAutoLoadout.PageNames) do RivalsAutoLoadout.BindPageFrame(pageName) end end)) end function RivalsAutoLoadout.NormalizeProfiles(profiles) local normalized = {} if type(profiles) == 'table' then for mapKey, mapProfiles in pairs(profiles) do if type(mapKey) == 'string' and mapKey ~= '' and type(mapProfiles) == 'table' then local normalizedMap = {} for _, priority in ipairs(RivalsAutoLoadout.AllPriorities) do local profile = mapProfiles[priority] if type(profile) == 'table' then local normalizedProfile = {} for _, slotConfig in ipairs(RivalsAutoLoadout.SlotOrder) do normalizedProfile[slotConfig.Name] = RivalsAutoLoadout.NormalizeSelection( slotConfig, profile[slotConfig.Name] or profile[slotConfig.SlotIndex], mapKey ~= 'Default' or priority ~= '1' ) end normalizedMap[priority] = normalizedProfile end end if next(normalizedMap) ~= nil then normalized[mapKey] = normalizedMap end end end end RivalsAutoLoadoutState.Profiles = normalized RivalsAutoLoadout.ResolveProfile('Default', '1', true) return normalized end function RivalsAutoLoadout.SetConfigName(configName) if type(configName) == 'string' and configName ~= '' then RivalsAutoLoadoutState.ConfigName = configName end end function RivalsAutoLoadout.LoadProfilesFromConfig() local configPath = ScriptPaths.config_path(RivalsAutoLoadoutState.ConfigName or 'RIVALS Default') if not isfile(configPath) then RivalsAutoLoadout.NormalizeProfiles({}) RivalsAutoLoadout.RefreshDropdownOptions() RivalsAutoLoadout.QueueVisiblePageSubmit() return false end local readOk, rawConfig = pcall(readfile, configPath) if not readOk or type(rawConfig) ~= 'string' then return false end local decodeOk, decoded = pcall(function() return HttpService:JSONDecode(rawConfig) end) local objects = decodeOk and type(decoded) == 'table' and decoded.objects or nil if type(objects) ~= 'table' then return false end local profiles = nil for _, object in ipairs(objects) do if object and object.idx == RivalsAutoLoadout.ConfigMetadataId then profiles = object.value break end end RivalsAutoLoadout.NormalizeProfiles(profiles or {}) RivalsAutoLoadout.RefreshDropdownOptions() RivalsAutoLoadout.QueueVisiblePageSubmit() return profiles ~= nil end function RivalsAutoLoadout.SaveProfilesToConfig() local configPath = ScriptPaths.config_path(RivalsAutoLoadoutState.ConfigName or 'RIVALS Default') if not isfile(configPath) then return false end local readOk, rawConfig = pcall(readfile, configPath) if not readOk or type(rawConfig) ~= 'string' then return false end local decodeOk, decoded = pcall(function() return HttpService:JSONDecode(rawConfig) end) if not decodeOk or type(decoded) ~= 'table' then return false end local existingObjects = type(decoded.objects) == 'table' and decoded.objects or {} local mergedObjects = {} for _, object in ipairs(existingObjects) do if not object or object.idx ~= RivalsAutoLoadout.ConfigMetadataId then mergedObjects[#mergedObjects + 1] = object end end mergedObjects[#mergedObjects + 1] = { idx = RivalsAutoLoadout.ConfigMetadataId, type = 'ZkxHubMetadata', value = RivalsAutoLoadout.NormalizeProfiles(RivalsAutoLoadoutState.Profiles), } decoded.objects = mergedObjects return pcall(function() writefile(configPath, HttpService:JSONEncode(decoded)) end) end -- replaced once the settings object exists; true while a config is being applied RivalsRuntimeBridge.IsConfigLoading = function() return false end RivalsRuntimeBridge.IsManualLoadoutActive = function() return false end RivalsRuntimeBridge.BindAutoLoadoutControllers = RivalsAutoLoadout.BindPageControllers RivalsRuntimeBridge.QueueAutoLoadoutSubmission = RivalsAutoLoadout.QueueVisiblePageSubmit RivalsRuntimeBridge.ResolveAutoLoadoutWeaponValues = RivalsAutoLoadout.ResolveWeaponValues RivalsRuntimeBridge.ResolveAutoLoadoutDefaultWeapon = function(slotName) return RivalsAutoLoadout.ResolveDefaultWeapons()[slotName] end RivalsRuntimeBridge.ResolveAutoLoadoutMapValues = RivalsAutoLoadout.ResolveMapValues RivalsRuntimeBridge.ResolveAutoLoadoutPriorities = function() return RivalsAutoLoadout.Priorities end RivalsRuntimeBridge.AutoLoadoutAllPriorities = function() return RivalsAutoLoadout.AllPriorities end RivalsRuntimeBridge.SetAutoLoadoutCount = function(count) count = math.clamp(math.floor(tonumber(count) or 3), 1, RivalsAutoLoadout.MaxLoadouts) local active = RivalsAutoLoadout.Priorities for index = #active, 1, -1 do active[index] = nil end for index = 1, count do active[index] = RivalsAutoLoadout.AllPriorities[index] end return count end RivalsRuntimeBridge.RefreshAutoLoadoutDropdownValues = RivalsAutoLoadout.RefreshDropdownOptions RivalsRuntimeBridge.SyncAutoLoadoutSelection = function(optionId) for _, slotConfig in ipairs(RivalsAutoLoadout.SlotOrder) do if slotConfig.OptionId == optionId then RivalsAutoLoadout.SyncSavedSelection(slotConfig) break end end end RivalsRuntimeBridge.SetAutoLoadoutConfigName = RivalsAutoLoadout.SetConfigName RivalsRuntimeBridge.LoadAutoLoadoutProfiles = RivalsAutoLoadout.LoadProfilesFromConfig RivalsRuntimeBridge.SaveAutoLoadoutProfiles = RivalsAutoLoadout.SaveProfilesToConfig RivalsRuntimeBridge.ResetAutoLoadoutState = RivalsAutoLoadout.ResetState end do -- ====================================================================== -- Auto ban / map vote -- ====================================================================== local RivalsAutoBan = { State = { Controller = nil, BoundDuel = nil, LastVoteType = nil, LastSubmitted = nil, }, } function RivalsAutoBan.IsEnabled() return Toggles.P8S5T2 and Toggles.P8S5T2.Value == true end function RivalsAutoBan.IsMapVoteEnabled() return Toggles.P8S5T5 and Toggles.P8S5T5.Value == true end function RivalsAutoBan.IsAnyEnabled() return RivalsAutoBan.IsEnabled() or RivalsAutoBan.IsMapVoteEnabled() end function RivalsAutoBan.Read(object, key) if not object then return nil end if type(object.Get) == 'function' then local ok, value = pcall(object.Get, object, key) if ok then return value end end return object[key] end function RivalsAutoBan.ResolveController() if RivalsAutoBan.State.Controller then return RivalsAutoBan.State.Controller end if not IsReplicatedStateReady() then return nil end local playerScripts = LP:FindFirstChild('PlayerScripts') local controllers = playerScripts and playerScripts:FindFirstChild('Controllers') local controllerModule = controllers and controllers:FindFirstChild('DuelController') if not controllerModule then return nil end local ok, controller = pcall(require, controllerModule) if not ok or type(controller) ~= 'table' then return nil end RivalsAutoBan.State.Controller = controller return controller end function RivalsAutoBan.ResolveWeaponValues() local values = {} local seen = {} for _, slotName in ipairs({ 'Primary', 'Secondary', 'Melee', 'Utility' }) do for _, weaponName in ipairs(RivalsRuntimeBridge.ResolveAutoLoadoutWeaponValues(slotName)) do if type(weaponName) == 'string' and weaponName ~= '' and not seen[weaponName] then seen[weaponName] = true table.insert(values, weaponName) end end end table.sort(values) return values end function RivalsAutoBan.IsSelected(optionId, weaponName) local option = Options[optionId] local selected = option and option.Value if type(selected) ~= 'table' then return false end if selected[weaponName] == true then return true end for _, selectedName in ipairs(selected) do if selectedName == weaponName then return true end end return false end function RivalsAutoBan.ResolveMapValues() return RivalsRuntimeBridge.ResolveAutoLoadoutMapValues() end function RivalsAutoBan.IsMapSelected(mapName) return RivalsAutoBan.IsSelected('P8S5D9', mapName) end function RivalsAutoBan.ResolveLocalDueler(duel) if type(duel.GetDueler) ~= 'function' then return nil end local ok, dueler = pcall(duel.GetDueler, duel, LP) if ok and dueler then return dueler end ok, dueler = pcall(duel.GetDueler, duel, LP.UserId) return ok and dueler or nil end function RivalsAutoBan.IsOptionBanned(voteOptions, optionName) if type(voteOptions) ~= 'table' or optionName == nil then return false end for _, option in pairs(voteOptions) do if RivalsAutoBan.Read(option, 'Name') == optionName then return RivalsAutoBan.Read(option, 'IsBanned') ~= nil end end return false end function RivalsAutoBan.BansRemaining(duel, voteType) local localDueler = RivalsAutoBan.ResolveLocalDueler(duel) local teamId = RivalsAutoBan.Read(localDueler, 'TeamID') if teamId == nil then return 0 end local maxKey = voteType == 'Weapons' and 'MaxWeaponBansPerTeam' or 'MaxMapBansPerTeam' local maxBans = tonumber(RivalsAutoBan.Read(duel, maxKey)) or 0 local bansByTeam = RivalsAutoBan.Read(duel, 'VoteBansRemaining') local bansUsed = type(bansByTeam) == 'table' and tonumber(bansByTeam[teamId]) or 0 return maxBans - bansUsed end function RivalsAutoBan.TryVote() if not RivalsAutoBan.IsAnyEnabled() then return false, 'disabled' end local controller = RivalsAutoBan.ResolveController() local duel = RivalsAutoBan.State.BoundDuel or (controller and controller.CurrentObject) if not duel then RivalsAutoBan.State.LastVoteType = nil RivalsAutoBan.State.LastSubmitted = nil return false, 'no_duel' end local voteType = RivalsAutoBan.Read(duel, 'VoteOptionsType') if voteType ~= RivalsAutoBan.State.LastVoteType then RivalsAutoBan.State.LastVoteType = voteType RivalsAutoBan.State.LastSubmitted = nil end if voteType == nil then return false, 'no_vote' end local voteOptions = RivalsAutoBan.Read(duel, 'VoteOptions') if type(voteOptions) ~= 'table' or next(voteOptions) == nil then return false, 'no_vote' end if RivalsAutoBan.State.LastSubmitted ~= nil and not RivalsAutoBan.IsOptionBanned(voteOptions, RivalsAutoBan.State.LastSubmitted) then return false, 'already_voted' end local remotes = ReplicatedStorage:FindFirstChild('Remotes') local duels = remotes and remotes:FindFirstChild('Duels') local voteRemote = duels and duels:FindFirstChild('Vote') if not voteRemote or not voteRemote:IsA('RemoteEvent') then return false, 'no_vote_remote' end if voteType == 'Maps' then if not RivalsAutoBan.IsMapVoteEnabled() then return false, 'map_vote_disabled' end if not RivalsAutoBan.ResolveLocalDueler(duel) then return false, 'not_dueler' end if RivalsAutoBan.Read(duel, 'MaxMapBansPerTeam') ~= nil and RivalsAutoBan.BansRemaining(duel, 'Maps') <= 0 then return false, 'bans_complete' end for _, option in pairs(voteOptions) do local mapName = RivalsAutoBan.Read(option, 'Name') if mapName and RivalsAutoBan.Read(option, 'IsBanned') == nil and RivalsAutoBan.IsMapSelected(mapName) then RivalsAutoBan.State.LastSubmitted = mapName local ok, err = pcall(voteRemote.FireServer, voteRemote, mapName) if not ok then RivalsAutoBan.State.LastSubmitted = nil return false, tostring(err) end return true end end return false, 'no_selected_map' end if voteType ~= 'Weapons' or not RivalsAutoBan.IsEnabled() then return false, 'no_weapon_vote' end local bansRemaining = RivalsAutoBan.BansRemaining(duel, 'Weapons') if bansRemaining <= 0 then return false, 'bans_complete' end local selectionIds = { 'P8S5D5', 'P8S5D6' } local selectionId = selectionIds[#selectionIds - bansRemaining + 1] if not selectionId then return false, 'no_ban_list' end for _, option in pairs(voteOptions) do local weaponName = RivalsAutoBan.Read(option, 'Name') if weaponName and RivalsAutoBan.Read(option, 'IsBanned') == nil and RivalsAutoBan.IsSelected(selectionId, weaponName) then RivalsAutoBan.State.LastSubmitted = weaponName local ok, err = pcall(voteRemote.FireServer, voteRemote, weaponName) if not ok then RivalsAutoBan.State.LastSubmitted = nil return false, tostring(err) end return true end end return false, 'no_selected_option' end function RivalsAutoBan.BindDuel(duel) if not Connections then return end Connections:disconnect({ 'AutoBan_VoteOptionsType', 'AutoBan_VoteOptions', 'AutoBan_VoteBansRemaining', 'AutoBan_MaxWeaponBansPerTeam', 'AutoBan_MaxMapBansPerTeam', }) RivalsAutoBan.State.BoundDuel = duel RivalsAutoBan.State.LastVoteType = nil RivalsAutoBan.State.LastSubmitted = nil if not duel or type(duel.GetDataChangedSignal) ~= 'function' then return end for _, key in ipairs({ 'VoteOptionsType', 'VoteOptions', 'VoteBansRemaining', 'MaxWeaponBansPerTeam', 'MaxMapBansPerTeam', }) do local ok, signal = pcall(duel.GetDataChangedSignal, duel, key) if ok and signal and type(signal.Connect) == 'function' then Connections:register( 'AutoBan_' .. key, signal:Connect(GuardRivalsCallback('AutoBan_' .. key, RivalsAutoBan.TryVote)) ) end end RivalsAutoBan.TryVote() end function RivalsAutoBan.RegisterSignals() if not Connections then return end if not RivalsAutoBan.IsAnyEnabled() then RivalsAutoBan.Reset() return end local controller = RivalsAutoBan.ResolveController() if not controller then return end if controller.LocalPlayerJoinedDuel and type(controller.LocalPlayerJoinedDuel.Connect) == 'function' then Connections:register( 'AutoBan_JoinedDuel', controller.LocalPlayerJoinedDuel:Connect( GuardRivalsCallback( 'AutoBan_JoinedDuel', function(duel) RivalsAutoBan.BindDuel(duel or controller.CurrentObject) end ) ) ) end if controller.LocalPlayerLeftDuel and type(controller.LocalPlayerLeftDuel.Connect) == 'function' then Connections:register( 'AutoBan_LeftDuel', controller.LocalPlayerLeftDuel:Connect( GuardRivalsCallback('AutoBan_LeftDuel', function() RivalsAutoBan.BindDuel(nil) end) ) ) end RivalsAutoBan.BindDuel(controller.CurrentObject) end function RivalsAutoBan.Reset() RivalsAutoBan.State.BoundDuel = nil RivalsAutoBan.State.LastVoteType = nil RivalsAutoBan.State.LastSubmitted = nil if Connections then Connections:disconnect({ 'AutoBan_JoinedDuel', 'AutoBan_LeftDuel', 'AutoBan_VoteOptionsType', 'AutoBan_VoteOptions', 'AutoBan_VoteBansRemaining', 'AutoBan_MaxWeaponBansPerTeam', 'AutoBan_MaxMapBansPerTeam', }) end end RivalsRuntimeBridge.ResolveAutoBanWeaponValues = RivalsAutoBan.ResolveWeaponValues RivalsRuntimeBridge.ResolveAutoVoteMapValues = RivalsAutoBan.ResolveMapValues RivalsRuntimeBridge.TryAutoBanVote = RivalsAutoBan.TryVote RivalsRuntimeBridge.RegisterAutoBanSignals = RivalsAutoBan.RegisterSignals RivalsRuntimeBridge.ResetAutoBanState = RivalsAutoBan.Reset end do -- ====================================================================== -- Subspace tripmine automation -- ====================================================================== local RivalsTripmineAutomation = { Entries = setmetatable({}, { __mode = 'k' }), DestroyingConnections = setmetatable({}, { __mode = 'k' }), } function RivalsTripmineAutomation.IsEnabled() return Toggles.P8S8T1 and Toggles.P8S8T1.Value == true end function RivalsTripmineAutomation.Untrack(tripmine) local entry = RivalsTripmineAutomation.Entries[tripmine] if entry and entry.Hitbox then RivalsTripmineAutomation.Entries[entry.Hitbox] = nil end RivalsTripmineAutomation.Entries[tripmine] = nil local connection = RivalsTripmineAutomation.DestroyingConnections[tripmine] if connection then pcall(function() connection:Disconnect() end) end RivalsTripmineAutomation.DestroyingConnections[tripmine] = nil end function RivalsTripmineAutomation.Track(tripmine) if not tripmine or RivalsTripmineAutomation.Entries[tripmine] then return end task.spawn(GuardRivalsCallback('TripmineAutomation_Track', function() local hitbox = tripmine:WaitForChild('Hitbox', 3) if not hitbox or not hitbox:IsA('BasePart') or not tripmine.Parent then return end local entry = { Tripmine = tripmine, Hitbox = hitbox, EnvironmentId = tripmine:GetAttribute('EnvironmentID'), PlacedBy = tripmine:GetAttribute('PlacedByUserID'), TeamId = tripmine:GetAttribute('TeamID'), } RivalsTripmineAutomation.Entries[tripmine] = entry RivalsTripmineAutomation.Entries[hitbox] = entry RivalsTripmineAutomation.DestroyingConnections[tripmine] = hitbox.Destroying:Connect( GuardRivalsCallback( 'TripmineAutomation_Destroying', function() RivalsTripmineAutomation.Untrack(tripmine) end ) ) end)) end function RivalsTripmineAutomation.Bind() local collectionService = game:GetService('CollectionService') Connections:register( 'TripmineAutomation_Added', collectionService :GetInstanceAddedSignal('SubspaceTripmine') :Connect(GuardRivalsCallback('TripmineAutomation_Added', RivalsTripmineAutomation.Track)) ) Connections:register( 'TripmineAutomation_Removed', collectionService :GetInstanceRemovedSignal('SubspaceTripmine') :Connect(GuardRivalsCallback('TripmineAutomation_Removed', RivalsTripmineAutomation.Untrack)) ) for _, tripmine in ipairs(collectionService:GetTagged('SubspaceTripmine')) do RivalsTripmineAutomation.Track(tripmine) end end function RivalsTripmineAutomation.Update() if not RivalsTripmineAutomation.IsEnabled() then return end if not __ZkhHub_genv.ZkxHubCaps.gate('Subspace Tripmine Auto Trigger', 'firetouchinterest') then return end local activeChar = RivalsRuntimeBridge.AcquireActiveCharacter() local touchPart = activeChar and (activeChar:FindFirstChild('HitboxBody') or activeChar:FindFirstChild('HumanoidRootPart')) or nil if not touchPart or not touchPart:IsA('BasePart') then return end local fighter = ResolveLocalFighter() local localEnvironmentId = ReadFighterValue(fighter, 'EnvironmentID') or activeChar:GetAttribute('EnvironmentID') local localTeamId = ReadFighterValue(fighter, 'TeamID') or LP:GetAttribute('TeamID') for tripmine, entry in pairs(RivalsTripmineAutomation.Entries) do if tripmine == entry.Tripmine then local hitbox = entry.Hitbox if not hitbox or not hitbox.Parent or not tripmine.Parent then RivalsTripmineAutomation.Untrack(tripmine) else local friendly = entry.PlacedBy == LP.UserId if not friendly and localTeamId ~= nil and entry.TeamId ~= nil then friendly = localTeamId == entry.TeamId end local environmentMatches = localEnvironmentId == entry.EnvironmentId local teamTrigger = Toggles.P8S8T2 and Toggles.P8S8T2.Value == true local expectedTransparency = friendly and 0.5 or 1 if environmentMatches and (not friendly or teamTrigger) and hitbox.LocalTransparencyModifier == expectedTransparency then firetouchinterest(hitbox, touchPart, 0) firetouchinterest(hitbox, touchPart, 1) return end end end end end function RivalsTripmineAutomation.Reset() for tripmine, entry in pairs(RivalsTripmineAutomation.Entries) do if tripmine == entry.Tripmine then RivalsTripmineAutomation.Untrack(tripmine) end end if Connections then Connections:disconnect({ 'TripmineAutomation_Added', 'TripmineAutomation_Removed' }) end end RivalsRuntimeBridge.BindTripmineAutomation = RivalsTripmineAutomation.Bind RivalsRuntimeBridge.UpdateTripmineAutomation = RivalsTripmineAutomation.Update RivalsRuntimeBridge.ResetTripmineAutomation = RivalsTripmineAutomation.Reset end do local AUTO_QUEUE_FALLBACK_MODES = { '1v1', '2v2', '3v3', '4v4', '5v5', 'ranked_1v1', 'ranked_2v2', 'ranked_3v3', } local AUTO_QUEUE_FIRE_DEBOUNCE = 0.08 local AUTO_QUEUE_RETRY_DELAY = 0.5 -- ====================================================================== -- Auto queue -- ====================================================================== local RivalsAutoQueue = { State = { Believed = 'idle', LastFireAt = 0, LastModeFired = nil, Modes = nil, DisplayToKey = nil, RefreshSuppress = false, PlayAgainRegistrationQueued = false, MatchEndedConfirmed = false, RetryActive = false, RetryAttempts = 0, RetryToken = 0, SkipPressActive = false, SkipPressToken = 0, MatchmakingController = nil, ArcadeController = nil, ArcadeModes = nil, LastArcadeTeleportAt = 0, }, } local function CollectActiveQueueKeys() local seen = {} local order = {} local function add(key) if type(key) == 'string' and key ~= '' and not seen[key] then seen[key] = true table.insert(order, key) end end local CollectionService = game:GetService('CollectionService') local okPads, pads = pcall(function() return CollectionService:GetTagged('QueuePad') end) if okPads and type(pads) == 'table' then for _, pad in ipairs(pads) do local q = pad:GetAttribute('QueueName') add(q) end end local modules = ReplicatedStorage:FindFirstChild('Modules') local rotLib = modules and modules:FindFirstChild('RotatingQueueLibrary') if rotLib then local okReq, lib = pcall(require, rotLib) if okReq and type(lib) == 'table' and type(lib.GetCurrent) == 'function' then local okCur, current = pcall(function() return lib:GetCurrent() end) if okCur and type(current) == 'table' and type(current.QueueNames) == 'table' then for _, qn in ipairs(current.QueueNames) do add(qn) end end end end return order end local function BuildModeListFromKeys(keys, queueInfoTable) local pairs_list = {} for _, key in ipairs(keys) do local info = queueInfoTable and queueInfoTable[key] local display = nil if type(info) == 'table' and type(info.DisplayName) == 'string' and info.DisplayName ~= '' then display = info.DisplayName else display = key end table.insert(pairs_list, { display = display, key = key }) end local displays = {} local map = {} local seenDisplay = {} for _, entry in ipairs(pairs_list) do if not seenDisplay[entry.display] then seenDisplay[entry.display] = true table.insert(displays, entry.display) map[entry.display] = entry.key end end return displays, map end function RivalsAutoQueue.ResolveModeValues() if RivalsAutoQueue.State.Modes ~= nil then return RivalsAutoQueue.State.Modes end local modules = ReplicatedStorage:FindFirstChild('Modules') local duelLib = modules and modules:FindFirstChild('DuelLibrary') local queueInfoTable = nil local duelLibrary = nil if duelLib then local ok, lib = pcall(require, duelLib) if ok and type(lib) == 'table' then duelLibrary = lib if type(lib.MatchmakingQueues) == 'table' then queueInfoTable = lib.MatchmakingQueues end end end local serverOsTime = os.time() local serverOsTimeModule = modules and modules:FindFirstChild('ServerOsTime') if serverOsTimeModule then local ok, serverTimeLibrary = pcall(require, serverOsTimeModule) if ok and type(serverTimeLibrary) == 'table' and type(serverTimeLibrary.Get) == 'function' then local timeOk, currentServerTime = pcall(serverTimeLibrary.Get, serverTimeLibrary) if timeOk and type(currentServerTime) == 'number' then serverOsTime = currentServerTime end end end local keys = {} local seen = {} local function addKey(queueKey) if type(queueKey) == 'string' and queueKey ~= '' and not seen[queueKey] then seen[queueKey] = true keys[#keys + 1] = queueKey end end local queueOrder = duelLibrary and duelLibrary.MatchmakingQueueOrder or nil if type(queueOrder) == 'table' and type(queueInfoTable) == 'table' then for _, queueKey in ipairs(queueOrder) do local queueInfo = queueInfoTable[queueKey] local releaseTime = type(queueInfo) == 'table' and tonumber(queueInfo.TerminalsReleaseTime) or nil if type(queueInfo) == 'table' and queueInfo.CanBeStartedFromClient ~= false and (not releaseTime or releaseTime <= serverOsTime) then addKey(queueKey) end end end for _, queueKey in ipairs(CollectActiveQueueKeys()) do addKey(queueKey) end local arcadeModes = duelLibrary and duelLibrary.ArcadeModes or nil RivalsAutoQueue.State.ArcadeModes = type(arcadeModes) == 'table' and arcadeModes or {} local arcadeOrder = duelLibrary and duelLibrary.ArcadeModeOrder or nil if type(arcadeOrder) == 'table' then for _, arcadeModeKey in ipairs(arcadeOrder) do addKey(arcadeModeKey) end end local catalogInfoTable = {} for queueKey, queueInfo in pairs(queueInfoTable or {}) do catalogInfoTable[queueKey] = queueInfo end for arcadeModeKey, arcadeInfo in pairs(RivalsAutoQueue.State.ArcadeModes) do catalogInfoTable[arcadeModeKey] = arcadeInfo end if #keys > 0 then local displays, map = BuildModeListFromKeys(keys, catalogInfoTable) RivalsAutoQueue.State.Modes = displays RivalsAutoQueue.State.DisplayToKey = map return displays end local fallback = {} local fallbackMap = {} for _, key in ipairs(AUTO_QUEUE_FALLBACK_MODES) do table.insert(fallback, key) fallbackMap[key] = key end RivalsAutoQueue.State.Modes = fallback RivalsAutoQueue.State.DisplayToKey = fallbackMap return fallback end function RivalsAutoQueue.ResolveQueueKeyForDisplay(displayName) if type(displayName) ~= 'string' or displayName == '' then return nil end if RivalsAutoQueue.State.DisplayToKey == nil then RivalsAutoQueue.ResolveModeValues() end local map = RivalsAutoQueue.State.DisplayToKey if map and map[displayName] then return map[displayName] end return displayName end function RivalsAutoQueue.RefreshDropdownValues() RivalsAutoQueue.State.Modes = nil RivalsAutoQueue.State.DisplayToKey = nil local values = RivalsAutoQueue.ResolveModeValues() if Options and Options.P8S6D1 and type(Options.P8S6D1.SetValues) == 'function' then RivalsAutoQueue.State.RefreshSuppress = true pcall(function() Options.P8S6D1:SetValues(values) end) RivalsAutoQueue.State.RefreshSuppress = false end return values end local function ResolveQueueRemote(folderName, remoteName, remoteClass) local remotes = ReplicatedStorage:FindFirstChild('Remotes') local folder = remotes and remotes:FindFirstChild(folderName) local remote = folder and folder:FindFirstChild(remoteName) if remote and remote:IsA(remoteClass) then return remote end return nil end local function ReadAutoQueueDisplay() if Options and Options.P8S6D1 then local v = Options.P8S6D1.Value if type(v) == 'string' and v ~= '' then return v end end return nil end local function ReadAutoQueueToggle() if Toggles and Toggles.P8S6T1 then return Toggles.P8S6T1.Value == true end return false end function RivalsAutoQueue.ResolveMatchmakingController() if RivalsAutoQueue.State.MatchmakingController then return RivalsAutoQueue.State.MatchmakingController end if not IsReplicatedStateReady() then return nil end local playerScripts = LP:FindFirstChild('PlayerScripts') local controllers = playerScripts and playerScripts:FindFirstChild('Controllers') local controllerModule = controllers and controllers:FindFirstChild('MatchmakingController') if not controllerModule then return nil end local ok, controller = pcall(require, controllerModule) if not ok or type(controller) ~= 'table' then return nil end RivalsAutoQueue.State.MatchmakingController = controller return controller end function RivalsAutoQueue.ResolveArcadeMode(queueKey) if RivalsAutoQueue.State.ArcadeModes == nil then RivalsAutoQueue.ResolveModeValues() end local arcadeModes = RivalsAutoQueue.State.ArcadeModes return type(arcadeModes) == 'table' and arcadeModes[queueKey] or nil end function RivalsAutoQueue.ResolveArcadeController() if RivalsAutoQueue.State.ArcadeController then return RivalsAutoQueue.State.ArcadeController end if not IsReplicatedStateReady() then return nil end local playerScripts = LP:FindFirstChild('PlayerScripts') local controllers = playerScripts and playerScripts:FindFirstChild('Controllers') local controllerModule = controllers and controllers:FindFirstChild('ArcadeController') if not controllerModule then return nil end local ok, controller = pcall(require, controllerModule) if not ok or type(controller) ~= 'table' then return nil end RivalsAutoQueue.State.ArcadeController = controller return controller end function RivalsAutoQueue.TeleportToArcade(queueKey, arcadeMode) local placeId = type(arcadeMode) == 'table' and tonumber(arcadeMode.PlaceID) or nil if placeId and game.PlaceId == placeId then return true end local now = tick() if now - RivalsAutoQueue.State.LastArcadeTeleportAt < 5 then return false, 'arcade_debounced' end RivalsAutoQueue.State.LastArcadeTeleportAt = now local result = nil local controller = RivalsAutoQueue.ResolveArcadeController() if controller and type(controller.ToArcadeServer) == 'function' then local ok ok, result = pcall(controller.ToArcadeServer, controller, queueKey) if ok and not (type(result) == 'string' and result ~= '') then RivalsAutoQueue.State.Believed = 'matched' RivalsAutoQueue.State.LastModeFired = queueKey return true end else local remote = ResolveQueueRemote('Arcade', 'TeleportToArcadeServer', 'RemoteFunction') if remote then local ok ok, result = pcall(remote.InvokeServer, remote, queueKey) if ok and not (type(result) == 'string' and result ~= '') then RivalsAutoQueue.State.Believed = 'matched' RivalsAutoQueue.State.LastModeFired = queueKey return true end end end RivalsAutoQueue.State.LastArcadeTeleportAt = 0 return false, type(result) == 'string' and result or 'arcade_teleport_failed' end function RivalsAutoQueue.CanFireQueue() local status = workspace:GetAttribute('MatchmadeStatus') if status ~= 'Started' then return true end local gameOver = workspace:GetAttribute('MatchmadeGameOver') if gameOver == true then return true end if RivalsAutoQueue.State.MatchEndedConfirmed == true then return true end return false end function RivalsAutoQueue.IsInLobby() return RivalsAutoQueue.CanFireQueue() end function RivalsAutoQueue.StopMatchEndRetry() RivalsAutoQueue.State.RetryActive = false RivalsAutoQueue.State.RetryAttempts = 0 RivalsAutoQueue.State.RetryToken = (RivalsAutoQueue.State.RetryToken or 0) + 1 end function RivalsAutoQueue.ScheduleMatchEndRetry() if not ReadAutoQueueToggle() then return end if not RivalsAutoQueue.CanFireQueue() then return end local believed = RivalsAutoQueue.State.Believed if believed == 'queued' or believed == 'matched' then return end if RivalsAutoQueue.State.RetryAttempts >= 150 then RivalsAutoQueue.StopMatchEndRetry() return end RivalsAutoQueue.State.RetryActive = true RivalsAutoQueue.State.RetryToken = (RivalsAutoQueue.State.RetryToken or 0) + 1 local token = RivalsAutoQueue.State.RetryToken task.delay(0.1, function() if token ~= RivalsAutoQueue.State.RetryToken then return end if not RivalsAutoQueue.State.RetryActive then return end if not ReadAutoQueueToggle() then RivalsAutoQueue.StopMatchEndRetry() return end if not RivalsAutoQueue.CanFireQueue() then RivalsAutoQueue.StopMatchEndRetry() return end local b = RivalsAutoQueue.State.Believed if b == 'queued' or b == 'matched' then RivalsAutoQueue.StopMatchEndRetry() return end RivalsAutoQueue.State.RetryAttempts = RivalsAutoQueue.State.RetryAttempts + 1 RivalsAutoQueue.FireJoin() end) end function RivalsAutoQueue.ResolveSkipButton() local duelInterfaces = RivalsAutoQueue.ResolveDuelInterfacesFrame() if not duelInterfaces then return nil end for _, descendant in ipairs(duelInterfaces:GetDescendants()) do if descendant:IsA('GuiButton') then local title = descendant:FindFirstChild('Title') if title and title:IsA('TextLabel') and title.Text == 'Skip' then return descendant end if descendant:IsA('TextButton') and descendant.Text == 'Skip' then return descendant end end end return nil end function RivalsAutoQueue.PressSkip() local button = RivalsAutoQueue.ResolveSkipButton() if not button then return false, 'no_skip_button' end if not RivalsAutoQueue.IsGuiObjectVisibleInTree(button) then return false, 'skip_not_visible' end local pos = button.AbsolutePosition local size = button.AbsoluteSize if size.X <= 0 or size.Y <= 0 then return false, 'skip_zero_size' end if AimbotInputState.SyntheticInputBlocked() then return false, 'ui_open' end local cx = pos.X + size.X * 0.5 local cy = pos.Y + game:GetService('GuiService'):GetGuiInset().Y + size.Y * 0.5 local ok = pcall(function() local vim = Instance.new('VirtualInputManager') vim:SendMouseButtonEvent(cx, cy, 0, true, button, 1) vim:SendMouseButtonEvent(cx, cy, 0, false, button, 1) end) if not ok then return false, 'vim_failed' end return true, 'vim' end function RivalsAutoQueue.StopSkipPress() RivalsAutoQueue.State.SkipPressActive = false RivalsAutoQueue.State.SkipPressToken = (RivalsAutoQueue.State.SkipPressToken or 0) + 1 end function RivalsAutoQueue.PressSkipWithRetry() RivalsAutoQueue.StopSkipPress() RivalsAutoQueue.State.SkipPressActive = true RivalsAutoQueue.State.SkipPressToken = (RivalsAutoQueue.State.SkipPressToken or 0) + 1 local token = RivalsAutoQueue.State.SkipPressToken local startedAt = tick() local function attempt() if token ~= RivalsAutoQueue.State.SkipPressToken then return end if not RivalsAutoQueue.State.SkipPressActive then return end local ok = RivalsAutoQueue.PressSkip() if ok then RivalsAutoQueue.State.SkipPressActive = false return end if tick() - startedAt >= 30.0 then RivalsAutoQueue.State.SkipPressActive = false return end task.delay(0.25, attempt) end attempt() end function RivalsAutoQueue.FireJoin() if not RivalsAutoQueue.IsInLobby() then return false, 'not_in_lobby' end if not IsReplicatedStateReady() then return false, 'replicated_state_not_ready' end local believed = RivalsAutoQueue.State.Believed if believed == 'queued' or believed == 'matched' then return false, 'already_' .. believed end local display = ReadAutoQueueDisplay() if display == nil then return false, 'no_mode_selected' end local queueKey = RivalsAutoQueue.ResolveQueueKeyForDisplay(display) if type(queueKey) ~= 'string' or queueKey == '' then return false, 'no_key_for_display' end local now = tick() if now - RivalsAutoQueue.State.LastFireAt < AUTO_QUEUE_FIRE_DEBOUNCE then return false, 'debounced' end RivalsAutoQueue.State.LastFireAt = now local arcadeMode = RivalsAutoQueue.ResolveArcadeMode(queueKey) if arcadeMode then return RivalsAutoQueue.TeleportToArcade(queueKey, arcadeMode) end local controller = RivalsAutoQueue.ResolveMatchmakingController() if controller and type(controller.QueueInto) == 'function' then local ok, result = pcall(function() return controller:QueueInto(queueKey) end) if ok then if type(result) == 'string' and #result > 0 then RivalsAutoQueue.State.Believed = 'idle' RivalsAutoQueue.ScheduleMatchEndRetry() return false, result end RivalsAutoQueue.State.Believed = 'queued' RivalsAutoQueue.State.LastModeFired = queueKey RivalsAutoQueue.StopMatchEndRetry() return true end elseif not controller then local primary = ResolveQueueRemote('Matchmaking', 'JoinQueue', 'RemoteFunction') if primary then local ok, result = pcall(function() return safe_invoke_server(primary, queueKey) end) if ok and not (type(result) == 'string' and #result > 0) then RivalsAutoQueue.State.Believed = 'queued' RivalsAutoQueue.State.LastModeFired = queueKey RivalsAutoQueue.StopMatchEndRetry() return true end end end return false, 'no_remote' end function RivalsAutoQueue.FireLeave() RivalsAutoQueue.StopMatchEndRetry() RivalsAutoQueue.StopSkipPress() if RivalsAutoQueue.State.Believed == 'idle' then return true end if not IsReplicatedStateReady() then RivalsAutoQueue.State.Believed = 'idle' return true end local controller = RivalsAutoQueue.ResolveMatchmakingController() if controller and type(controller.TryLeaveQueue) == 'function' then local ok = pcall(function() controller:TryLeaveQueue() end) if ok then RivalsAutoQueue.State.Believed = 'idle' return true end elseif not controller then local primary = ResolveQueueRemote('Matchmaking', 'TryLeaveQueue', 'RemoteEvent') or ResolveQueueRemote('Matchmaking', 'LeaveQueue', 'RemoteEvent') if primary then local ok = pcall(function() safe_fire_server(primary) end) if ok then RivalsAutoQueue.State.Believed = 'idle' return true end end end RivalsAutoQueue.State.Believed = 'idle' return false end function RivalsAutoQueue.OnToggled(value) if value then RivalsAutoQueue.FireJoin() else RivalsAutoQueue.FireLeave() end end function RivalsAutoQueue.OnModeChanged() if RivalsAutoQueue.State.RefreshSuppress then return end if not ReadAutoQueueToggle() then return end if RivalsAutoQueue.State.Believed == 'queued' then RivalsAutoQueue.FireLeave() task.delay(0.15, function() if ReadAutoQueueToggle() then RivalsAutoQueue.FireJoin() end end) return end RivalsAutoQueue.FireJoin() end function RivalsAutoQueue.HandleStatusUpdate(...) local args = { ... } local statusValue = args[1] if type(statusValue) == 'table' then statusValue = statusValue.Status or statusValue.State or statusValue[1] end if type(statusValue) == 'string' then local lowered = statusValue:lower() if lowered == 'queued' or lowered == 'searching' then RivalsAutoQueue.State.Believed = 'queued' RivalsAutoQueue.StopMatchEndRetry() return end if lowered == 'matched' or lowered == 'matchfound' or lowered == 'ready' then RivalsAutoQueue.State.Believed = 'matched' RivalsAutoQueue.StopMatchEndRetry() return end if lowered == 'cancelled' or lowered == 'canceled' or lowered == 'expired' or lowered == 'failed' or lowered == 'timeout' or lowered == 'idle' then RivalsAutoQueue.State.Believed = 'idle' if ReadAutoQueueToggle() then task.delay(AUTO_QUEUE_RETRY_DELAY, function() if ReadAutoQueueToggle() and RivalsAutoQueue.IsInLobby() and RivalsAutoQueue.State.Believed == 'idle' then RivalsAutoQueue.FireJoin() end end) end end end end function RivalsAutoQueue.OnLobbyReturn() if not ReadAutoQueueToggle() then return end if not RivalsAutoQueue.CanFireQueue() then return end local now = tick() if now - RivalsAutoQueue.State.LastFireAt < AUTO_QUEUE_FIRE_DEBOUNCE then return end RivalsAutoQueue.State.Believed = 'idle' RivalsAutoQueue.FireJoin() end function RivalsAutoQueue.OnMatchmadeGameOverChanged() if not ReadAutoQueueToggle() then return end local gameOver = workspace:GetAttribute('MatchmadeGameOver') if gameOver ~= true then return end local now = tick() if now - RivalsAutoQueue.State.LastFireAt < AUTO_QUEUE_FIRE_DEBOUNCE then return end RivalsAutoQueue.State.Believed = 'idle' RivalsAutoQueue.FireJoin() end function RivalsAutoQueue.OnIsInDuelChanged(newValue, previousValue) local isNowInDuel = newValue == true local wasInDuel = previousValue == true if isNowInDuel and not wasInDuel then RivalsAutoQueue.State.MatchEndedConfirmed = false RivalsAutoQueue.StopMatchEndRetry() RivalsAutoQueue.StopSkipPress() return end if not (wasInDuel and not isNowInDuel) then return end RivalsAutoQueue.State.MatchEndedConfirmed = true if not ReadAutoQueueToggle() then return end RivalsAutoQueue.PressSkipWithRetry() local now = tick() if now - RivalsAutoQueue.State.LastFireAt < AUTO_QUEUE_FIRE_DEBOUNCE then return end RivalsAutoQueue.State.Believed = 'idle' RivalsAutoQueue.FireJoin() end function RivalsAutoQueue.ResolveFinalResultsButtonsContainer() local playerGui = LP and LP:FindFirstChildOfClass('PlayerGui') local mainGui = playerGui and playerGui:FindFirstChild('MainGui') local mainFrame = mainGui and mainGui:FindFirstChild('MainFrame') local duelInterfaces = mainFrame and mainFrame:FindFirstChild('DuelInterfaces') local duelInterface = duelInterfaces and duelInterfaces:FindFirstChild('DuelInterface') local finalResults = duelInterface and duelInterface:FindFirstChild('FinalResults') local buttons = finalResults and finalResults:FindFirstChild('Buttons') local bottomRight = buttons and buttons:FindFirstChild('BottomRight') return bottomRight and bottomRight:FindFirstChild('Container') or nil end function RivalsAutoQueue.ResolveDuelInterfacesFrame() local playerGui = LP and LP:FindFirstChildOfClass('PlayerGui') local mainGui = playerGui and playerGui:FindFirstChild('MainGui') local mainFrame = mainGui and mainGui:FindFirstChild('MainFrame') return mainFrame and mainFrame:FindFirstChild('DuelInterfaces') or nil end function RivalsAutoQueue.ResolvePlayAgainButton() local container = RivalsAutoQueue.ResolveFinalResultsButtonsContainer() local buttonFrame = container and container:FindFirstChild('PlayAgain') local button = buttonFrame and buttonFrame:FindFirstChild('Button') if button and button:IsA('GuiButton') then local title = button:FindFirstChild('Title') if title and title:IsA('TextLabel') and title.Text == 'Play Again' then return button end end return nil end function RivalsAutoQueue.IsGuiObjectVisibleInTree(guiObject) if not guiObject or not guiObject:IsA('GuiObject') then return false end local playerGui = LP and LP:FindFirstChildOfClass('PlayerGui') local current = guiObject while current and current ~= playerGui do if current:IsA('GuiObject') and current.Visible ~= true then return false end current = current.Parent end return current == playerGui end function RivalsAutoQueue.OnPlayAgainVisible(button) if not ReadAutoQueueToggle() then return end if not RivalsAutoQueue.IsGuiObjectVisibleInTree(button) then return end RivalsAutoQueue.State.Believed = 'idle' RivalsAutoQueue.FireJoin() end function RivalsAutoQueue.QueuePlayAgainCheck(button) task.defer(function() RivalsAutoQueue.OnPlayAgainVisible(button) end) end function RivalsAutoQueue.RegisterPlayAgainButton() local button = RivalsAutoQueue.ResolvePlayAgainButton() if not Connections or not button or not button:IsA('GuiButton') then return end Connections:register( 'AutoQueue_PlayAgain_Visible', button:GetPropertyChangedSignal('Visible'):Connect(GuardRivalsCallback('AutoQueue_PlayAgain_Visible', function() if button.Visible == true then RivalsAutoQueue.QueuePlayAgainCheck(button) end end)) ) local title = button:FindFirstChild('Title') if title and title:IsA('TextLabel') then Connections:register( 'AutoQueue_PlayAgain_Title', title:GetPropertyChangedSignal('Text'):Connect( GuardRivalsCallback( 'AutoQueue_PlayAgain_Title', function() RivalsAutoQueue.QueuePlayAgainCheck(button) end ) ) ) end local playerGui = LP and LP:FindFirstChildOfClass('PlayerGui') local ancestor = button.Parent local ancestorIndex = 0 while ancestor and ancestor ~= playerGui do if ancestor:IsA('GuiObject') then ancestorIndex = ancestorIndex + 1 local connectionName = 'AutoQueue_PlayAgain_AncestorVisible_' .. tostring(ancestorIndex) local watchedAncestor = ancestor Connections:register( connectionName, watchedAncestor :GetPropertyChangedSignal('Visible') :Connect(GuardRivalsCallback(connectionName, function() if watchedAncestor.Visible == true then RivalsAutoQueue.QueuePlayAgainCheck(button) end end)) ) end ancestor = ancestor.Parent end if RivalsAutoQueue.IsGuiObjectVisibleInTree(button) then RivalsAutoQueue.QueuePlayAgainCheck(button) end end function RivalsAutoQueue.QueuePlayAgainRegistration() if RivalsAutoQueue.State.PlayAgainRegistrationQueued then return end RivalsAutoQueue.State.PlayAgainRegistrationQueued = true task.defer(function() RivalsAutoQueue.State.PlayAgainRegistrationQueued = false RivalsAutoQueue.RegisterPlayAgainButton() RivalsAutoQueue.RegisterSkipButton() end) end function RivalsAutoQueue.RegisterPlayAgainChildWatcher(parent, childName, connectionName) if not Connections or not parent then if Connections then Connections:disconnect({ connectionName }) end return nil end Connections:register( connectionName, parent.ChildAdded:Connect(GuardRivalsCallback(connectionName, function(child) if child.Name == childName then RivalsAutoQueue.QueuePlayAgainRegistration() end end)) ) return parent:FindFirstChild(childName) end function RivalsAutoQueue.OnSkipVisible(button) if not ReadAutoQueueToggle() then return end if not RivalsAutoQueue.IsGuiObjectVisibleInTree(button) then return end RivalsAutoQueue.PressSkip() end function RivalsAutoQueue.QueueSkipPress(button) task.defer(function() RivalsAutoQueue.OnSkipVisible(button) end) end function RivalsAutoQueue.RegisterSkipButton() local button = RivalsAutoQueue.ResolveSkipButton() if not Connections or not button or not button:IsA('GuiButton') then return end Connections:register( 'AutoQueue_Skip_Visible', button:GetPropertyChangedSignal('Visible'):Connect(GuardRivalsCallback('AutoQueue_Skip_Visible', function() if button.Visible == true then RivalsAutoQueue.QueueSkipPress(button) end end)) ) local title = button:FindFirstChild('Title') if title and title:IsA('TextLabel') then Connections:register( 'AutoQueue_Skip_Title', title :GetPropertyChangedSignal('Text') :Connect( GuardRivalsCallback('AutoQueue_Skip_Title', function() RivalsAutoQueue.QueueSkipPress(button) end) ) ) end local playerGui = LP and LP:FindFirstChildOfClass('PlayerGui') local ancestor = button.Parent local ancestorIndex = 0 while ancestor and ancestor ~= playerGui do if ancestor:IsA('GuiObject') then ancestorIndex = ancestorIndex + 1 local connectionName = 'AutoQueue_Skip_AncestorVisible_' .. tostring(ancestorIndex) local watchedAncestor = ancestor Connections:register( connectionName, watchedAncestor :GetPropertyChangedSignal('Visible') :Connect(GuardRivalsCallback(connectionName, function() if watchedAncestor.Visible == true then RivalsAutoQueue.QueueSkipPress(button) end end)) ) end ancestor = ancestor.Parent end if RivalsAutoQueue.IsGuiObjectVisibleInTree(button) then RivalsAutoQueue.QueueSkipPress(button) end end function RivalsAutoQueue.RegisterPlayAgainSignals() if not Connections then return end RivalsAutoQueue.RegisterPlayAgainButton() RivalsAutoQueue.RegisterSkipButton() local duelInterfaces = RivalsAutoQueue.ResolveDuelInterfacesFrame() if not duelInterfaces then Connections:disconnect({ 'AutoQueue_DuelInterfaceAdded', 'AutoQueue_FinalResultsFrameAdded', 'AutoQueue_FinalResultsButtonsAdded', 'AutoQueue_FinalResultsBottomRightAdded', 'AutoQueue_FinalResultsContainerAdded', }) return end local duelInterface = RivalsAutoQueue.RegisterPlayAgainChildWatcher(duelInterfaces, 'DuelInterface', 'AutoQueue_DuelInterfaceAdded') local finalResults = RivalsAutoQueue.RegisterPlayAgainChildWatcher(duelInterface, 'FinalResults', 'AutoQueue_FinalResultsFrameAdded') local buttons = RivalsAutoQueue.RegisterPlayAgainChildWatcher(finalResults, 'Buttons', 'AutoQueue_FinalResultsButtonsAdded') local bottomRight = RivalsAutoQueue.RegisterPlayAgainChildWatcher(buttons, 'BottomRight', 'AutoQueue_FinalResultsBottomRightAdded') RivalsAutoQueue.RegisterPlayAgainChildWatcher(bottomRight, 'Container', 'AutoQueue_FinalResultsContainerAdded') end RivalsRuntimeBridge.ResolveAutoQueueModeValues = RivalsAutoQueue.ResolveModeValues RivalsRuntimeBridge.RefreshAutoQueueDropdownValues = RivalsAutoQueue.RefreshDropdownValues RivalsRuntimeBridge.OnAutoQueueToggled = RivalsAutoQueue.OnToggled RivalsRuntimeBridge.OnAutoQueueModeChanged = RivalsAutoQueue.OnModeChanged RivalsRuntimeBridge.HandleAutoQueueStatusUpdate = RivalsAutoQueue.HandleStatusUpdate RivalsRuntimeBridge.OnAutoQueueLobbyReturn = RivalsAutoQueue.OnLobbyReturn RivalsRuntimeBridge.OnAutoQueueMatchmadeGameOverChanged = RivalsAutoQueue.OnMatchmadeGameOverChanged FighterDataCache.OnLocalDuelChanged = RivalsAutoQueue.OnIsInDuelChanged RivalsRuntimeBridge.OnAutoQueueIsInDuelChanged = RivalsAutoQueue.OnIsInDuelChanged RivalsRuntimeBridge.FireAutoQueueJoin = RivalsAutoQueue.FireJoin RivalsRuntimeBridge.IsAutoQueueLobby = RivalsAutoQueue.IsInLobby RivalsRuntimeBridge.CanAutoQueueFire = RivalsAutoQueue.CanFireQueue RivalsRuntimeBridge.GetAutoQueueBelief = function() return RivalsAutoQueue.State.Believed end RivalsRuntimeBridge.RegisterAutoQueuePlayAgainSignals = RivalsAutoQueue.RegisterPlayAgainSignals end do local DEFAULT_RIVALS_COSMETIC_VALUE = 'Default' local RIVALS_COSMETIC_UNSELECTED = 'Unselected' local RIVALS_COSMETIC_NONE = 'None' local RIVALS_COSMETIC_RANDOM = 'Random' local RIVALS_COSMETIC_ALL_WEAPONS = 'All' local RIVALS_GET_WEAPON_DATA_SENTINEL = 'GetWeaponData\0ohaio' local RIVALS_COSMETIC_SLOT_FRAME_SENTINEL = 'Frame\0rahh' local RIVALS_RANK_CHARM_OFF = 'Off' local RIVALS_RANK_CHARM_STATE_OPTION_ID = 'P5RANK_STATE' local RIVALS_FAVORITES_STATE_OPTION_ID = 'P5FAVORITES_STATE' -- ====================================================================== -- Auto weapon menu + manual loadout in ranked -- ====================================================================== -- Auto Loadout picks your weapons the moment the weapon page opens. This module (1) opens that page for you by pressing the -- key you use for it, when a round starts or you switch loadout (Trello #27), and (2) leaves the choice to you in ranked -- matches when Manual Loadout is on (Trello #31). Ranked is read from the duel object the way Auto Ban does: a ranked -- queue name, or weapon / map bans per team. do local UserInputService = game:GetService('UserInputService') local VirtualInputManager = game:GetService('VirtualInputManager') local Menu = { LastOpenAt = 0, Pending = 0, Opened = 0 } RivalsRuntimeBridge.AutoWeaponMenu = Menu local QUEUE_KEYS = { 'QueueName', 'Queue', 'QueueType', 'DuelLogic' } local BAN_KEYS = { 'MaxWeaponBansPerTeam', 'MaxMapBansPerTeam' } local LOADOUT_OPTIONS = { 'P8S5D1', 'P8S5D2', 'P8S5D3', 'P8S5D4' } local function toggleOn(id) return Toggles ~= nil and Toggles[id] ~= nil and Toggles[id].Value == true end local function optionValue(id, fallback) local option = Options and Options[id] if option == nil or option.Value == nil then return fallback end return option.Value end local function read(object, key) if not object then return nil end if type(object.Get) == 'function' then local ok, value = pcall(object.Get, object, key) if ok then return value end end return object[key] end local function resolveDuel() if not IsReplicatedStateReady() then return nil end local playerScripts = LP:FindFirstChild('PlayerScripts') local controllers = playerScripts and playerScripts:FindFirstChild('Controllers') local controllerModule = controllers and controllers:FindFirstChild('DuelController') if not controllerModule then return nil end local ok, controller = pcall(require, controllerModule) if not ok or type(controller) ~= 'table' then return nil end return controller.CurrentObject end -- 'range' = the practice zone (shooting range), 'duel' = in a match, round intermission included, 'lobby' = everywhere else. -- The weapon menu is only worth opening in a duel: the range has its own pick page and the lobby has nothing to pick. function Menu.Zone() local state = FighterDataCache and FighterDataCache.LocalDuel if state and state.Seeded then if state.IsInShootingRange then return 'range' end if state.IsInDuel then return 'duel' end else local fighter = ResolveLocalFighter() if fighter and read(fighter, 'IsInShootingRange') == true then return 'range' end if fighter and read(fighter, 'IsInDuel') == true then return 'duel' end end -- between rounds the local fighter can be gone while the duel object is still there if resolveDuel() ~= nil then return 'duel' end return 'lobby' end function Menu.IsRankedMatch(duel) duel = duel or resolveDuel() if not duel then return false end for _, key in ipairs(QUEUE_KEYS) do local value = read(duel, key) if type(value) == 'string' and value:lower():find('ranked', 1, true) then return true end end for _, key in ipairs(BAN_KEYS) do if (tonumber(read(duel, key)) or 0) > 0 then return true end end return false end -- weapons the ranked ban vote has taken out of the match (empty once the vote is over or outside ranked) function Menu.BannedWeapons(duel) local banned = {} duel = duel or resolveDuel() if not duel or read(duel, 'VoteOptionsType') ~= 'Weapons' then return banned end local options = read(duel, 'VoteOptions') if type(options) == 'table' then for _, option in pairs(options) do local name = read(option, 'Name') if type(name) == 'string' and read(option, 'IsBanned') ~= nil then banned[name] = true end end end return banned end function Menu.SelectedLoadoutBanned(duel) local banned = Menu.BannedWeapons(duel) for _, optionId in ipairs(LOADOUT_OPTIONS) do local value = optionValue(optionId, nil) if type(value) == 'string' and banned[value] then return true, value end end return false, nil end -- Auto Loadout asks this before it picks anything function Menu.IsManualLoadoutActive() return toggleOn('ZMLT1') and Menu.IsRankedMatch() end RivalsRuntimeBridge.IsManualLoadoutActive = Menu.IsManualLoadoutActive local function resolveKeyCode() local name = optionValue('ZAWK1', 'M') if type(name) ~= 'string' or name == '' or name == 'Unknown' then return nil end local ok, keyCode = pcall(function() return Enum.KeyCode[name] end) return ok and keyCode or nil end local function weaponPageOpen() local playerScripts = LP:FindFirstChild('PlayerScripts') local modules = playerScripts and playerScripts:FindFirstChild('Modules') local pages = modules and modules:FindFirstChild('Pages') local pageModule = pages and pages:FindFirstChild('PickWeapons') if not pageModule then return false end local ok, page = pcall(require, pageModule) return ok and type(page) == 'table' and rawget(page, '_is_open') == true end -- true when the key was pressed function Menu.Open() if not toggleOn('ZAWT1') then return false end local now = os.clock() if now - Menu.LastOpenAt < 2 then return false end if UserInputService:GetFocusedTextBox() then return false end if Menu.IsManualLoadoutActive() then return false end if Menu.Zone() ~= 'duel' then return false end local duel = resolveDuel() if Menu.SelectedLoadoutBanned(duel) then return false end if weaponPageOpen() then return false end local keyCode = resolveKeyCode() if not keyCode then return false end Menu.LastOpenAt = now Menu.Opened = Menu.Opened + 1 task.spawn(function() VirtualInputManager:SendKeyEvent(true, keyCode, false, game) task.wait(0.06) VirtualInputManager:SendKeyEvent(false, keyCode, false, game) end) return true end -- the newest trigger wins, so cycling loadouts quickly presses the key once, after the last switch local function schedule() Menu.Pending = Menu.Pending + 1 local ticket = Menu.Pending task.delay(math.clamp(tonumber(optionValue('ZAWS1', 0.6)) or 0.6, 0, 5), function() if ticket == Menu.Pending then Menu.Open() end end) end function Menu.OnRoundStart() if toggleOn('ZAWT1') and toggleOn('ZAWT3') then schedule() end end -- everything that makes a loadout 'this one': the loadout number and the four picked weapons local function loadoutSignature() local parts = { tostring(optionValue('P8S5D8', '')) } for _, optionId in ipairs(LOADOUT_OPTIONS) do parts[#parts + 1] = tostring(optionValue(optionId, '')) end return table.concat(parts, '|') end function Menu.OnLoadoutSwitched() Menu.Signature = loadoutSignature() if not (toggleOn('ZAWT1') and toggleOn('ZAWT2')) then return end -- only inside a match (round intermission counts, the fighter does not exist then): there is nothing to pick in the lobby or the range if Menu.Zone() ~= 'duel' then return end schedule() end -- The switch handler only hears the loadout dropdown. This watches the whole signature, so a weapon change, a per-map profile or a switch -- made while the fighter is gone (round intermission) still opens the menu. Outside a duel it only keeps the signature up to date. function Menu.Watch() local signature = loadoutSignature() if Menu.Signature == nil then Menu.Signature = signature return end if signature == Menu.Signature then return end if RivalsRuntimeBridge.IsConfigLoading and RivalsRuntimeBridge.IsConfigLoading() then Menu.Signature = signature return end Menu.OnLoadoutSwitched() end function Menu.Start() if Menu.Watching then return end Menu.Watching = true task.spawn(function() while Menu.Watching and not Library.Unloaded do task.wait(0.25) if toggleOn('ZAWT1') then pcall(Menu.Watch) else Menu.Signature = nil end end end) end function Menu.Stop() Menu.Watching = false end -- menu: added to the Auto Loadout groupbox of the Automation tab function Menu.BuildMenu(group) group:AddToggle('ZMLT1', { Text = 'Manual Loadout (Ranked)', Default = false, Tooltip = 'In ranked matches Auto Loadout leaves your weapons alone so you pick them yourself.', }) group:AddToggle('ZAWT1', { Text = 'Auto Open Weapon Menu', Default = false, Tooltip = 'Presses your weapon menu key when a round starts or you switch loadout (also during the round intermission), so Auto Loadout can apply it. Never in the practice zone or the lobby. Skipped in ranked when the selected loadout has a banned weapon.', }) local box = group:AddDependencyBox() box:AddLabel('Weapon Menu Key'):AddKeyPicker('ZAWK1', { Default = 'M', Mode = 'Press', Text = 'Weapon Menu Key', NoUI = true, Tooltip = 'The key you use in game to open the weapon menu. This is the key that gets pressed.', }) box:AddToggle('ZAWT3', { Text = 'On Round Start', Default = true }) box:AddToggle('ZAWT2', { Text = 'On Loadout Switch', Default = true }) box:AddSlider('ZAWS1', { Text = 'Delay', Default = 0.6, Min = 0, Max = 3, Rounding = 1, Suffix = ' s', Compact = true, Tooltip = 'Wait before pressing the key (a new switch restarts the wait).', }) box:SetupDependencies({ { Toggles.ZAWT1, true } }) Menu.Start() end if type(getgenv) == 'function' then local probe = getgenv().__ZkxHubTest if type(probe) == 'table' then probe.AutoWeaponMenu = Menu end end end -- ====================================================================== -- Cosmetics changer (Unlock All) -- ====================================================================== local RivalsCosmetics = { Catalog = {}, SkinOptionIdsByWeapon = {}, WrapOptionIdsByWeapon = {}, CharmOptionIdsByWeapon = {}, FinisherOptionIdsByWeapon = {}, WrapInvertedOptionIdsByWeapon = {}, EditorOptionIds = { Weapon = 'P5EDITOR_WEAPON', Skin = 'P5EDITOR_SKIN', Wrap = 'P5EDITOR_WRAP', WrapInverted = 'P5EDITOR_WRAPINV', Charm = 'P5EDITOR_CHARM', Finisher = 'P5EDITOR_FINISHER', RankSeason = 'P5RANK_SEASON', RankName = 'P5RANK_RANK', RankLeaderboard = 'P5RANK_LEADERBOARD', }, FavoriteKinds = { 'Skin', 'Wrap', 'Charm', 'Finisher' }, CosmeticPresetPath = 'ZkxHub/CosmPre.json', } local RivalsEmotes = { Catalog = {}, } function RivalsCosmetics.IsCosmeticValueSelected(value) return type(value) == 'string' and value ~= '' and value ~= DEFAULT_RIVALS_COSMETIC_VALUE and value ~= RIVALS_COSMETIC_UNSELECTED and value ~= RIVALS_COSMETIC_NONE and value ~= RIVALS_COSMETIC_RANDOM end function RivalsCosmetics.ResolveSentinelValues() return { RIVALS_COSMETIC_UNSELECTED, RIVALS_COSMETIC_NONE, RIVALS_COSMETIC_RANDOM } end function RivalsCosmetics.NormalizeSelectionValue(value) if value == DEFAULT_RIVALS_COSMETIC_VALUE then return RIVALS_COSMETIC_NONE end return value end function RivalsCosmetics.IsCosmeticOptionId(optionId) if type(optionId) ~= 'string' then return false end return optionId:sub(1, 7) == 'P5SKIN_' or optionId:sub(1, 7) == 'P5WRAP_' or optionId:sub(1, 8) == 'P5CHARM_' or optionId:sub(1, 11) == 'P5FINISHER_' or optionId:sub(1, 10) == 'P5WRAPINV_' or optionId == RIVALS_RANK_CHARM_STATE_OPTION_ID or optionId == RIVALS_FAVORITES_STATE_OPTION_ID or RivalsCosmetics.IsEditorCosmeticOptionId(optionId) end function RivalsCosmetics.IsEditorCosmeticOptionId(optionId) for _, editorOptionId in pairs(RivalsCosmetics.EditorOptionIds) do if optionId == editorOptionId then return true end end return false end function RivalsCosmetics.ResolveEditorOptionId(kind) return RivalsCosmetics.EditorOptionIds[kind] end function RivalsCosmetics.IsPlaceholderCatalogName(name) return type(name) == 'string' and (name == 'MISSING_WEAPON' or name:match('^MISSING_') ~= nil) end function RivalsCosmetics.IsEnabled() return Toggles.P5S1T2 and Toggles.P5S1T2.Value == true end function RivalsEmotes.IsEnabled() return RivalsCosmetics.IsEnabled() end function RivalsCosmetics.CloneConfigObject(object) if type(object) ~= 'table' then return nil end local cloned = {} for key, value in pairs(object) do cloned[key] = value end return cloned end function RivalsCosmetics.BuildCosmeticData(data, name, cosmeticType) if RivalsCosmetics.IsPlaceholderCatalogName(name) then return nil end local cosmeticLibrary = RivalsCosmetics.ResolveCosmeticLibrary() local catalog = type(cosmeticLibrary) == 'table' and cosmeticLibrary.Cosmetics or nil local sourceData = type(catalog) == 'table' and catalog[name] or nil local nextData = {} if type(sourceData) == 'table' then for key, value in pairs(sourceData) do nextData[key] = value end elseif type(data) == 'table' then for key, value in pairs(data) do nextData[key] = value end end nextData.Name = name nextData.Type = nextData.Type or cosmeticType nextData.Seed = nextData.Seed or math.random(1, 1000000) local enumLibrary = ResolveEnumLibrary() if enumLibrary and type(enumLibrary.ToEnum) == 'function' then local ok, enumValue = pcall(enumLibrary.ToEnum, enumLibrary, name) if ok and enumValue ~= nil then nextData.Enum = enumValue nextData.ObjectID = enumValue end end if nextData.Inverted == nil then nextData.Inverted = false end return nextData end function RivalsCosmetics.HasRenderableDescendant(instance) if not instance then return false end if instance:IsA('BasePart') then return true end return instance:FindFirstChildWhichIsA('BasePart', true) ~= nil end function RivalsCosmetics.HasVisibleRenderableDescendant(instance) if not instance then return false end if instance:IsA('BasePart') then return instance.Transparency < 1 end for _, descendant in ipairs(instance:GetDescendants()) do if descendant:IsA('BasePart') and descendant.Transparency < 1 then return true end end return false end function RivalsCosmetics.IsModelContainer(instance) return instance and (instance:IsA('Model') or instance:IsA('Folder')) and RivalsCosmetics.HasRenderableDescendant(instance) end function RivalsCosmetics.SafeClone(instance) if not instance then return nil end local changedArchivableInstances = {} if instance.Archivable == false then changedArchivableInstances[#changedArchivableInstances + 1] = instance instance.Archivable = true end for _, descendant in ipairs(instance:GetDescendants()) do if descendant.Archivable == false then changedArchivableInstances[#changedArchivableInstances + 1] = descendant descendant.Archivable = true end end local ok, clone = pcall(function() return instance:Clone() end) for _, changedInstance in ipairs(changedArchivableInstances) do if changedInstance then changedInstance.Archivable = false end end if ok and clone then return clone end return nil end function RivalsCosmetics.AnchorPreviewDescendants(instance) if not instance then return end if instance:IsA('BasePart') then instance.Anchored = true end for _, descendant in ipairs(instance:GetDescendants()) do if descendant:IsA('BasePart') then descendant.Anchored = true end end end function RivalsCosmetics.IsSkinEnabled() return RivalsCosmetics.IsEnabled() end function RivalsCosmetics.IsWrapEnabled() return RivalsCosmetics.IsEnabled() end function RivalsCosmetics.ResolvePlayerScripts() return LP:FindFirstChild('PlayerScripts') end function RivalsCosmetics.ResolveCosmeticLibrary() local cachedCosmeticLibrary = RivalsCosmetics.Catalog.CosmeticLibrary if cachedCosmeticLibrary then return cachedCosmeticLibrary end local modules = ReplicatedStorage:FindFirstChild('Modules') local cosmeticLibraryModule = modules and modules:FindFirstChild('CosmeticLibrary') if not cosmeticLibraryModule then return nil end local ok, cosmeticLibrary = pcall(require, cosmeticLibraryModule) if not ok or type(cosmeticLibrary) ~= 'table' then return nil end RivalsCosmetics.Catalog.CosmeticLibrary = cosmeticLibrary return cosmeticLibrary end function RivalsCosmetics.ResolveWrapController() local cachedWrapController = RivalsCosmetics.Catalog.WrapController if cachedWrapController then return cachedWrapController end local playerScripts = RivalsCosmetics.ResolvePlayerScripts() local controllers = playerScripts and playerScripts:FindFirstChild('Controllers') local wrapControllerModule = controllers and controllers:FindFirstChild('WrapController') if not wrapControllerModule then return nil end local ok, wrapController = pcall(require, wrapControllerModule) if not ok or type(wrapController) ~= 'table' then return nil end RivalsCosmetics.Catalog.WrapController = wrapController return wrapController end function RivalsCosmetics.ResolveClientViewModelClass() local cached = RivalsCosmetics.Catalog.ClientViewModelClass if cached then return cached end local playerScripts = RivalsCosmetics.ResolvePlayerScripts() local modules = playerScripts and playerScripts:FindFirstChild('Modules') local replicated = modules and modules:FindFirstChild('ClientReplicatedClasses') local fighter = replicated and replicated:FindFirstChild('ClientFighter') local itemFolder = fighter and fighter:FindFirstChild('ClientItem') local cvmModule = itemFolder and itemFolder:FindFirstChild('ClientViewModel') if not cvmModule then return nil end local ok, cvmClass = pcall(require, cvmModule) if not ok or type(cvmClass) ~= 'table' then return nil end RivalsCosmetics.Catalog.ClientViewModelClass = cvmClass return cvmClass end function RivalsCosmetics.ResolveClientItemClass() local cached = RivalsCosmetics.Catalog.ClientItemClass if cached then return cached end local playerScripts = RivalsCosmetics.ResolvePlayerScripts() local modules = playerScripts and playerScripts:FindFirstChild('Modules') local replicated = modules and modules:FindFirstChild('ClientReplicatedClasses') local fighter = replicated and replicated:FindFirstChild('ClientFighter') local clientItemModule = fighter and fighter:FindFirstChild('ClientItem') if not clientItemModule then return nil end local ok, clientItem = pcall(require, clientItemModule) if not ok or type(clientItem) ~= 'table' then return nil end RivalsCosmetics.Catalog.ClientItemClass = clientItem return clientItem end function RivalsCosmetics.ResolvePlayerDataUtility() local cached = RivalsCosmetics.Catalog.PlayerDataUtility if cached then return cached end local modules = ReplicatedStorage:FindFirstChild('Modules') local utilityModule = modules and modules:FindFirstChild('PlayerDataUtility') if not utilityModule then return nil end local ok, utility = pcall(require, utilityModule) if not ok or type(utility) ~= 'table' then return nil end RivalsCosmetics.Catalog.PlayerDataUtility = utility return utility end function RivalsCosmetics.ResolveCosmeticSlot() local cached = RivalsCosmetics.Catalog.CosmeticSlot if cached then return cached end local playerScripts = RivalsCosmetics.ResolvePlayerScripts() local modules = playerScripts and playerScripts:FindFirstChild('Modules') local cosmeticSlotModule = modules and modules:FindFirstChild('CosmeticSlot') if not cosmeticSlotModule then return nil end local ok, cosmeticSlot = pcall(require, cosmeticSlotModule) if not ok or type(cosmeticSlot) ~= 'table' then return nil end RivalsCosmetics.Catalog.CosmeticSlot = cosmeticSlot return cosmeticSlot end function RivalsCosmetics.ResolveClientEntityClass() local cached = RivalsCosmetics.Catalog.ClientEntityClass if cached then return cached end local playerScripts = RivalsCosmetics.ResolvePlayerScripts() local modules = playerScripts and playerScripts:FindFirstChild('Modules') local replicated = modules and modules:FindFirstChild('ClientReplicatedClasses') local entityModule = replicated and replicated:FindFirstChild('ClientEntity') if not entityModule then return nil end local ok, clientEntity = pcall(require, entityModule) if not ok or type(clientEntity) ~= 'table' then return nil end RivalsCosmetics.Catalog.ClientEntityClass = clientEntity return clientEntity end function RivalsCosmetics.ResolvePlayerDataController() local cachedPlayerDataController = RivalsCosmetics.Catalog.PlayerDataController if cachedPlayerDataController then return cachedPlayerDataController end local playerScripts = RivalsCosmetics.ResolvePlayerScripts() local controllers = playerScripts and playerScripts:FindFirstChild('Controllers') local playerDataControllerModule = controllers and controllers:FindFirstChild('PlayerDataController') if not playerDataControllerModule then return nil end local ok, playerDataController = pcall(require, playerDataControllerModule) if not ok or type(playerDataController) ~= 'table' then return nil end RivalsCosmetics.Catalog.PlayerDataController = playerDataController return playerDataController end function RivalsCosmetics.ResolveViewModelsRoot() local cachedViewModelsRoot = RivalsCosmetics.Catalog.ViewModelsRoot if cachedViewModelsRoot and cachedViewModelsRoot.Parent then return cachedViewModelsRoot end local playerScripts = RivalsCosmetics.ResolvePlayerScripts() local assets = playerScripts and playerScripts:FindFirstChild('Assets') local viewModelsRoot = assets and assets:FindFirstChild('ViewModels') if not viewModelsRoot then return nil end RivalsCosmetics.Catalog.ViewModelsRoot = viewModelsRoot return viewModelsRoot end function RivalsCosmetics.ResolveDefaultViewModelsRoot() local cachedDefaultViewModelsRoot = RivalsCosmetics.Catalog.DefaultViewModelsRoot if cachedDefaultViewModelsRoot and cachedDefaultViewModelsRoot.Parent then return cachedDefaultViewModelsRoot end local starterPlayer = game:GetService('StarterPlayer') local starterPlayerScripts = starterPlayer and starterPlayer:FindFirstChild('StarterPlayerScripts') local assets = starterPlayerScripts and starterPlayerScripts:FindFirstChild('Assets') local viewModelsRoot = assets and assets:FindFirstChild('ViewModels') if not viewModelsRoot then return nil end RivalsCosmetics.Catalog.DefaultViewModelsRoot = viewModelsRoot return viewModelsRoot end function RivalsCosmetics.ResolveWeaponTemplatesFolder() local viewModelsRoot = RivalsCosmetics.ResolveViewModelsRoot() return viewModelsRoot and viewModelsRoot:FindFirstChild('Weapons') or nil end function RivalsCosmetics.ResolveDefaultWeaponTemplatesFolder() local viewModelsRoot = RivalsCosmetics.ResolveDefaultViewModelsRoot() return viewModelsRoot and viewModelsRoot:FindFirstChild('Weapons') or nil end function RivalsCosmetics.ResolveDefaultWeaponTemplate(weaponName) if type(weaponName) ~= 'string' or weaponName == '' then return nil end local weaponTemplatesFolder = RivalsCosmetics.ResolveDefaultWeaponTemplatesFolder() local weaponTemplate = weaponTemplatesFolder and weaponTemplatesFolder:FindFirstChild(weaponName) if weaponTemplate and RivalsCosmetics.IsModelContainer(weaponTemplate) then return weaponTemplate end return nil end function RivalsCosmetics.ResolveWeaponTemplate(weaponName) if type(weaponName) ~= 'string' or weaponName == '' then return nil end local weaponTemplatesFolder = RivalsCosmetics.ResolveWeaponTemplatesFolder() local weaponTemplate = weaponTemplatesFolder and weaponTemplatesFolder:FindFirstChild(weaponName) if weaponTemplate and RivalsCosmetics.IsModelContainer(weaponTemplate) then return weaponTemplate end return nil end function RivalsCosmetics.ResolveWeaponNames() local cachedWeaponNames = RivalsCosmetics.Catalog.WeaponNames if cachedWeaponNames then return cachedWeaponNames end local cosmeticLibrary = RivalsCosmetics.ResolveCosmeticLibrary() local cosmetics = cosmeticLibrary and cosmeticLibrary.Cosmetics or nil local weaponNames = {} local seenWeaponNames = {} if type(cosmetics) == 'table' then for _, cosmeticData in pairs(cosmetics) do local weaponName = cosmeticData and cosmeticData.ItemName or nil if cosmeticData and cosmeticData.Type == 'Skin' and type(weaponName) == 'string' and weaponName ~= '' and not RivalsCosmetics.IsPlaceholderCatalogName(weaponName) and seenWeaponNames[weaponName] ~= true and RivalsCosmetics.ResolveWeaponTemplate(weaponName) then seenWeaponNames[weaponName] = true weaponNames[#weaponNames + 1] = weaponName end end end table.sort(weaponNames) RivalsCosmetics.Catalog.WeaponNames = weaponNames return weaponNames end function RivalsCosmetics.ResolveEditorWeaponNames() local values = { RIVALS_COSMETIC_ALL_WEAPONS } for _, weaponName in ipairs(RivalsCosmetics.ResolveWeaponNames()) do values[#values + 1] = weaponName end return values end function RivalsCosmetics.ResolveWeaponSkinValues(weaponName) if type(weaponName) ~= 'string' or weaponName == '' then return RivalsCosmetics.ResolveSentinelValues() end local cacheKey = string.format('SkinValues_%s', weaponName) local cachedValues = RivalsCosmetics.Catalog[cacheKey] if cachedValues then return cachedValues end local cosmeticLibrary = RivalsCosmetics.ResolveCosmeticLibrary() local cosmetics = cosmeticLibrary and cosmeticLibrary.Cosmetics or nil local values = RivalsCosmetics.ResolveSentinelValues() if type(cosmetics) == 'table' then local skinNames = {} for cosmeticName, cosmeticData in pairs(cosmetics) do if cosmeticData and cosmeticData.Type == 'Skin' and cosmeticData.ItemName == weaponName and not RivalsCosmetics.IsPlaceholderCatalogName(cosmeticName) then skinNames[#skinNames + 1] = cosmeticName end end table.sort(skinNames) for _, skinName in ipairs(skinNames) do values[#values + 1] = skinName end end RivalsCosmetics.Catalog[cacheKey] = values return values end function RivalsCosmetics.ResolveWrapValues() local cachedWrapValues = RivalsCosmetics.Catalog.WrapValues if cachedWrapValues then return cachedWrapValues end local cosmeticLibrary = RivalsCosmetics.ResolveCosmeticLibrary() local cosmetics = cosmeticLibrary and cosmeticLibrary.Cosmetics or nil local values = RivalsCosmetics.ResolveSentinelValues() if type(cosmetics) == 'table' then local wrapNames = {} for cosmeticName, cosmeticData in pairs(cosmetics) do if cosmeticData and cosmeticData.Type == 'Wrap' and not RivalsCosmetics.IsPlaceholderCatalogName(cosmeticName) then wrapNames[#wrapNames + 1] = cosmeticName end end table.sort(wrapNames) for _, wrapName in ipairs(wrapNames) do values[#values + 1] = wrapName end end RivalsCosmetics.Catalog.WrapValues = values return values end function RivalsCosmetics.ResolveCharmAssetsFolder() local cached = RivalsCosmetics.Catalog.CharmAssetsFolder if cached and cached.Parent then return cached end local playerScripts = RivalsCosmetics.ResolvePlayerScripts() local assets = playerScripts and playerScripts:FindFirstChild('Assets') local charmsFolder = assets and assets:FindFirstChild('Charms') if charmsFolder then RivalsCosmetics.Catalog.CharmAssetsFolder = charmsFolder end return charmsFolder end function RivalsCosmetics.ResolveCharmValues() local cachedCharmValues = RivalsCosmetics.Catalog.CharmValues if cachedCharmValues then return cachedCharmValues end local cosmeticLibrary = RivalsCosmetics.ResolveCosmeticLibrary() local cosmetics = cosmeticLibrary and cosmeticLibrary.Cosmetics or nil local assetsFolder = RivalsCosmetics.ResolveCharmAssetsFolder() local availableAssetNames = nil if assetsFolder then availableAssetNames = {} for _, child in ipairs(assetsFolder:GetChildren()) do availableAssetNames[child.Name] = true end end local values = RivalsCosmetics.ResolveSentinelValues() if type(cosmetics) == 'table' then local charmNames = {} for cosmeticName, cosmeticData in pairs(cosmetics) do if cosmeticData and cosmeticData.Type == 'Charm' and not RivalsCosmetics.IsPlaceholderCatalogName(cosmeticName) then if availableAssetNames == nil or availableAssetNames[cosmeticName] == true then charmNames[#charmNames + 1] = cosmeticName end end end table.sort(charmNames) for _, charmName in ipairs(charmNames) do values[#values + 1] = charmName end end RivalsCosmetics.Catalog.CharmValues = values return values end function RivalsCosmetics.ResolveRankCharmCatalog() local cached = RivalsCosmetics.Catalog.RankCharmCatalog if cached then return cached end local modules = ReplicatedStorage:FindFirstChild('Modules') local seasonLibraryModule = modules and modules:FindFirstChild('SeasonLibrary') if not seasonLibraryModule then return nil end local ok, seasonLibrary = pcall(require, seasonLibraryModule) local currentSeason = ok and type(seasonLibrary) == 'table' and seasonLibrary.CurrentSeason or nil local rankProfile = type(currentSeason) == 'table' and currentSeason.RankProfile or nil local ranksOrder = type(rankProfile) == 'table' and rankProfile.RanksOrder or nil local ranks = type(rankProfile) == 'table' and rankProfile.Ranks or nil local seasonsByVersion = type(seasonLibrary) == 'table' and seasonLibrary.SeasonsByVersion or nil if type(ranksOrder) ~= 'table' or type(ranks) ~= 'table' or type(seasonsByVersion) ~= 'table' then return nil end local rankNames = {} local ranksByName = {} for _, rankName in ipairs(ranksOrder) do local rankData = ranks[rankName] if type(rankName) == 'string' and type(rankData) == 'table' then rankNames[#rankNames + 1] = rankName ranksByName[rankName] = { Name = rankName, RequiredELO = rankData.RequiredELO, RequiresLeaderboard = rankData.RequiredELOLeaderboardRanking ~= nil, } end end local versions = {} for version in pairs(seasonsByVersion) do versions[#versions + 1] = version end table.sort(versions) local seasonEntries = {} local seasonLabels = {} local seasonByLabel = {} local seasonByCharmName = {} for _, version in ipairs(versions) do local seasonData = seasonsByVersion[version] if type(seasonData) == 'table' then local charmName = string.format('Season %s', tostring(version)) local label = string.format('Season %s (%s)', tostring(version), tostring(seasonData.Name)) local entry = { Version = version, CharmName = charmName, Label = label, } seasonEntries[#seasonEntries + 1] = entry seasonLabels[#seasonLabels + 1] = label seasonByLabel[label] = charmName seasonByCharmName[charmName] = entry end end local catalog = { RankNames = rankNames, RanksByName = ranksByName, SeasonEntries = seasonEntries, SeasonLabels = seasonLabels, SeasonByLabel = seasonByLabel, SeasonByCharmName = seasonByCharmName, } RivalsCosmetics.Catalog.RankCharmCatalog = catalog return catalog end function RivalsCosmetics.ResolveRankCharmSeasonValues() local catalog = RivalsCosmetics.ResolveRankCharmCatalog() return catalog and table.clone(catalog.SeasonLabels) or {} end function RivalsCosmetics.ResolveRankCharmRankValues() local values = { RIVALS_RANK_CHARM_OFF } local catalog = RivalsCosmetics.ResolveRankCharmCatalog() for _, rankName in ipairs(catalog and catalog.RankNames or {}) do values[#values + 1] = rankName end return values end function RivalsCosmetics.IsSeasonRankCharm(charmName) return type(charmName) == 'string' and #charmName >= 8 and charmName:sub(1, 7) == 'Season ' and tonumber(charmName:sub(8)) ~= nil end function RivalsCosmetics.ParseRankCharmLeaderboardRank(value) local trimmed = tostring(value or ''):match('^%s*(.-)%s*$') if not trimmed or trimmed == '' then return nil end local rank = tonumber(trimmed) if not rank or rank < 1 then return nil end return math.floor(rank) end function RivalsCosmetics.BuildRankCharmMetadata(charmName) if not RivalsCosmetics.IsSeasonRankCharm(charmName) then return nil end local override = RivalsCosmeticsState.RankCharmOverridesBySeason[charmName] local catalog = type(override) == 'table' and RivalsCosmetics.ResolveRankCharmCatalog() or nil local rank = catalog and catalog.RanksByName[override.rankName] or nil if not rank then return nil end local leaderboardRank = override.leaderboardRank if rank.Name == 'Unranked' then return { SeasonELO = nil, SeasonLeaderboardRank = leaderboardRank, } end if rank.RequiresLeaderboard and leaderboardRank == nil then leaderboardRank = 1 end return { SeasonELO = rank.RequiredELO, SeasonLeaderboardRank = leaderboardRank, } end function RivalsCosmetics.PatchRankCharmPayload(payload) if type(payload) ~= 'table' then return nil end local metadata = RivalsCosmetics.BuildRankCharmMetadata(payload.Name) if not metadata then return nil end local patched = table.clone(payload) patched.Metadata = metadata return patched end function RivalsCosmetics.NormalizeRankCharmOverrides(value) local normalized = {} local catalog = RivalsCosmetics.ResolveRankCharmCatalog() if type(value) ~= 'table' or not catalog then return normalized end local function addOverride(charmName, override) if not RivalsCosmetics.IsSeasonRankCharm(charmName) or not catalog.SeasonByCharmName[charmName] or type(override) ~= 'table' then return end local rankName = type(override.rankName) == 'string' and override.rankName or nil if not rankName or not catalog.RanksByName[rankName] then return end local leaderboardRank = RivalsCosmetics.ParseRankCharmLeaderboardRank(override.leaderboardRank) normalized[charmName] = { rankName = rankName, leaderboardRank = leaderboardRank, } end if value.rankName ~= nil or value.leaderboardRank ~= nil then for _, season in ipairs(catalog.SeasonEntries) do addOverride(season.CharmName, value) end return normalized end for charmName, override in pairs(value) do addOverride(charmName, override) end return normalized end function RivalsCosmetics.ResolveSelectedRankCharmSeason() local catalog = RivalsCosmetics.ResolveRankCharmCatalog() if not catalog then return nil end local charmName = RivalsCosmeticsState.SelectedRankCharmSeason if catalog.SeasonByCharmName[charmName] then return charmName end local firstSeason = catalog.SeasonEntries[1] charmName = firstSeason and firstSeason.CharmName or nil RivalsCosmeticsState.SelectedRankCharmSeason = charmName return charmName end function RivalsCosmetics.SyncRankCharmEditorFromState() local catalog = RivalsCosmetics.ResolveRankCharmCatalog() local charmName = catalog and RivalsCosmetics.ResolveSelectedRankCharmSeason() or nil if not charmName then return false end local season = catalog.SeasonByCharmName[charmName] local override = RivalsCosmeticsState.RankCharmOverridesBySeason[charmName] local rankName = type(override) == 'table' and override.rankName or RIVALS_RANK_CHARM_OFF local leaderboardRank = type(override) == 'table' and override.leaderboardRank or nil RivalsCosmeticsState.RefreshingDropdownValues = RivalsCosmeticsState.RefreshingDropdownValues + 1 RivalsCosmetics.SyncDropdownOption( RivalsCosmetics.ResolveEditorOptionId('RankSeason'), catalog.SeasonLabels, season.Label ) RivalsCosmetics.SyncDropdownOption( RivalsCosmetics.ResolveEditorOptionId('RankName'), RivalsCosmetics.ResolveRankCharmRankValues(), rankName ) local leaderboardOption = Options[RivalsCosmetics.ResolveEditorOptionId('RankLeaderboard')] if leaderboardOption and type(leaderboardOption.SetValue) == 'function' then pcall(function() leaderboardOption:SetValue(leaderboardRank ~= nil and tostring(leaderboardRank) or '') end) end RivalsCosmeticsState.RefreshingDropdownValues = math.max(RivalsCosmeticsState.RefreshingDropdownValues - 1, 0) return true end function RivalsCosmetics.SetSelectedRankCharmSeason(value) local catalog = RivalsCosmetics.ResolveRankCharmCatalog() local charmName = catalog and (catalog.SeasonByLabel[value] or (catalog.SeasonByCharmName[value] and value)) or nil if not charmName then return false end RivalsCosmeticsState.SelectedRankCharmSeason = charmName RivalsCosmetics.SyncRankCharmEditorFromState() return true end function RivalsCosmetics.SetRankCharmOverrideForSeason(charmName, rankName, leaderboardValue) local catalog = RivalsCosmetics.ResolveRankCharmCatalog() if not catalog or not RivalsCosmetics.IsSeasonRankCharm(charmName) then return false end local previous = RivalsCosmeticsState.RankCharmOverridesBySeason[charmName] if rankName == nil or rankName == RIVALS_RANK_CHARM_OFF or not catalog.RanksByName[rankName] then if previous == nil then return false end RivalsCosmeticsState.RankCharmOverridesBySeason[charmName] = nil return true end local leaderboardRank = RivalsCosmetics.ParseRankCharmLeaderboardRank(leaderboardValue) if type(previous) == 'table' and previous.rankName == rankName and previous.leaderboardRank == leaderboardRank then return false end RivalsCosmeticsState.RankCharmOverridesBySeason[charmName] = { rankName = rankName, leaderboardRank = leaderboardRank, } return true end function RivalsCosmetics.RefreshRankCharmApplication() RivalsCosmetics.RestoreInjectedWeaponData() pcall(RivalsCosmetics.UpdatePreviewViewport) return RivalsCosmetics.ApplyAll() end function RivalsCosmetics.UpdateSelectedRankCharmOverride(rankName, leaderboardValue) local charmName = RivalsCosmetics.ResolveSelectedRankCharmSeason() if not charmName then return false end local changed = RivalsCosmetics.SetRankCharmOverrideForSeason(charmName, rankName, leaderboardValue) RivalsCosmetics.SyncRankCharmEditorFromState() if changed then RivalsCosmetics.RefreshRankCharmApplication() if type(RivalsCosmetics.QueueCosmeticPresetAutoSave) == 'function' then RivalsCosmetics.QueueCosmeticPresetAutoSave() end end return changed end function RivalsCosmetics.ResolveSkinOptionId(weaponName) local cachedOptionId = RivalsCosmetics.SkinOptionIdsByWeapon[weaponName] if cachedOptionId then return cachedOptionId end local sanitizedWeaponName = tostring(weaponName or ''):gsub('[^%w]+', '') local optionId = string.format('P5SKIN_%s', sanitizedWeaponName) RivalsCosmetics.SkinOptionIdsByWeapon[weaponName] = optionId return optionId end function RivalsCosmetics.ResolveWrapOptionId(weaponName) local cachedOptionId = RivalsCosmetics.WrapOptionIdsByWeapon[weaponName] if cachedOptionId then return cachedOptionId end local sanitizedWeaponName = tostring(weaponName or ''):gsub('[^%w]+', '') local optionId = string.format('P5WRAP_%s', sanitizedWeaponName) RivalsCosmetics.WrapOptionIdsByWeapon[weaponName] = optionId return optionId end function RivalsCosmetics.ResolveCharmOptionId(weaponName) local cachedOptionId = RivalsCosmetics.CharmOptionIdsByWeapon[weaponName] if cachedOptionId then return cachedOptionId end local sanitizedWeaponName = tostring(weaponName or ''):gsub('[^%w]+', '') local optionId = string.format('P5CHARM_%s', sanitizedWeaponName) RivalsCosmetics.CharmOptionIdsByWeapon[weaponName] = optionId return optionId end function RivalsCosmetics.ResolveWrapInvertedOptionId(weaponName) local cachedOptionId = RivalsCosmetics.WrapInvertedOptionIdsByWeapon[weaponName] if cachedOptionId then return cachedOptionId end local sanitizedWeaponName = tostring(weaponName or ''):gsub('[^%w]+', '') local optionId = string.format('P5WRAPINV_%s', sanitizedWeaponName) RivalsCosmetics.WrapInvertedOptionIdsByWeapon[weaponName] = optionId return optionId end function RivalsCosmetics.ResolveSelectionTable(kind) if kind == 'Skin' then return RivalsCosmeticsState.SelectedSkinByWeapon elseif kind == 'Wrap' then return RivalsCosmeticsState.SelectedWrapByWeapon elseif kind == 'Charm' then return RivalsCosmeticsState.SelectedCharmByWeapon elseif kind == 'Finisher' then return RivalsCosmeticsState.SelectedFinisherByWeapon elseif kind == 'WrapInverted' then return RivalsCosmeticsState.SelectedWrapInvertedByWeapon end return nil end function RivalsCosmetics.IsFavoriteKind(kind) for _, availableKind in ipairs(RivalsCosmetics.FavoriteKinds) do if kind == availableKind then return true end end return false end function RivalsCosmetics.NormalizeFavoritesState(value) local normalized = { favorites = {}, onlyUse = {} } if type(value) ~= 'table' then return normalized end local favorites = type(value.favorites) == 'table' and value.favorites or type(value.favoritedCosmetics) == 'table' and value.favoritedCosmetics or {} for weaponName, entries in pairs(favorites) do if type(weaponName) == 'string' and weaponName ~= '' and type(entries) == 'table' then local cleanEntries = {} for cosmeticName, enabled in pairs(entries) do if type(cosmeticName) == 'string' and cosmeticName ~= '' and enabled == true then cleanEntries[cosmeticName] = true end end if next(cleanEntries) ~= nil then normalized.favorites[weaponName] = cleanEntries end end end local onlyUse = type(value.onlyUse) == 'table' and value.onlyUse or type(value.onlyUseFavoritesSelections) == 'table' and value.onlyUseFavoritesSelections or {} for weaponName, entries in pairs(onlyUse) do if type(weaponName) == 'string' and weaponName ~= '' and type(entries) == 'table' then local cleanEntries = {} for kind, enabled in pairs(entries) do if RivalsCosmetics.IsFavoriteKind(kind) and enabled == true then cleanEntries[kind] = true end end if next(cleanEntries) ~= nil then normalized.onlyUse[weaponName] = cleanEntries end end end return normalized end function RivalsCosmetics.ResolveFavoritesState() return RivalsCosmetics.NormalizeFavoritesState({ favorites = RivalsCosmeticsState.FavoritedCosmeticsByWeapon, onlyUse = RivalsCosmeticsState.OnlyUseFavoritesByWeapon, }) end function RivalsCosmetics.IsFavorited(weaponName, cosmeticName) local entries = RivalsCosmeticsState.FavoritedCosmeticsByWeapon[weaponName] return type(entries) == 'table' and entries[cosmeticName] == true end function RivalsCosmetics.SetFavorited(weaponName, cosmeticName, enabled) if not RivalsCosmetics.IsKnownWeaponName(weaponName) or not RivalsCosmetics.IsCosmeticValueSelected(cosmeticName) then return false end local entries = RivalsCosmeticsState.FavoritedCosmeticsByWeapon[weaponName] if type(entries) ~= 'table' then entries = {} RivalsCosmeticsState.FavoritedCosmeticsByWeapon[weaponName] = entries end if enabled then entries[cosmeticName] = true else entries[cosmeticName] = nil if next(entries) == nil then RivalsCosmeticsState.FavoritedCosmeticsByWeapon[weaponName] = nil end end return true end function RivalsCosmetics.UsesOnlyFavorites(weaponName, kind) local entries = RivalsCosmeticsState.OnlyUseFavoritesByWeapon[weaponName] return type(entries) == 'table' and entries[kind] == true end function RivalsCosmetics.SetOnlyUseFavorites(weaponName, kind, enabled) if not RivalsCosmetics.IsKnownWeaponName(weaponName) or not RivalsCosmetics.IsFavoriteKind(kind) then return false end local entries = RivalsCosmeticsState.OnlyUseFavoritesByWeapon[weaponName] if type(entries) ~= 'table' then entries = {} RivalsCosmeticsState.OnlyUseFavoritesByWeapon[weaponName] = entries end if enabled then entries[kind] = true else entries[kind] = nil if next(entries) == nil then RivalsCosmeticsState.OnlyUseFavoritesByWeapon[weaponName] = nil end end local randomByKind = RivalsCosmeticsState.StableRandomSelectionByWeapon[weaponName] if randomByKind then randomByKind[kind] = nil end return true end function RivalsCosmetics.SetStoredSelection(weaponName, kind, value) if type(weaponName) ~= 'string' or weaponName == '' then return false end local selectionTable = RivalsCosmetics.ResolveSelectionTable(kind) if not selectionTable then return false end if kind ~= 'WrapInverted' then value = RivalsCosmetics.NormalizeSelectionValue(value) end if value == nil or value == '' then selectionTable[weaponName] = nil else selectionTable[weaponName] = value end if RivalsCosmeticsState.RefreshingDropdownValues == 0 and not RivalsCosmeticsState.ApplyingCosmeticPreset and type(RivalsCosmetics.QueueCosmeticConfigAutoSave) == 'function' then RivalsCosmetics.QueueCosmeticConfigAutoSave() end return true end function RivalsCosmetics.ResolveStoredSelectionValue(weaponName, kind) if type(weaponName) ~= 'string' or weaponName == '' then return nil end local selectionTable = RivalsCosmetics.ResolveSelectionTable(kind) local storedValue = selectionTable and selectionTable[weaponName] if storedValue ~= nil then return storedValue end if kind == 'Skin' then local option = Options[RivalsCosmetics.ResolveSkinOptionId(weaponName)] return option and RivalsCosmetics.NormalizeSelectionValue(option.Value) or nil elseif kind == 'Wrap' then local option = Options[RivalsCosmetics.ResolveWrapOptionId(weaponName)] return option and RivalsCosmetics.NormalizeSelectionValue(option.Value) or nil elseif kind == 'Charm' then local option = Options[RivalsCosmetics.ResolveCharmOptionId(weaponName)] return option and RivalsCosmetics.NormalizeSelectionValue(option.Value) or nil elseif kind == 'Finisher' then local option = Options[RivalsCosmetics.ResolveFinisherOptionId(weaponName)] return option and RivalsCosmetics.NormalizeSelectionValue(option.Value) or nil elseif kind == 'WrapInverted' then local toggle = Toggles[RivalsCosmetics.ResolveWrapInvertedOptionId(weaponName)] if toggle and toggle.Value ~= nil then return toggle.Value == true end end return nil end function RivalsCosmetics.ResolveValidDropdownValue(value, values) if type(values) == 'table' then for _, availableValue in ipairs(values) do if availableValue == value then return value end end return values[1] end return nil end function RivalsCosmetics.ResolveBaseSelectionValues(weaponName, kind) if kind == 'Skin' then return RivalsCosmetics.ResolveWeaponSkinValues(weaponName) elseif kind == 'Wrap' then return RivalsCosmetics.ResolveWrapValues() elseif kind == 'Charm' then return RivalsCosmetics.ResolveCharmValues() elseif kind == 'Finisher' then return RivalsCosmetics.ResolveFinisherValues() end return nil end function RivalsCosmetics.ResolveSelectionValues(weaponName, kind) local values = RivalsCosmetics.ResolveBaseSelectionValues(weaponName, kind) if not RivalsCosmetics.UsesOnlyFavorites(weaponName, kind) then return values end local filtered = {} for _, value in ipairs(values or {}) do if not RivalsCosmetics.IsCosmeticValueSelected(value) or RivalsCosmetics.IsFavorited(weaponName, value) then filtered[#filtered + 1] = value end end return filtered end function RivalsCosmetics.ResolveEffectiveSelectionValue(weaponName, kind, value) value = RivalsCosmetics.NormalizeSelectionValue(value) if RivalsCosmetics.IsCosmeticValueSelected(value) and RivalsCosmetics.UsesOnlyFavorites(weaponName, kind) and not RivalsCosmetics.IsFavorited(weaponName, value) then return RIVALS_COSMETIC_UNSELECTED end if value ~= RIVALS_COSMETIC_RANDOM then return value end local randomByKind = RivalsCosmeticsState.StableRandomSelectionByWeapon[weaponName] if not randomByKind then randomByKind = {} RivalsCosmeticsState.StableRandomSelectionByWeapon[weaponName] = randomByKind end local values = RivalsCosmetics.ResolveSelectionValues(weaponName, kind) local cachedValue = randomByKind[kind] if RivalsCosmetics.ResolveValidDropdownValue(cachedValue, values) == cachedValue and RivalsCosmetics.IsCosmeticValueSelected(cachedValue) then return cachedValue end local candidates = {} for _, candidate in ipairs(values or {}) do if RivalsCosmetics.IsCosmeticValueSelected(candidate) then candidates[#candidates + 1] = candidate end end if #candidates == 0 then randomByKind[kind] = nil return RIVALS_COSMETIC_UNSELECTED end cachedValue = candidates[math.random(1, #candidates)] randomByKind[kind] = cachedValue return cachedValue end function RivalsCosmetics.ResolveWrapInvertedValue(weaponName) if not RivalsCosmetics.IsEnabled() then return false end if type(weaponName) ~= 'string' or weaponName == '' then return false end local value = RivalsCosmetics.ResolveStoredSelectionValue(weaponName, 'WrapInverted') return value == true end function RivalsCosmetics.ResolveDefaultPreviewWeaponName() local weaponNames = RivalsCosmetics.ResolveWeaponNames() for _, weaponName in ipairs(weaponNames) do local value = RivalsCosmetics.ResolveStoredSelectionValue(weaponName, 'Skin') if RivalsCosmetics.IsCosmeticValueSelected(value) then return weaponName end end for _, weaponName in ipairs(weaponNames) do local wrapValue = RivalsCosmetics.ResolveStoredSelectionValue(weaponName, 'Wrap') local charmValue = RivalsCosmetics.ResolveStoredSelectionValue(weaponName, 'Charm') local finisherValue = RivalsCosmetics.ResolveStoredSelectionValue(weaponName, 'Finisher') local invertedValue = RivalsCosmetics.ResolveStoredSelectionValue(weaponName, 'WrapInverted') if RivalsCosmetics.IsCosmeticValueSelected(wrapValue) or RivalsCosmetics.IsCosmeticValueSelected(charmValue) or RivalsCosmetics.IsCosmeticValueSelected(finisherValue) or invertedValue == true then return weaponName end end return weaponNames[1] end function RivalsCosmetics.SetPreviewWeaponName(weaponName) if type(weaponName) ~= 'string' or weaponName == '' then return false end for _, availableWeaponName in ipairs(RivalsCosmetics.ResolveWeaponNames()) do if availableWeaponName == weaponName then RivalsCosmeticsState.PreviewWeaponName = weaponName return true end end return false end function RivalsCosmetics.ResolvePreviewWeaponName() local previewWeaponName = RivalsCosmeticsState.PreviewWeaponName if RivalsCosmetics.SetPreviewWeaponName(previewWeaponName) then return previewWeaponName end previewWeaponName = RivalsCosmetics.ResolveDefaultPreviewWeaponName() RivalsCosmeticsState.PreviewWeaponName = previewWeaponName return previewWeaponName end function RivalsCosmetics.ResolveSkinSelectionValue(weaponName) if not RivalsCosmetics.IsSkinEnabled() then return nil end if type(weaponName) ~= 'string' or weaponName == '' then return nil end local pendingSkinName = RivalsCosmeticsState.PendingSkinSelectionByWeapon[weaponName] local value = pendingSkinName if value == nil then value = RivalsCosmetics.ResolveStoredSelectionValue(weaponName, 'Skin') end return RivalsCosmetics.ResolveEffectiveSelectionValue(weaponName, 'Skin', value) end function RivalsCosmetics.ResolveSelectedSkinName(weaponName) local value = RivalsCosmetics.ResolveSkinSelectionValue(weaponName) if RivalsCosmetics.IsCosmeticValueSelected(value) then return value end return nil end function RivalsCosmetics.ResolveWrapSelectionValue(weaponName) if not RivalsCosmetics.IsWrapEnabled() then return nil end if type(weaponName) ~= 'string' or weaponName == '' then return nil end local pendingWrapName = RivalsCosmeticsState.PendingWrapSelectionByWeapon[weaponName] local value = pendingWrapName if value == nil then value = RivalsCosmetics.ResolveStoredSelectionValue(weaponName, 'Wrap') end return RivalsCosmetics.ResolveEffectiveSelectionValue(weaponName, 'Wrap', value) end function RivalsCosmetics.ResolveSelectedWrapName(weaponName) local value = RivalsCosmetics.ResolveWrapSelectionValue(weaponName) if RivalsCosmetics.IsCosmeticValueSelected(value) then return value end return nil end function RivalsCosmetics.ResolveCharmSelectionValue(weaponName) if not RivalsCosmetics.IsEnabled() then return nil end if type(weaponName) ~= 'string' or weaponName == '' then return nil end local pendingCharmName = RivalsCosmeticsState.PendingCharmSelectionByWeapon[weaponName] local value = pendingCharmName if value == nil then value = RivalsCosmetics.ResolveStoredSelectionValue(weaponName, 'Charm') end return RivalsCosmetics.ResolveEffectiveSelectionValue(weaponName, 'Charm', value) end function RivalsCosmetics.ResolveSelectedCharmName(weaponName) local value = RivalsCosmetics.ResolveCharmSelectionValue(weaponName) if RivalsCosmetics.IsCosmeticValueSelected(value) then return value end return nil end function RivalsCosmetics.ResolveFinisherAssetsFolder() local cached = RivalsCosmetics.Catalog.FinisherAssetsFolder if cached and cached.Parent then return cached end local replicatedStorage = game:GetService('ReplicatedStorage') local modules = replicatedStorage:FindFirstChild('Modules') local finishersFolder = modules and modules:FindFirstChild('Finishers') if finishersFolder then RivalsCosmetics.Catalog.FinisherAssetsFolder = finishersFolder end return finishersFolder end function RivalsCosmetics.ResolveFinisherValues() local cachedFinisherValues = RivalsCosmetics.Catalog.FinisherValues if cachedFinisherValues then return cachedFinisherValues end local cosmeticLibrary = RivalsCosmetics.ResolveCosmeticLibrary() local cosmetics = cosmeticLibrary and cosmeticLibrary.Cosmetics or nil local assetsFolder = RivalsCosmetics.ResolveFinisherAssetsFolder() local availableAssetNames = nil if assetsFolder then availableAssetNames = {} for _, child in ipairs(assetsFolder:GetChildren()) do availableAssetNames[child.Name] = true end end local values = RivalsCosmetics.ResolveSentinelValues() if type(cosmetics) == 'table' then local finisherNames = {} for cosmeticName, cosmeticData in pairs(cosmetics) do if cosmeticData and cosmeticData.Type == 'Finisher' and not RivalsCosmetics.IsPlaceholderCatalogName(cosmeticName) then if availableAssetNames == nil or availableAssetNames[cosmeticName] == true then finisherNames[#finisherNames + 1] = cosmeticName end end end table.sort(finisherNames) for _, finisherName in ipairs(finisherNames) do values[#values + 1] = finisherName end end RivalsCosmetics.Catalog.FinisherValues = values return values end function RivalsCosmetics.ResolveFinisherOptionId(weaponName) local cachedOptionId = RivalsCosmetics.FinisherOptionIdsByWeapon[weaponName] if cachedOptionId then return cachedOptionId end local sanitizedWeaponName = tostring(weaponName or ''):gsub('[^%w]+', '') local optionId = string.format('P5FINISHER_%s', sanitizedWeaponName) RivalsCosmetics.FinisherOptionIdsByWeapon[weaponName] = optionId return optionId end function RivalsCosmetics.ResolveCosmeticOptionInfo(optionId) if type(optionId) ~= 'string' then return nil end for _, weaponName in ipairs(RivalsCosmetics.ResolveWeaponNames()) do if optionId == RivalsCosmetics.ResolveSkinOptionId(weaponName) then return { weaponName = weaponName, kind = 'Skin', type = 'Dropdown' } elseif optionId == RivalsCosmetics.ResolveWrapOptionId(weaponName) then return { weaponName = weaponName, kind = 'Wrap', type = 'Dropdown' } elseif optionId == RivalsCosmetics.ResolveCharmOptionId(weaponName) then return { weaponName = weaponName, kind = 'Charm', type = 'Dropdown' } elseif optionId == RivalsCosmetics.ResolveFinisherOptionId(weaponName) then return { weaponName = weaponName, kind = 'Finisher', type = 'Dropdown' } elseif optionId == RivalsCosmetics.ResolveWrapInvertedOptionId(weaponName) then return { weaponName = weaponName, kind = 'WrapInverted', type = 'Toggle' } end end return nil end function RivalsCosmetics.ResolveFinisherSelectionValue(weaponName) if not RivalsCosmetics.IsEnabled() then return nil end if type(weaponName) ~= 'string' or weaponName == '' then return nil end local pendingFinisherName = RivalsCosmeticsState.PendingFinisherSelectionByWeapon[weaponName] local value = pendingFinisherName if value == nil then value = RivalsCosmetics.ResolveStoredSelectionValue(weaponName, 'Finisher') end return RivalsCosmetics.ResolveEffectiveSelectionValue(weaponName, 'Finisher', value) end function RivalsCosmetics.ResolveSelectedFinisherName(weaponName) local value = RivalsCosmetics.ResolveFinisherSelectionValue(weaponName) if RivalsCosmetics.IsCosmeticValueSelected(value) then return value end return nil end function RivalsCosmetics.ResolveSelectedPlaybackFinisherName() if not RivalsCosmetics.IsEnabled() then return nil end local fighter = ResolveLocalFighter() local item = fighter and fighter.EquippedItem or nil local weaponName = item and item.Name or nil if type(weaponName) ~= 'string' or weaponName == '' then return nil end local selectedFinisherName = RivalsCosmetics.ResolveSelectedFinisherName(weaponName) if type(selectedFinisherName) == 'string' and selectedFinisherName ~= '' then return selectedFinisherName end return nil end function RivalsCosmetics.IsKnownWeaponName(weaponName) if type(weaponName) ~= 'string' or weaponName == '' then return false end for _, availableWeaponName in ipairs(RivalsCosmetics.ResolveWeaponNames()) do if availableWeaponName == weaponName then return true end end return false end function RivalsCosmetics.IsKnownEditorWeaponName(weaponName) return weaponName == RIVALS_COSMETIC_ALL_WEAPONS or RivalsCosmetics.IsKnownWeaponName(weaponName) end function RivalsCosmetics.ResolveSelectedEditorWeaponName() local selectedWeaponName = RivalsCosmeticsState.SelectedEditorWeaponName if RivalsCosmetics.IsKnownEditorWeaponName(selectedWeaponName) then return selectedWeaponName end local option = Options[RivalsCosmetics.ResolveEditorOptionId('Weapon')] selectedWeaponName = option and option.Value or nil if RivalsCosmetics.IsKnownEditorWeaponName(selectedWeaponName) then RivalsCosmeticsState.SelectedEditorWeaponName = selectedWeaponName if selectedWeaponName ~= RIVALS_COSMETIC_ALL_WEAPONS then RivalsCosmetics.SetPreviewWeaponName(selectedWeaponName) end return selectedWeaponName end RivalsCosmeticsState.SelectedEditorWeaponName = RIVALS_COSMETIC_ALL_WEAPONS return RIVALS_COSMETIC_ALL_WEAPONS end function RivalsCosmetics.SyncDropdownOption(optionId, values, value) local option = Options[optionId] if not option then return false end if type(option.SetValues) == 'function' and type(values) == 'table' then pcall(function() option:SetValues(values) end) end if type(option.SetValue) == 'function' then pcall(function() option:SetValue(value) end) end return true end function RivalsCosmetics.ResolveEditorSelectionValue(weaponName, kind, values) if weaponName ~= RIVALS_COSMETIC_ALL_WEAPONS then local storedValue = RivalsCosmetics.ResolveStoredSelectionValue(weaponName, kind) return RivalsCosmetics.ResolveValidDropdownValue(storedValue, values) or RIVALS_COSMETIC_UNSELECTED end local commonValue = nil for _, availableWeaponName in ipairs(RivalsCosmetics.ResolveWeaponNames()) do local storedValue = RivalsCosmetics.ResolveStoredSelectionValue(availableWeaponName, kind) storedValue = RivalsCosmetics.ResolveValidDropdownValue(storedValue, values) or RIVALS_COSMETIC_UNSELECTED if commonValue == nil then commonValue = storedValue elseif commonValue ~= storedValue then return RIVALS_COSMETIC_UNSELECTED end end return commonValue or RIVALS_COSMETIC_UNSELECTED end function RivalsCosmetics.ResolveEditorWrapInvertedValue(weaponName) if weaponName ~= RIVALS_COSMETIC_ALL_WEAPONS then return RivalsCosmetics.ResolveStoredSelectionValue(weaponName, 'WrapInverted') == true end local sawWeapon = false for _, availableWeaponName in ipairs(RivalsCosmetics.ResolveWeaponNames()) do sawWeapon = true if RivalsCosmetics.ResolveStoredSelectionValue(availableWeaponName, 'WrapInverted') ~= true then return false end end return sawWeapon end function RivalsCosmetics.SyncActiveWeaponEditorFromState() local weaponName = RivalsCosmetics.ResolveSelectedEditorWeaponName() if not weaponName then return false end local weaponValues = RivalsCosmetics.ResolveEditorWeaponNames() local skinValues = weaponName == RIVALS_COSMETIC_ALL_WEAPONS and RivalsCosmetics.ResolveSentinelValues() or RivalsCosmetics.ResolveSelectionValues(weaponName, 'Skin') local wrapValues = weaponName == RIVALS_COSMETIC_ALL_WEAPONS and RivalsCosmetics.ResolveWrapValues() or RivalsCosmetics.ResolveSelectionValues(weaponName, 'Wrap') local charmValues = weaponName == RIVALS_COSMETIC_ALL_WEAPONS and RivalsCosmetics.ResolveCharmValues() or RivalsCosmetics.ResolveSelectionValues(weaponName, 'Charm') local finisherValues = weaponName == RIVALS_COSMETIC_ALL_WEAPONS and RivalsCosmetics.ResolveFinisherValues() or RivalsCosmetics.ResolveSelectionValues(weaponName, 'Finisher') local skinValue = RivalsCosmetics.ResolveEditorSelectionValue(weaponName, 'Skin', skinValues) local wrapValue = RivalsCosmetics.ResolveEditorSelectionValue(weaponName, 'Wrap', wrapValues) local charmValue = RivalsCosmetics.ResolveEditorSelectionValue(weaponName, 'Charm', charmValues) local finisherValue = RivalsCosmetics.ResolveEditorSelectionValue(weaponName, 'Finisher', finisherValues) local invertedValue = RivalsCosmetics.ResolveEditorWrapInvertedValue(weaponName) RivalsCosmeticsState.RefreshingDropdownValues = RivalsCosmeticsState.RefreshingDropdownValues + 1 RivalsCosmetics.SyncDropdownOption(RivalsCosmetics.ResolveEditorOptionId('Weapon'), weaponValues, weaponName) RivalsCosmetics.SyncDropdownOption(RivalsCosmetics.ResolveEditorOptionId('Skin'), skinValues, skinValue) RivalsCosmetics.SyncDropdownOption(RivalsCosmetics.ResolveEditorOptionId('Wrap'), wrapValues, wrapValue) RivalsCosmetics.SyncDropdownOption(RivalsCosmetics.ResolveEditorOptionId('Charm'), charmValues, charmValue) RivalsCosmetics.SyncDropdownOption(RivalsCosmetics.ResolveEditorOptionId('Finisher'), finisherValues, finisherValue) local invertedToggle = Toggles[RivalsCosmetics.ResolveEditorOptionId('WrapInverted')] if invertedToggle and type(invertedToggle.SetValue) == 'function' then pcall(function() invertedToggle:SetValue(invertedValue) end) end RivalsCosmeticsState.RefreshingDropdownValues = math.max(RivalsCosmeticsState.RefreshingDropdownValues - 1, 0) return true end function RivalsCosmetics.ResolveSelectedFavoriteKind() local option = Options.P5FAVORITE_TYPE local kind = option and option.Value or RivalsCosmetics.FavoriteKinds[1] return RivalsCosmetics.IsFavoriteKind(kind) and kind or RivalsCosmetics.FavoriteKinds[1] end function RivalsCosmetics.SyncFavoriteEditorFromState() local weaponName = RivalsCosmetics.ResolveSelectedEditorWeaponName() local kind = RivalsCosmetics.ResolveSelectedFavoriteKind() local onlyUse = weaponName ~= RIVALS_COSMETIC_ALL_WEAPONS and RivalsCosmetics.UsesOnlyFavorites(weaponName, kind) or false local toggle = Toggles.P5FAVORITE_ONLY if toggle and type(toggle.SetValue) == 'function' then RivalsCosmeticsState.RefreshingDropdownValues = RivalsCosmeticsState.RefreshingDropdownValues + 1 pcall(function() toggle:SetValue(onlyUse) end) RivalsCosmeticsState.RefreshingDropdownValues = math.max(RivalsCosmeticsState.RefreshingDropdownValues - 1, 0) end return weaponName ~= RIVALS_COSMETIC_ALL_WEAPONS end function RivalsCosmetics.SetSelectedFavoriteKind(kind) if not RivalsCosmetics.IsFavoriteKind(kind) then return false end RivalsCosmetics.SyncFavoriteEditorFromState() return true end function RivalsCosmetics.RefreshFavoriteApplication() RivalsCosmetics.SyncActiveWeaponEditorFromState() RivalsCosmetics.SyncFavoriteEditorFromState() RivalsCosmetics.RestoreAppliedCosmetics() pcall(RivalsCosmetics.UpdatePreviewViewport) RivalsCosmetics.ApplyAll() if type(RivalsCosmetics.QueueCosmeticPresetAutoSave) == 'function' then RivalsCosmetics.QueueCosmeticPresetAutoSave() end return true end function RivalsCosmetics.ToggleSelectedFavorite() local weaponName = RivalsCosmetics.ResolveSelectedEditorWeaponName() local kind = RivalsCosmetics.ResolveSelectedFavoriteKind() if weaponName == RIVALS_COSMETIC_ALL_WEAPONS then return false end local cosmeticName = RivalsCosmetics.ResolveStoredSelectionValue(weaponName, kind) if not RivalsCosmetics.IsCosmeticValueSelected(cosmeticName) then return false end if not RivalsCosmetics.SetFavorited( weaponName, cosmeticName, not RivalsCosmetics.IsFavorited(weaponName, cosmeticName) ) then return false end local randomByKind = RivalsCosmeticsState.StableRandomSelectionByWeapon[weaponName] if randomByKind then randomByKind[kind] = nil end return RivalsCosmetics.RefreshFavoriteApplication() end function RivalsCosmetics.SetSelectedOnlyUseFavorites(enabled) local weaponName = RivalsCosmetics.ResolveSelectedEditorWeaponName() local kind = RivalsCosmetics.ResolveSelectedFavoriteKind() if weaponName == RIVALS_COSMETIC_ALL_WEAPONS or not RivalsCosmetics.SetOnlyUseFavorites(weaponName, kind, enabled == true) then return false end return RivalsCosmetics.RefreshFavoriteApplication() end function RivalsCosmetics.SetSelectedEditorWeapon(weaponName) if not RivalsCosmetics.IsKnownEditorWeaponName(weaponName) then return false end RivalsCosmeticsState.SelectedEditorWeaponName = weaponName if weaponName ~= RIVALS_COSMETIC_ALL_WEAPONS then RivalsCosmetics.SetPreviewWeaponName(weaponName) end RivalsCosmetics.SyncActiveWeaponEditorFromState() RivalsCosmetics.SyncFavoriteEditorFromState() pcall(RivalsCosmetics.UpdatePreviewViewport) return true end function RivalsCosmetics.IsValidEditorSelection(weaponName, kind, value) if kind == 'WrapInverted' then return type(value) == 'boolean' end value = RivalsCosmetics.NormalizeSelectionValue(value) local values = nil if weaponName == RIVALS_COSMETIC_ALL_WEAPONS and kind == 'Skin' then values = RivalsCosmetics.ResolveSentinelValues() else values = RivalsCosmetics.ResolveSelectionValues(weaponName, kind) end return RivalsCosmetics.ResolveValidDropdownValue(value, values) == value end function RivalsCosmetics.SetSelectionForSelectedEditorWeapon(kind, value) local previewWeaponName = RivalsCosmetics.ResolveSelectedEditorWeaponName() if not previewWeaponName or not RivalsCosmetics.IsValidEditorSelection(previewWeaponName, kind, value) then return false end value = kind == 'WrapInverted' and value == true or RivalsCosmetics.NormalizeSelectionValue(value) if previewWeaponName == RIVALS_COSMETIC_ALL_WEAPONS then for _, weaponName in ipairs(RivalsCosmetics.ResolveWeaponNames()) do RivalsCosmetics.SetStoredSelection(weaponName, kind, value) end elseif not RivalsCosmetics.SetStoredSelection(previewWeaponName, kind, value) then return false end RivalsCosmetics.RestoreAppliedCosmetics() pcall(RivalsCosmetics.UpdatePreviewViewport) RivalsCosmetics.ApplyAll() if type(RivalsCosmetics.QueueCosmeticPresetAutoSave) == 'function' then RivalsCosmetics.QueueCosmeticPresetAutoSave() end if type(RivalsCosmetics.QueueCosmeticConfigAutoSave) == 'function' then RivalsCosmetics.QueueCosmeticConfigAutoSave() end return true end function RivalsCosmetics.RerollRandomSelections() RivalsCosmeticsState.StableRandomSelectionByWeapon = {} RivalsCosmetics.RestoreAppliedCosmetics() pcall(RivalsCosmetics.UpdatePreviewViewport) RivalsCosmetics.ApplyAll() return true end function RivalsCosmetics.BuildZkhHubItemSelection(weaponName) local selection = {} local hasSelection = false local skin = RivalsCosmetics.ResolveSkinSelectionValue(weaponName) local wrap = RivalsCosmetics.ResolveWrapSelectionValue(weaponName) local charm = RivalsCosmetics.ResolveCharmSelectionValue(weaponName) local finisher = RivalsCosmetics.ResolveFinisherSelectionValue(weaponName) if skin ~= nil and skin ~= RIVALS_COSMETIC_UNSELECTED then selection.skin = skin hasSelection = true end if wrap ~= nil and wrap ~= RIVALS_COSMETIC_UNSELECTED then selection.wrap = { name = wrap, inverted = RivalsCosmetics.ResolveWrapInvertedValue(weaponName) == true, } hasSelection = true end if charm ~= nil and charm ~= RIVALS_COSMETIC_UNSELECTED then selection.charm = charm hasSelection = true end if finisher ~= nil and finisher ~= RIVALS_COSMETIC_UNSELECTED then selection.finisher = finisher hasSelection = true end return hasSelection and selection or nil end function RivalsCosmetics.BuildZkhHubCosmeticPayload(cosmeticName, inverted) if cosmeticName == nil or cosmeticName == RIVALS_COSMETIC_NONE then return nil end local payload = { Name = cosmeticName } if inverted ~= nil then payload.Inverted = inverted == true and true or nil end return payload end function RivalsCosmetics.OverrideWeaponData(weaponName, weaponData) if type(weaponData) ~= 'table' then return weaponData end local selection = RivalsCosmetics.BuildZkhHubItemSelection(weaponName) local patchedNaturalCharm = RivalsCosmetics.PatchRankCharmPayload(weaponData.Charm) local hasOnlyUseFavoritesOverride = false for _, cosmeticType in ipairs(RivalsCosmetics.FavoriteKinds) do if RivalsCosmetics.UsesOnlyFavorites(weaponName, cosmeticType) then hasOnlyUseFavoritesOverride = true break end end if selection == nil and patchedNaturalCharm == nil and not hasOnlyUseFavoritesOverride then return weaponData end local overriddenWeaponData = table.clone(weaponData) if selection ~= nil then if selection.skin ~= nil then overriddenWeaponData.Skin = RivalsCosmetics.BuildZkhHubCosmeticPayload(selection.skin) end if selection.wrap ~= nil then overriddenWeaponData.Wrap = RivalsCosmetics.BuildZkhHubCosmeticPayload(selection.wrap.name, selection.wrap.inverted) end if selection.charm ~= nil then overriddenWeaponData.Charm = RivalsCosmetics.BuildZkhHubCosmeticPayload(selection.charm) end if selection.finisher ~= nil then overriddenWeaponData.Finisher = RivalsCosmetics.BuildZkhHubCosmeticPayload(selection.finisher) end end overriddenWeaponData.Charm = RivalsCosmetics.PatchRankCharmPayload(overriddenWeaponData.Charm) or overriddenWeaponData.Charm for _, cosmeticType in ipairs(RivalsCosmetics.FavoriteKinds) do local cosmeticData = overriddenWeaponData[cosmeticType] if RivalsCosmetics.UsesOnlyFavorites(weaponName, cosmeticType) and type(cosmeticData) == 'table' and type(cosmeticData.Name) == 'string' then cosmeticData = table.clone(cosmeticData) cosmeticData.OnlyUseFavorites = true overriddenWeaponData[cosmeticType] = cosmeticData end end overriddenWeaponData.Level = rawget(weaponData, 'Level') overriddenWeaponData.Prestige = rawget(weaponData, 'Prestige') overriddenWeaponData.XP = rawget(weaponData, 'XP') return overriddenWeaponData end function RivalsCosmetics.ResolveItemLibrary() local cached = RivalsCosmetics.Catalog.ItemLibrary if cached then return cached end local modules = ReplicatedStorage:FindFirstChild('Modules') local itemLibraryModule = modules and modules:FindFirstChild('ItemLibrary') if not itemLibraryModule then return nil end local ok, itemLibrary = pcall(require, itemLibraryModule) if not ok or type(itemLibrary) ~= 'table' then return nil end RivalsCosmetics.Catalog.ItemLibrary = itemLibrary return itemLibrary end function RivalsCosmetics.ResolveEquipmentModule() local cached = RivalsCosmetics.Catalog.EquipmentModule if cached then return cached end local playerScripts = RivalsCosmetics.ResolvePlayerScripts() local modules = playerScripts and playerScripts:FindFirstChild('Modules') local userInterface = modules and modules:FindFirstChild('UserInterface') local equipmentModule = userInterface and userInterface:FindFirstChild('Equipment') if not equipmentModule then return nil end local ok, equipment = pcall(require, equipmentModule) if not ok or type(equipment) ~= 'table' then return nil end RivalsCosmetics.Catalog.EquipmentModule = equipment return equipment end function RivalsCosmetics.BuildNativeUnlockedInventory() local cached = RivalsCosmeticsState.NativeUnlockedInventory if type(cached) == 'table' then return cached end local cosmeticLibrary = RivalsCosmetics.ResolveCosmeticLibrary() local itemLibrary = RivalsCosmetics.ResolveItemLibrary() local cosmetics = cosmeticLibrary and cosmeticLibrary.Cosmetics or nil local items = itemLibrary and itemLibrary.Items or nil if type(cosmetics) ~= 'table' or type(items) ~= 'table' then return nil end local allItemNames = {} for itemName in pairs(items) do if type(itemName) == 'string' and itemName ~= '' then allItemNames[itemName] = true end end local inventory = {} for cosmeticName, cosmeticData in pairs(cosmetics) do local cosmeticType = type(cosmeticData) == 'table' and cosmeticData.Type or nil if type(cosmeticName) == 'string' then if cosmeticType == 'Skin' or cosmeticType == 'Emote' then inventory[cosmeticName] = true elseif cosmeticType == 'Wrap' or cosmeticType == 'Charm' or cosmeticType == 'Finisher' then inventory[cosmeticName] = allItemNames end end end RivalsCosmeticsState.NativeUnlockedInventory = inventory return inventory end function RivalsCosmetics.CloneNativeFavorites(value) local cloned = {} if type(value) ~= 'table' then return cloned end for itemName, entries in pairs(value) do if type(itemName) == 'string' and type(entries) == 'table' then local itemFavorites = {} for cosmeticName, enabled in pairs(entries) do if type(cosmeticName) == 'string' and enabled == true then itemFavorites[cosmeticName] = true end end if next(itemFavorites) ~= nil then cloned[itemName] = itemFavorites end end end return cloned end function RivalsCosmetics.SeedNativeFavorites(baseFavorites) if RivalsCosmeticsState.NativeFavoritesSeeded then return end if next(RivalsCosmeticsState.FavoritedCosmeticsByWeapon) == nil then RivalsCosmeticsState.FavoritedCosmeticsByWeapon = RivalsCosmetics.CloneNativeFavorites(baseFavorites) end RivalsCosmeticsState.NativeFavoritesSeeded = true end function RivalsCosmetics.ResolveNativeDataField(fieldName, originalValue) do local extrasValue = RivalsRuntimeBridge.Extras.DataField(fieldName) if extrasValue ~= nil then return extrasValue end end if not RivalsCosmetics.IsEnabled() and not RivalsCosmetics.IsWeaponUnlockEnabled() then return originalValue end if fieldName == 'CosmeticInventory' then return RivalsCosmetics.BuildNativeUnlockedInventory() or originalValue end if fieldName == 'FavoritedCosmetics' then RivalsCosmetics.SeedNativeFavorites(originalValue) return RivalsCosmeticsState.FavoritedCosmeticsByWeapon end return originalValue end function RivalsCosmetics.EnsureNativeDataView() if not RivalsCosmetics.IsEnabled() then return false end return type(RivalsCosmetics.BuildNativeUnlockedInventory()) == 'table' end function RivalsCosmetics.TriggerNativeDataChanged(fieldName) local playerDataController = RivalsCosmetics.ResolvePlayerDataController() local currentData = playerDataController and playerDataController.CurrentData or nil if type(currentData) ~= 'table' then return false end local events = rawget(currentData, '_value_changed_events') local signal = type(events) == 'table' and events[fieldName] or nil if type(signal) == 'table' and type(signal.Fire) == 'function' then return pcall(signal.Fire, signal) end return false end function RivalsCosmetics.RefreshNativeCosmeticsScene() RivalsCosmetics.TriggerNativeDataChanged('CosmeticInventory') RivalsCosmetics.TriggerNativeDataChanged('FavoritedCosmetics') local equipment = RivalsCosmetics.ResolveEquipmentModule() local interface = equipment and equipment.Interface or nil local customize = interface and interface.Customize or nil local cosmetics = customize and customize.Cosmetics or nil if type(cosmetics) ~= 'table' then return false end local onStateChanged = cosmetics.OnStateChanged if type(onStateChanged) ~= 'function' then return false end task.spawn(function() local previousIdentity = type(getthreadidentity) == 'function' and getthreadidentity() or nil if type(setthreadidentity) == 'function' then pcall(setthreadidentity, 2) end pcall(onStateChanged, cosmetics) if previousIdentity ~= nil and type(setthreadidentity) == 'function' then pcall(setthreadidentity, previousIdentity) end end) return true end function RivalsCosmetics.ReadEquipmentStateValue(fieldName) local equipment = RivalsCosmetics.ResolveEquipmentModule() local equipmentState = equipment and equipment.EquipmentState or nil if type(equipmentState) ~= 'table' then return nil end local ok, value = pcall(function() return equipmentState[fieldName] end) return ok and value or nil end function RivalsCosmetics.NormalizeNativeStateName(value) if type(value) == 'string' then return value end if type(value) == 'table' and type(value.Name) == 'string' then return value.Name end if typeof(value) == 'EnumItem' then return value.Name end return nil end function RivalsCosmetics.ResolveNativeFavoriteName(cosmeticName, cosmeticType) local nativeName = cosmeticName if cosmeticName == RIVALS_COSMETIC_NONE then nativeName = 'NONE_COSMETIC' elseif cosmeticName == RIVALS_COSMETIC_RANDOM then nativeName = 'RANDOM_COSMETIC' end local cosmeticLibrary = RivalsCosmetics.ResolveCosmeticLibrary() if type(cosmeticLibrary) == 'table' and type(cosmeticLibrary.GetNoneSpecificCosmeticName) == 'function' then local ok, resolved = pcall(cosmeticLibrary.GetNoneSpecificCosmeticName, cosmeticLibrary, nativeName, cosmeticType) if ok and type(resolved) == 'string' and resolved ~= '' then return resolved end end return nativeName end function RivalsCosmetics.QueueNativeStateSave() RivalsCosmeticsState.NativeSaveToken = RivalsCosmeticsState.NativeSaveToken + 1 local saveToken = RivalsCosmeticsState.NativeSaveToken task.delay(0.2, function() if RivalsCosmeticsState.NativeSaveToken == saveToken then pcall(RivalsCosmetics.SaveSelectionsToConfig) pcall(RivalsCosmetics.SaveCosmeticsCache) end end) end do local _configSaveToken = 0 function RivalsCosmetics.QueueCosmeticConfigAutoSave() if RivalsCosmeticsState.ApplyingCosmeticPreset then return end _configSaveToken = _configSaveToken + 1 local token = _configSaveToken task.delay(0.4, function() if token ~= _configSaveToken then return end pcall(RivalsCosmetics.SaveCosmeticsCache) pcall(RivalsCosmetics.SaveSelectionsToConfig) end) end end function RivalsCosmetics.CaptureNativeSelection(toggleFavorite, toggleOnlyUseFavorites, invertedOverride) if not RivalsCosmetics.IsEnabled() then return false end local weaponName = RivalsCosmetics.NormalizeNativeStateName(RivalsCosmetics.ReadEquipmentStateValue('SelectedWeapon')) local cosmeticType = RivalsCosmetics.NormalizeNativeStateName(RivalsCosmetics.ReadEquipmentStateValue('CustomizingType')) local cosmeticName = RivalsCosmetics.NormalizeNativeStateName(RivalsCosmetics.ReadEquipmentStateValue('SelectedCosmetic')) if type(weaponName) ~= 'string' or not RivalsCosmetics.IsFavoriteKind(cosmeticType) then return false end if type(cosmeticName) ~= 'string' or cosmeticName == '' or cosmeticName == 'NONE_COSMETIC' then cosmeticName = RIVALS_COSMETIC_NONE elseif cosmeticName == 'RANDOM_COSMETIC' then cosmeticName = RIVALS_COSMETIC_RANDOM end local changed = false if toggleFavorite then local favoriteName = RivalsCosmetics.ResolveNativeFavoriteName(cosmeticName, cosmeticType) if cosmeticName == RIVALS_COSMETIC_NONE then favoriteName = 'NONE_COSMETIC_' .. cosmeticType end changed = RivalsCosmetics.SetFavorited( weaponName, favoriteName, not RivalsCosmetics.IsFavorited(weaponName, favoriteName) ) or changed else if toggleOnlyUseFavorites then changed = RivalsCosmetics.SetOnlyUseFavorites( weaponName, cosmeticType, not RivalsCosmetics.UsesOnlyFavorites(weaponName, cosmeticType) ) or changed end if RivalsCosmetics.ResolveStoredSelectionValue(weaponName, cosmeticType) ~= cosmeticName then changed = RivalsCosmetics.SetStoredSelection(weaponName, cosmeticType, cosmeticName) or changed end if cosmeticType == 'Wrap' then local inverted = invertedOverride if inverted == nil then inverted = RivalsCosmeticsState.NativeInvertedValue end inverted = inverted == true if RivalsCosmetics.ResolveStoredSelectionValue(weaponName, 'WrapInverted') ~= inverted then changed = RivalsCosmetics.SetStoredSelection(weaponName, 'WrapInverted', inverted) or changed end end end if not changed then return false end RivalsCosmetics.TriggerNativeDataChanged('WeaponInventory') RivalsCosmetics.TriggerNativeDataChanged('WeaponData') pcall(RivalsCosmetics.ReloadWeaponViewModel, weaponName) pcall(RivalsCosmetics.ApplyWraps) pcall(RivalsCosmetics.RefreshNativeCosmeticsScene) pcall(RivalsCosmetics.RefreshWeaponCardIcons) task.delay(0.15, function() pcall(RivalsCosmetics.RefreshWeaponCardIcons) end) RivalsCosmetics.QueueNativeStateSave() return true end function RivalsCosmetics.EnsureNativeInvertedState() if RivalsCosmeticsState.NativeEquipmentStateRestore ~= nil then return true end local equipment = RivalsCosmetics.ResolveEquipmentModule() local equipmentState = type(equipment) == 'table' and rawget(equipment, 'EquipmentState') or nil local originalMetatable = type(equipmentState) == 'table' and getmetatable(equipmentState) or nil local originalIndex = type(originalMetatable) == 'table' and rawget(originalMetatable, '__index') or nil if type(equipmentState) ~= 'table' or originalMetatable == nil or originalIndex == nil then return false end local selectedWeapon = rawget(equipmentState, 'SelectedWeapon') if type(selectedWeapon) == 'string' and selectedWeapon ~= '' then RivalsCosmeticsState.NativeInvertedValue = RivalsCosmetics.ResolveWrapInvertedValue(selectedWeapon) == true end rawset(equipmentState, 'CosmeticInverted', nil) setmetatable(equipmentState, { __index = function(_, key) if key == 'CosmeticInverted' then local weaponName = rawget(equipmentState, 'SelectedWeapon') if type(weaponName) == 'string' and weaponName ~= '' then RivalsCosmeticsState.NativeInvertedValue = RivalsCosmetics.ResolveWrapInvertedValue(weaponName) == true return RivalsCosmeticsState.NativeInvertedValue end end if type(originalIndex) == 'table' then return rawget(originalIndex, key) end return originalIndex(equipmentState, key) end, __newindex = function(target, key, value) if key ~= 'CosmeticInverted' or value == nil then rawset(target, key, value) return end RivalsCosmeticsState.NativeInvertedValue = value == true RivalsCosmetics.CaptureNativeSelection(false, false, RivalsCosmeticsState.NativeInvertedValue) end, }) RivalsCosmeticsState.NativeEquipmentStateRestore = { EquipmentState = equipmentState, Metatable = originalMetatable, } return true end function RivalsCosmetics.EnsureNativeCosmeticSlotBinding() if RivalsCosmeticsState.NativeCosmeticSlotConstructor ~= nil then return true end local cosmeticSlot = RivalsCosmetics.ResolveCosmeticSlot() local constructor = type(cosmeticSlot) == 'table' and rawget(cosmeticSlot, 'new') or nil if type(constructor) ~= 'function' or type(debug.getupvalue) ~= 'function' or type(debug.setupvalue) ~= 'function' then return false end local prototype = debug.getupvalue(constructor, 1) if type(prototype) ~= 'table' then return false end local proxy = {} proxy.__index = function(instance, key) if key ~= 'Frame' then return rawget(prototype, key) end if type(debug.info) == 'function' and type(debug.getupvalues) == 'function' then local source, caller = debug.info(2, 'sf') local upvalues = type(caller) == 'function' and debug.getupvalues(caller) or nil if type(source) == 'string' and source:match('Cosmetics$') and type(upvalues) == 'table' and type(upvalues[9]) == 'boolean' then debug.setupvalue(caller, 9, false) end end return rawget(instance, RIVALS_COSMETIC_SLOT_FRAME_SENTINEL) end proxy.__newindex = function(instance, key, value) if key ~= 'Frame' then rawset(instance, key, value) return end local source = type(debug.info) == 'function' and debug.info(3, 's') or nil if type(source) == 'string' and source:match('Cosmetics$') and rawget(instance, 'IsLocked') ~= true and value ~= nil then local button = value:FindFirstChild('Button') if button ~= nil then button.MouseButton1Click:Connect(function() RivalsCosmetics.CaptureNativeSelection(false, false) end) end end rawset(instance, RIVALS_COSMETIC_SLOT_FRAME_SENTINEL, value) end debug.setupvalue(constructor, 1, proxy) RivalsCosmeticsState.NativeCosmeticSlotConstructor = constructor RivalsCosmeticsState.NativeCosmeticSlotPrototype = prototype return true end function RivalsCosmetics.ResolveNativeOptionButton(optionName) local equipment = RivalsCosmetics.ResolveEquipmentModule() local interface = type(equipment) == 'table' and rawget(equipment, 'Interface') or nil local customize = type(interface) == 'table' and rawget(interface, 'Customize') or nil local options = type(customize) == 'table' and rawget(customize, 'Options') or nil local container = type(options) == 'table' and rawget(options, 'Container') or nil local option = container and container:FindFirstChild(optionName) or nil local inner = option and option:FindFirstChild('Container') or nil return inner and inner:FindFirstChild('Button') or nil end function RivalsCosmetics.EnsureNativeUiBindings() pcall(RivalsCosmetics.EnsureWeaponCardSkinHook) if RivalsCosmeticsState.NativeUiBound then return true end if not RivalsCosmetics.EnsureNativeCosmeticSlotBinding() or not RivalsCosmetics.EnsureNativeInvertedState() then RivalsCosmetics.RestoreNativeUiBindings() return false end local favoritedButton = RivalsCosmetics.ResolveNativeOptionButton('Favorited') local onlyUseFavoritesButton = RivalsCosmetics.ResolveNativeOptionButton('OnlyUseFavorites') if favoritedButton ~= nil then RivalsCosmeticsState.NativeFavoriteConnection = favoritedButton.MouseButton1Click:Connect( function() RivalsCosmetics.CaptureNativeSelection(true, false) end ) end if onlyUseFavoritesButton ~= nil then RivalsCosmeticsState.NativeOnlyUseFavoritesConnection = onlyUseFavoritesButton.MouseButton1Click:Connect( function() RivalsCosmetics.CaptureNativeSelection(false, true) end ) end RivalsCosmeticsState.NativeUiBound = true return true end function RivalsCosmetics.RestoreNativeUiBindings() local favoriteConnection = RivalsCosmeticsState.NativeFavoriteConnection if favoriteConnection ~= nil then favoriteConnection:Disconnect() end local onlyUseFavoritesConnection = RivalsCosmeticsState.NativeOnlyUseFavoritesConnection if onlyUseFavoritesConnection ~= nil then onlyUseFavoritesConnection:Disconnect() end local equipmentRestore = RivalsCosmeticsState.NativeEquipmentStateRestore if type(equipmentRestore) == 'table' and type(equipmentRestore.EquipmentState) == 'table' then setmetatable(equipmentRestore.EquipmentState, equipmentRestore.Metatable) end local constructor = RivalsCosmeticsState.NativeCosmeticSlotConstructor local prototype = RivalsCosmeticsState.NativeCosmeticSlotPrototype if type(constructor) == 'function' and type(prototype) == 'table' and type(debug.setupvalue) == 'function' then debug.setupvalue(constructor, 1, prototype) end RivalsCosmeticsState.NativeFavoriteConnection = nil RivalsCosmeticsState.NativeOnlyUseFavoritesConnection = nil RivalsCosmeticsState.NativeEquipmentStateRestore = nil RivalsCosmeticsState.NativeCosmeticSlotConstructor = nil RivalsCosmeticsState.NativeCosmeticSlotPrototype = nil RivalsCosmeticsState.NativeUiBound = false return true end function RivalsCosmetics.ResolveOriginalPlayerDataField(fieldName) local restore = RivalsCosmeticsState.PlayerDataRestore local data = type(restore) == 'table' and restore.Inner or nil if type(data) ~= 'table' then local playerDataController = RivalsCosmetics.ResolvePlayerDataController() local currentData = playerDataController and rawget(playerDataController, 'CurrentData') or nil data = type(currentData) == 'table' and rawget(currentData, 'Data') or nil end return type(data) == 'table' and rawget(data, fieldName) or nil end function RivalsCosmetics.RestorePlayerDataProxy() local restore = RivalsCosmeticsState.PlayerDataRestore if type(restore) ~= 'table' then return true end RivalsCosmeticsState.PlayerDataRestore = nil if type(restore.Current) == 'table' then rawset(restore.Current, 'Data', restore.Inner) end return true end function RivalsCosmetics.IsWeaponUnlockEnabled() local toggle = Toggles and Toggles.P5S1T4 return toggle ~= nil and toggle.Value == true end function RivalsCosmetics.ResolveInnerPlayerDataForWeapons() local restore = RivalsCosmeticsState.PlayerDataRestore if type(restore) == 'table' and type(restore.Inner) == 'table' then return restore.Inner end local controller = RivalsCosmetics.ResolvePlayerDataController() local currentData = controller and rawget(controller, 'CurrentData') or nil if type(currentData) ~= 'table' then return nil end local inner = rawget(currentData, 'Data') return type(inner) == 'table' and inner or nil end function RivalsCosmetics.SyncWeaponInventoryUnlock() local inner = RivalsCosmetics.ResolveInnerPlayerDataForWeapons() if type(inner) ~= 'table' then return false end local realInventory = rawget(inner, 'WeaponInventory') if type(realInventory) ~= 'table' then return false end local originals = RivalsCosmeticsState.WeaponUnlockOriginals if type(originals) ~= 'table' then originals = {} RivalsCosmeticsState.WeaponUnlockOriginals = originals end if not RivalsCosmetics.IsWeaponUnlockEnabled() then for weaponName in pairs(originals) do realInventory[weaponName] = nil end table.clear(originals) return true end local itemLibrary = RivalsCosmetics.ResolveItemLibrary() local items = type(itemLibrary) == 'table' and itemLibrary.Items or nil if type(items) ~= 'table' then return false end for weaponName, weaponDef in pairs(items) do if type(weaponName) == 'string' and weaponName ~= '' and type(weaponDef) == 'table' and type(weaponDef.Class) == 'string' and weaponDef.Class ~= '' then if not realInventory[weaponName] and not originals[weaponName] then originals[weaponName] = true realInventory[weaponName] = { Name = weaponName, Owned = true, Class = weaponDef.Class, Unlocked = true, Level = 1, XP = 0, Prestige = 0, IsFavorited = false, } end end end return true end function RivalsCosmetics.BuildUnlockedWeaponInventory(originalValue) if type(originalValue) ~= 'table' then return originalValue end local cache = RivalsCosmeticsState.WeaponInventoryUnlockCache local cached = cache[originalValue] if cached ~= nil then return cached end local merged = {} for weaponName, weaponData in pairs(originalValue) do merged[weaponName] = weaponData end local itemLibrary = RivalsCosmetics.ResolveItemLibrary() local items = type(itemLibrary) == 'table' and itemLibrary.Items or nil if type(items) == 'table' then for weaponName, weaponDef in pairs(items) do if type(weaponName) == 'string' and weaponName ~= '' and type(weaponDef) == 'table' and type(weaponDef.Class) == 'string' and weaponDef.Class ~= '' and not merged[weaponName] then merged[weaponName] = { Name = weaponName, Owned = true, Class = weaponDef.Class, Unlocked = true, Level = 1, XP = 0, Prestige = 0, IsFavorited = false, } end end end cache[originalValue] = merged return merged end function RivalsCosmetics.LoadPlayerDataProxy(currentData) if type(currentData) ~= 'table' then return false end local restore = RivalsCosmeticsState.PlayerDataRestore if type(restore) == 'table' then if restore.Current == currentData then return true end RivalsCosmetics.RestorePlayerDataProxy() end local inner = rawget(currentData, 'Data') if type(inner) ~= 'table' then return false end local proxy = setmetatable({}, { __index = function(_, fieldName) return RivalsCosmetics.ResolveNativeDataField(fieldName, rawget(inner, fieldName)) end, __newindex = function(_, fieldName, value) rawset(inner, fieldName, value) end, }) rawset(currentData, 'Data', proxy) RivalsCosmeticsState.PlayerDataRestore = { Current = currentData, Inner = inner, Proxy = proxy, } return true end RivalsRuntimeBridge.Extras.CosmeticsRef = RivalsCosmetics function RivalsCosmetics.ResolvePlayerDataGetWeaponDataFunction() local playerDataController = RivalsCosmetics.ResolvePlayerDataController() local metatable = type(playerDataController) == 'table' and getmetatable(playerDataController) or nil local index = type(metatable) == 'table' and rawget(metatable, '__index') or nil return type(index) == 'table' and rawget(index, 'GetWeaponData') or nil end function RivalsCosmetics.FindOriginalWeaponData(weaponName) local inventory = RivalsCosmetics.ResolveOriginalPlayerDataField('WeaponInventory') if type(inventory) ~= 'table' then return nil end for _, weaponData in pairs(inventory) do if type(weaponData) == 'table' and rawget(weaponData, 'Name') == weaponName then return weaponData end end return nil end function RivalsCosmetics.EnsureItemDataHook() if RivalsCosmeticsState.PlayerDataGetWeaponDataConstantIndex ~= nil then return true end local getWeaponData = RivalsCosmetics.ResolvePlayerDataGetWeaponDataFunction() local playerDataUtility = RivalsCosmetics.ResolvePlayerDataUtility() if type(getWeaponData) ~= 'function' or type(playerDataUtility) ~= 'table' or type(debug.getconstants) ~= 'function' or type(debug.setconstant) ~= 'function' then local playerDataController = RivalsCosmetics.ResolvePlayerDataController() local mt = type(playerDataController) == 'table' and getmetatable(playerDataController) or nil local idx = type(mt) == 'table' and rawget(mt, '__index') or nil if type(idx) == 'table' then local original = rawget(idx, 'GetWeaponData') if type(original) == 'function' then rawset(idx, 'GetWeaponData', function(self, weaponName) local ok, data = pcall(original, self, weaponName) if not ok or data == nil then return data end if self ~= playerDataController or not RivalsCosmetics.IsEnabled() then return data end return RivalsCosmetics.OverrideWeaponData(weaponName, data) end) return true end end return false end local constantIndex = nil for index, value in pairs(debug.getconstants(getWeaponData)) do if value == 'GetWeaponData' then constantIndex = index break end end if constantIndex == nil then return false end local previousSentinel = rawget(playerDataUtility, RIVALS_GET_WEAPON_DATA_SENTINEL) debug.setconstant(getWeaponData, constantIndex, RIVALS_GET_WEAPON_DATA_SENTINEL) rawset(playerDataUtility, RIVALS_GET_WEAPON_DATA_SENTINEL, function(_, playerData, weaponName) local originalWeaponData = RivalsCosmetics.FindOriginalWeaponData(weaponName) if originalWeaponData == nil then return nil end local player = type(playerData) == 'table' and rawget(playerData, 'Player') or nil if player == nil and type(playerData) == 'table' then player = rawget(playerData, '_player_object') end if typeof(player) ~= 'Instance' or not player:IsA('Player') then player = LP end if player ~= LP or not RivalsCosmetics.IsEnabled() then return originalWeaponData end return RivalsCosmetics.OverrideWeaponData(weaponName, originalWeaponData) end) RivalsCosmeticsState.PlayerDataUtility = playerDataUtility RivalsCosmeticsState.PlayerDataUtilityGetWeaponData = previousSentinel RivalsCosmeticsState.PlayerDataGetWeaponDataFunction = getWeaponData RivalsCosmeticsState.PlayerDataGetWeaponDataConstantIndex = constantIndex RivalsCosmeticsState.PlayerDataGetWeaponDataConstantValue = 'GetWeaponData' return true end function RivalsCosmetics.RestoreItemDataHook() local getWeaponData = RivalsCosmeticsState.PlayerDataGetWeaponDataFunction local constantIndex = RivalsCosmeticsState.PlayerDataGetWeaponDataConstantIndex local originalConstant = RivalsCosmeticsState.PlayerDataGetWeaponDataConstantValue if type(getWeaponData) == 'function' and type(constantIndex) == 'number' and type(debug.setconstant) == 'function' then pcall(debug.setconstant, getWeaponData, constantIndex, originalConstant) end local playerDataUtility = RivalsCosmeticsState.PlayerDataUtility if type(playerDataUtility) == 'table' then rawset(playerDataUtility, RIVALS_GET_WEAPON_DATA_SENTINEL, RivalsCosmeticsState.PlayerDataUtilityGetWeaponData) end RivalsCosmeticsState.PlayerDataUtility = nil RivalsCosmeticsState.PlayerDataUtilityGetWeaponData = nil RivalsCosmeticsState.PlayerDataGetWeaponDataFunction = nil RivalsCosmeticsState.PlayerDataGetWeaponDataConstantIndex = nil RivalsCosmeticsState.PlayerDataGetWeaponDataConstantValue = nil return true end function RivalsCosmetics.EnsurePlayerDataHook() local playerDataController = RivalsCosmetics.ResolvePlayerDataController() local currentData = type(playerDataController) == 'table' and rawget(playerDataController, 'CurrentData') or nil if type(playerDataController) ~= 'table' or type(currentData) ~= 'table' then return false end RivalsCosmeticsState.PlayerDataController = playerDataController local dataLoaded = RivalsCosmetics.LoadPlayerDataProxy(currentData) if dataLoaded then pcall(RivalsCosmetics.SyncWeaponInventoryUnlock) end local itemHookLoaded = RivalsCosmetics.EnsureItemDataHook() if RivalsCosmeticsState.PlayerDataAddedConnection == nil then local playerDataAdded = rawget(playerDataController, 'PlayerDataAdded') if type(playerDataAdded) == 'table' and type(playerDataAdded.Connect) == 'function' then RivalsCosmeticsState.PlayerDataAddedConnection = playerDataAdded:Connect(function() task.defer(function() local nextCurrentData = rawget(playerDataController, 'CurrentData') RivalsCosmetics.LoadPlayerDataProxy(nextCurrentData) RivalsCosmetics.RefreshNativeCosmeticsScene() end) end) end end RivalsCosmeticsState.PlayerDataHooked = dataLoaded == true return dataLoaded == true and itemHookLoaded == true end function RivalsCosmetics.RestorePlayerDataHook() local playerDataAddedConnection = RivalsCosmeticsState.PlayerDataAddedConnection if playerDataAddedConnection ~= nil then playerDataAddedConnection:Disconnect() end RivalsCosmeticsState.PlayerDataAddedConnection = nil RivalsCosmetics.RestoreItemDataHook() RivalsCosmetics.RestorePlayerDataProxy() RivalsCosmeticsState.PlayerDataController = nil RivalsCosmeticsState.PlayerDataHooked = false return true end function RivalsCosmetics.EncodeGameKey(key) local enumLibrary = ResolveEnumLibrary() local toEnum = type(enumLibrary) == 'table' and rawget(enumLibrary, '_to_enum') or nil local encoded = type(toEnum) == 'table' and rawget(toEnum, key) or nil return encoded ~= nil and encoded or key end function RivalsCosmetics.ApplyZkhHubViewModelSelection(weaponName, viewModelData, selection) if type(viewModelData) ~= 'table' or type(selection) ~= 'table' then return viewModelData end if selection.skin ~= nil then viewModelData[RivalsCosmetics.EncodeGameKey('Name')] = selection.skin == RIVALS_COSMETIC_NONE and weaponName or selection.skin end if selection.wrap ~= nil then local wrapKey = RivalsCosmetics.EncodeGameKey('Wrap') if selection.wrap.name == RIVALS_COSMETIC_NONE then viewModelData[wrapKey] = nil else viewModelData[wrapKey] = { Name = selection.wrap.name, Inverted = selection.wrap.inverted == true, } end end if selection.charm ~= nil then local charmKey = RivalsCosmetics.EncodeGameKey('Charm') if selection.charm == RIVALS_COSMETIC_NONE then viewModelData[charmKey] = nil else local charm = { Name = selection.charm } viewModelData[charmKey] = RivalsCosmetics.PatchRankCharmPayload(charm) or charm end end return viewModelData end function RivalsCosmetics.GetClientItemRestoreData(player, weaponName) local byPlayer = RivalsCosmeticsState.ClientItemRestoreData[player] return type(byPlayer) == 'table' and byPlayer[weaponName] or nil end function RivalsCosmetics.EnsureClientItemRestoreData(player, weaponName, viewModelData) if type(viewModelData) ~= 'table' then return end local byPlayer = RivalsCosmeticsState.ClientItemRestoreData[player] if type(byPlayer) ~= 'table' then byPlayer = {} RivalsCosmeticsState.ClientItemRestoreData[player] = byPlayer end if byPlayer[weaponName] == nil then byPlayer[weaponName] = table.clone(viewModelData) end end function RivalsCosmetics.EnsureClientViewModelHook() if RivalsCosmeticsState.ClientViewModelHooked then return true end local clientItem = RivalsCosmetics.ResolveClientItemClass() local constructor = type(clientItem) == 'table' and rawget(clientItem, 'new') or nil if type(constructor) ~= 'function' or type(debug.getupvalues) ~= 'function' or type(debug.getstack) ~= 'function' or type(debug.setupvalue) ~= 'function' then return false end local prototypeIndex = nil local prototype = nil for index, value in pairs(debug.getupvalues(constructor)) do if type(value) == 'table' and rawget(value, 'CreateSpectatorSound') ~= nil then prototypeIndex = index prototype = value break end end if type(prototypeIndex) ~= 'number' or type(prototype) ~= 'table' then return false end local proxy = {} proxy.__index = function(_, key) local value = rawget(prototype, key) if value == nil then local metatable = getmetatable(prototype) local index = type(metatable) == 'table' and rawget(metatable, '__index') or nil if type(index) == 'table' then value = rawget(index, key) elseif type(index) == 'function' then value = index(prototype, key) end end return value end proxy.__newindex = function(instance, key, value) if key ~= 'Info' then rawset(instance, key, value) return end rawset(instance, 'Info', value) setmetatable(instance, prototype) local stack = debug.getstack(2, 1) local viewModel = type(stack) == 'table' and rawget(stack, 'ViewModel') or nil local clientFighter = rawget(instance, 'ClientFighter') local player = type(clientFighter) == 'table' and rawget(clientFighter, 'Player') or nil local weaponName = rawget(instance, 'Name') local selection = player == LP and RivalsCosmetics.BuildZkhHubItemSelection(weaponName) or nil local dataKey = RivalsCosmetics.EncodeGameKey('Data') local viewModelData = type(viewModel) == 'table' and viewModel[dataKey] or nil if type(selection) ~= 'table' or type(viewModelData) ~= 'table' then return end RivalsCosmetics.EnsureClientItemRestoreData(player, weaponName, viewModelData) RivalsCosmetics.ApplyZkhHubViewModelSelection(weaponName, viewModelData, selection) end debug.setupvalue(constructor, prototypeIndex, proxy) RivalsCosmeticsState.ClientItemConstructor = constructor RivalsCosmeticsState.ClientItemPrototype = prototype RivalsCosmeticsState.ClientItemPrototypeIndex = prototypeIndex RivalsCosmeticsState.ClientViewModelHooked = true return true end function RivalsCosmetics.RestoreClientViewModelHook() local constructor = RivalsCosmeticsState.ClientItemConstructor local prototype = RivalsCosmeticsState.ClientItemPrototype local prototypeIndex = RivalsCosmeticsState.ClientItemPrototypeIndex if type(constructor) == 'function' and type(prototype) == 'table' and type(prototypeIndex) == 'number' and type(debug.setupvalue) == 'function' then debug.setupvalue(constructor, prototypeIndex, prototype) end RivalsCosmeticsState.ClientItemConstructor = nil RivalsCosmeticsState.ClientItemPrototype = nil RivalsCosmeticsState.ClientItemPrototypeIndex = nil RivalsCosmeticsState.ClientViewModelHooked = false return true end function RivalsCosmetics.EnsureWeaponCardSkinHook() if RivalsCosmeticsState.WeaponCardSkinHooked then return true end local itemLibrary = RivalsCosmetics.ResolveItemLibrary() if type(itemLibrary) ~= 'table' then return false end local original = nil pcall(function() original = itemLibrary.GetViewModelImageFromWeaponData end) if type(original) ~= 'function' then return false end local function detour(lib, wdata, hires) if type(wdata) == 'table' and type(wdata.Name) == 'string' then local skinName = nil pcall(function() local raw = RivalsCosmetics.ResolveSkinSelectionValue(wdata.Name) if RivalsCosmetics.IsCosmeticValueSelected(raw) then skinName = raw end end) if skinName ~= nil then local img = nil pcall(function() local info = lib.ViewModels and lib.ViewModels[skinName] if type(info) == 'table' then img = (hires and info.ImageHighResolution) or info.Image end end) if img ~= nil then return img end end end return original(lib, wdata, hires) end local hooked = false if type(hookfunction) == 'function' then local ok2, orig2 = pcall(hookfunction, original, detour) if ok2 and type(orig2) == 'function' and orig2 ~= detour then original = orig2 hooked = true end end if not hooked then pcall(function() if type(setreadonly) == 'function' then setreadonly(itemLibrary, false) end end) local ok3 = pcall(function() itemLibrary.GetViewModelImageFromWeaponData = detour end) hooked = ok3 end if not hooked then return false end RivalsCosmeticsState.WeaponCardSkinHooked = true return true end function RivalsCosmetics.RestoreWeaponCardSkinHook() if not RivalsCosmeticsState.WeaponCardSkinHooked then return end RivalsCosmeticsState.WeaponCardSkinHooked = false if RivalsCosmetics.Catalog then RivalsCosmetics.Catalog.ItemLibrary = nil end end function RivalsCosmetics.EnsureFinisherHook() if RivalsCosmeticsState.ClientEntityFinisherHooked then return true end local clientEntity = RivalsCosmetics.ResolveClientEntityClass() if type(clientEntity) ~= 'table' or type(clientEntity._PlayFinisher) ~= 'function' then return false end local previousOriginalPlayFinisher = rawget(clientEntity, '__ZkxHubOriginalPlayFinisher') if rawget(clientEntity, '__ZkxHubFinisherHooked') == true and type(previousOriginalPlayFinisher) == 'function' then clientEntity._PlayFinisher = previousOriginalPlayFinisher end local originalPlayFinisher = clientEntity._PlayFinisher local wrappedPlayFinisher = function(self, finisherName, ...) local selectedFinisherName = RivalsCosmetics.ResolveSelectedPlaybackFinisherName() if type(selectedFinisherName) == 'string' and selectedFinisherName ~= '' then return originalPlayFinisher(self, selectedFinisherName, ...) end return originalPlayFinisher(self, finisherName, ...) end RivalsCosmeticsState.HookedClientEntityClass = clientEntity RivalsCosmeticsState.OriginalClientEntityPlayFinisher = originalPlayFinisher RivalsCosmeticsState.ClientEntityPlayFinisherWrapper = wrappedPlayFinisher clientEntity.__ZkxHubOriginalPlayFinisher = originalPlayFinisher clientEntity.__ZkxHubFinisherHooked = true clientEntity._PlayFinisher = wrappedPlayFinisher RivalsCosmeticsState.ClientEntityFinisherHooked = true return true end function RivalsCosmetics.RestoreFinisherHook() local clientEntity = RivalsCosmeticsState.HookedClientEntityClass local originalPlayFinisher = RivalsCosmeticsState.OriginalClientEntityPlayFinisher local wrappedPlayFinisher = RivalsCosmeticsState.ClientEntityPlayFinisherWrapper if type(clientEntity) == 'table' and type(originalPlayFinisher) == 'function' then if clientEntity._PlayFinisher == wrappedPlayFinisher or rawget(clientEntity, '__ZkxHubFinisherHooked') == true then clientEntity._PlayFinisher = originalPlayFinisher end clientEntity.__ZkxHubOriginalPlayFinisher = nil clientEntity.__ZkxHubFinisherHooked = nil end RivalsCosmeticsState.HookedClientEntityClass = nil RivalsCosmeticsState.OriginalClientEntityPlayFinisher = nil RivalsCosmeticsState.ClientEntityPlayFinisherWrapper = nil RivalsCosmeticsState.ClientEntityFinisherHooked = false return true end function RivalsCosmetics.EnsureViewModelImageHook() if RivalsCosmeticsState.ViewModelImageHooked then return true end local itemLibrary = RivalsCosmetics.ResolveItemLibrary() if type(itemLibrary) ~= 'table' or type(itemLibrary.GetViewModelImageFromWeaponData) ~= 'function' then return false end local originalGetImage = itemLibrary.GetViewModelImageFromWeaponData local function resolveSkinCardImage(lib, skinName, hires) local image = nil pcall(function() local viewModels = rawget(lib, 'ViewModels') if type(viewModels) ~= 'table' then viewModels = lib.ViewModels end local info = type(viewModels) == 'table' and viewModels[skinName] or nil if type(info) == 'table' then if hires and info.ImageHighResolution ~= nil then image = info.ImageHighResolution else image = info.Image end end end) return image end local wrappedGetImage = function(self, weaponData, hires, ...) local extraArgs = table.pack(...) local skinName = nil if type(weaponData) == 'table' then local weaponName = weaponData.Name if type(weaponName) ~= 'string' or weaponName == '' then weaponName = weaponData[RivalsCosmetics.EncodeGameKey('Name')] end if type(weaponName) == 'string' and weaponName ~= '' then skinName = RivalsCosmetics.ResolveSelectedSkinName(weaponName) end end if type(skinName) == 'string' and skinName ~= '' then local fallback = nil pcall( function() fallback = originalGetImage(self, weaponData, hires, table.unpack(extraArgs, 1, extraArgs.n)) end ) local injected = nil pcall(function() local clone = table.clone(weaponData) local skinData = RivalsCosmetics.BuildCosmeticData(nil, skinName, 'Skin') or { Name = skinName } clone.Skin = skinData clone[RivalsCosmetics.EncodeGameKey('Skin')] = skinData injected = originalGetImage(self, clone, hires, table.unpack(extraArgs, 1, extraArgs.n)) end) local direct = resolveSkinCardImage(self, skinName, hires) if direct ~= nil and direct ~= fallback then return direct end if injected ~= nil and injected ~= fallback then return injected end if direct ~= nil then return direct end if injected ~= nil then return injected end if fallback ~= nil then return fallback end end return originalGetImage(self, weaponData, hires, ...) end RivalsCosmeticsState.HookedImageItemLibrary = itemLibrary RivalsCosmeticsState.OriginalGetViewModelImage = originalGetImage RivalsCosmeticsState.GetViewModelImageWrapper = wrappedGetImage itemLibrary.GetViewModelImageFromWeaponData = wrappedGetImage if type(itemLibrary.GetViewModelImage) == 'function' then local originalPlainGetImage = itemLibrary.GetViewModelImage local wrappedPlainGetImage = function(self, weaponName, ...) local packedRest = table.pack(...) if type(weaponName) == 'string' and weaponName ~= '' then local skinName = nil pcall(function() skinName = RivalsCosmetics.ResolveSelectedSkinName(weaponName) end) if type(skinName) == 'string' and skinName ~= '' and skinName ~= weaponName then local remapped = nil local ok = pcall( function() remapped = originalPlainGetImage(self, skinName, table.unpack(packedRest, 1, packedRest.n)) end ) if ok and remapped ~= nil then return remapped end end end return originalPlainGetImage(self, weaponName, table.unpack(packedRest, 1, packedRest.n)) end RivalsCosmeticsState.OriginalPlainGetViewModelImage = originalPlainGetImage RivalsCosmeticsState.PlainGetViewModelImageWrapper = wrappedPlainGetImage itemLibrary.GetViewModelImage = wrappedPlainGetImage end RivalsCosmeticsState.ViewModelImageHooked = true return true end function RivalsCosmetics.RestoreViewModelImageHook() local itemLibrary = RivalsCosmeticsState.HookedImageItemLibrary local originalGetImage = RivalsCosmeticsState.OriginalGetViewModelImage local wrappedGetImage = RivalsCosmeticsState.GetViewModelImageWrapper if type(itemLibrary) == 'table' and type(originalGetImage) == 'function' then if itemLibrary.GetViewModelImageFromWeaponData == wrappedGetImage then itemLibrary.GetViewModelImageFromWeaponData = originalGetImage end end local originalPlainGetImage = RivalsCosmeticsState.OriginalPlainGetViewModelImage local wrappedPlainGetImage = RivalsCosmeticsState.PlainGetViewModelImageWrapper if type(itemLibrary) == 'table' and type(originalPlainGetImage) == 'function' then if itemLibrary.GetViewModelImage == wrappedPlainGetImage then itemLibrary.GetViewModelImage = originalPlainGetImage end end RivalsCosmeticsState.HookedImageItemLibrary = nil RivalsCosmeticsState.OriginalGetViewModelImage = nil RivalsCosmeticsState.GetViewModelImageWrapper = nil RivalsCosmeticsState.OriginalPlainGetViewModelImage = nil RivalsCosmeticsState.PlainGetViewModelImageWrapper = nil RivalsCosmeticsState.ViewModelImageHooked = false return true end function RivalsCosmetics.RefreshWeaponCardIcons() pcall(function() local itemLibrary = RivalsCosmetics.ResolveItemLibrary() local viewModels = type(itemLibrary) == 'table' and itemLibrary.ViewModels or nil if type(viewModels) ~= 'table' or not LP then return end local playerGui = LP:FindFirstChild('PlayerGui') local mainGui = playerGui and playerGui:FindFirstChild('MainGui') local mainFrame = mainGui and mainGui:FindFirstChild('MainFrame') local fighterInterfaces = mainFrame and mainFrame:FindFirstChild('FighterInterfaces') if not fighterInterfaces then return end local iface = fighterInterfaces:FindFirstChild(LP.Name) if not iface then return end local baseImageToWeapon = {} for _, weaponName in ipairs(RivalsCosmetics.ResolveWeaponNames()) do local vm = viewModels[weaponName] if type(vm) == 'table' then if type(vm.Image) == 'string' then baseImageToWeapon[vm.Image] = { weapon = weaponName, hires = false } end if type(vm.ImageHighResolution) == 'string' then baseImageToWeapon[vm.ImageHighResolution] = { weapon = weaponName, hires = true } end end end for _, label in ipairs(iface:GetDescendants()) do if label:IsA('ImageLabel') then local currentImage = label.Image local weaponName = nil local wantHires = false local parent = label.Parent if label.Name == 'Icon' and parent and type(parent.Name) == 'string' and viewModels[parent.Name] then weaponName = parent.Name wantHires = type(currentImage) == 'string' and viewModels[parent.Name].ImageHighResolution == currentImage elseif type(currentImage) == 'string' and baseImageToWeapon[currentImage] then weaponName = baseImageToWeapon[currentImage].weapon wantHires = baseImageToWeapon[currentImage].hires end if weaponName then local skinName = RivalsCosmetics.ResolveSelectedSkinName(weaponName) if type(skinName) == 'string' and skinName ~= '' and skinName ~= weaponName then local svm = viewModels[skinName] if type(svm) == 'table' then local newImage = (wantHires and svm.ImageHighResolution) or svm.Image if type(newImage) == 'string' and label.Image ~= newImage then label.Image = newImage end end end end end end end) return true end function RivalsCosmetics.QueueRuntimeApplyWhenReady() if RivalsCosmeticsState.RuntimeApplyRetryPending then return false end RivalsCosmeticsState.RuntimeApplyRetryPending = true task.spawn(GuardRivalsCallback('Cosmetics_WaitForReplicatedState', function() while RivalsCosmeticsState.RuntimeApplyRetryPending and RivalsCosmeticsState.CosmeticsUiLoaded == true and (RivalsCosmetics.IsEnabled() or RivalsEmotes.IsEnabled()) and not ReplicatedStateReady do task.wait(0.25) end RivalsCosmeticsState.RuntimeApplyRetryPending = false if RivalsCosmeticsState.CosmeticsUiLoaded == true and (RivalsCosmetics.IsEnabled() or RivalsEmotes.IsEnabled()) and ReplicatedStateReady then RivalsCosmetics.ApplyAll() RivalsEmotes.ApplyAll() end end)) return true end function RivalsCosmetics.ShouldUseCosmeticsRuntime() if RivalsCosmeticsState.CosmeticsUiLoaded ~= true then return false end if not RivalsCosmetics.IsEnabled() and not RivalsCosmetics.IsWeaponUnlockEnabled() then RivalsCosmeticsState.RuntimeApplyRetryPending = false return false end if not ReplicatedStateReady then RivalsCosmetics.QueueRuntimeApplyWhenReady() return false end return true end function RivalsCosmetics.SetConfigName(configName) if type(configName) == 'string' and configName ~= '' then RivalsCosmeticsState.CosmeticsConfigName = configName end end function RivalsEmotes.ResolveEmoteValues() local cachedEmoteValues = RivalsEmotes.Catalog.EmoteValues if cachedEmoteValues then return cachedEmoteValues end local cosmeticLibrary = RivalsCosmetics.ResolveCosmeticLibrary() local cosmetics = cosmeticLibrary and cosmeticLibrary.Cosmetics or nil local values = { DEFAULT_RIVALS_COSMETIC_VALUE } if type(cosmetics) == 'table' then local emoteNames = {} for cosmeticName, cosmeticData in pairs(cosmetics) do if cosmeticData and cosmeticData.Type == 'Emote' then if not RivalsCosmetics.IsPlaceholderCatalogName(cosmeticName) then emoteNames[#emoteNames + 1] = cosmeticName end end end table.sort(emoteNames) for _, emoteName in ipairs(emoteNames) do values[#values + 1] = emoteName end end RivalsEmotes.Catalog.EmoteValues = values return values end function RivalsEmotes.IsCatalogEmoteName(emoteName) if not RivalsCosmetics.IsCosmeticValueSelected(emoteName) then return false end if RivalsCosmetics.IsPlaceholderCatalogName(emoteName) then return false end local cosmeticLibrary = RivalsCosmetics.ResolveCosmeticLibrary() local cosmetics = cosmeticLibrary and cosmeticLibrary.Cosmetics or nil local cosmeticData = type(cosmetics) == 'table' and cosmetics[emoteName] or nil return type(cosmeticData) == 'table' and cosmeticData.Type == 'Emote' end function RivalsEmotes.ResolveEmoteSlotCount() local constants = RivalsEmotes.ResolveConstants() if type(constants) == 'table' then return constants.MAX_EQUIPPABLE_EMOTES or 8 end return 8 end function RivalsEmotes.BuildEquippedEmoteData(emoteName) if not RivalsEmotes.IsCatalogEmoteName(emoteName) then return nil end return RivalsCosmetics.BuildCosmeticData(nil, emoteName, 'Emote') end function RivalsEmotes.BuildUnlockedCosmeticInventory(baseInventory) local unlockedInventory = {} if type(baseInventory) == 'table' then for key, value in pairs(baseInventory) do unlockedInventory[key] = value end end for _, emoteName in ipairs(RivalsEmotes.ResolveEmoteValues()) do if RivalsCosmetics.IsCosmeticValueSelected(emoteName) then unlockedInventory[emoteName] = true end end return unlockedInventory end function RivalsEmotes.ResolveRawCosmeticInventory() return RivalsCosmetics.ResolveOriginalPlayerDataField('CosmeticInventory') end function RivalsEmotes.IsNaturallyOwnedEmote(emoteName) if not RivalsEmotes.IsCatalogEmoteName(emoteName) then return false end local inventory = RivalsEmotes.ResolveRawCosmeticInventory() local cosmeticLibrary = RivalsCosmetics.ResolveCosmeticLibrary() if type(cosmeticLibrary) == 'table' and type(cosmeticLibrary.OwnsCosmetic) == 'function' then local ok, owns = pcall(cosmeticLibrary.OwnsCosmetic, cosmeticLibrary, inventory, emoteName) if ok then return owns == true end end return type(inventory) == 'table' and inventory[emoteName] ~= nil end function RivalsEmotes.ResolveEmoteController() local cachedEmoteController = RivalsEmotes.Catalog.EmoteController if cachedEmoteController then return cachedEmoteController end local playerScripts = RivalsCosmetics.ResolvePlayerScripts() local controllers = playerScripts and playerScripts:FindFirstChild('Controllers') local emoteControllerModule = controllers and controllers:FindFirstChild('EmoteController') if not emoteControllerModule then return nil end local ok, emoteController = pcall(require, emoteControllerModule) if not ok or type(emoteController) ~= 'table' then return nil end RivalsEmotes.Catalog.EmoteController = emoteController return emoteController end function RivalsEmotes.ResolveConstants() local cachedConstants = RivalsEmotes.Catalog.Constants if cachedConstants then return cachedConstants end local modules = ReplicatedStorage:FindFirstChild('Modules') local constantsModule = modules and modules:FindFirstChild('CONSTANTS') if not constantsModule then return nil end local ok, constants = pcall(require, constantsModule) if not ok or type(constants) ~= 'table' then return nil end RivalsEmotes.Catalog.Constants = constants return constants end function RivalsEmotes.ResolveCurrentData() local playerDataController = RivalsCosmetics.ResolvePlayerDataController() return playerDataController and playerDataController.CurrentData or nil end function RivalsEmotes.CloneEquippedEmotes(equippedEmotes) local cloned = {} if type(equippedEmotes) == 'table' then for key, value in pairs(equippedEmotes) do cloned[key] = value end end return cloned end function RivalsEmotes.ResolveEquippedEmotes(currentData) if not currentData then return nil end if type(currentData.Get) == 'function' then local ok, equippedEmotes = pcall(function() return currentData:Get('EquippedEmotes') end) if ok and type(equippedEmotes) == 'table' then return equippedEmotes end end local data = type(currentData.Data) == 'table' and currentData.Data or nil if not data then return nil end if type(data.EquippedEmotes) ~= 'table' then data.EquippedEmotes = {} end return data.EquippedEmotes end function RivalsEmotes.IsValidEquippedEmoteData(data) return type(data) == 'table' and RivalsCosmetics.IsCosmeticValueSelected(data.Name) end function RivalsEmotes.ResolveSlotOptionId(slotIndex) local numericSlot = math.floor(tonumber(slotIndex) or 1) return string.format('P5EMOTE_SLOT%d', numericSlot) end function RivalsEmotes.ResolveSelectedEmoteForSlot(slotIndex) local option = Options[RivalsEmotes.ResolveSlotOptionId(slotIndex)] local emoteName = option and option.Value or nil if RivalsEmotes.IsCatalogEmoteName(emoteName) then return emoteName end return nil end function RivalsEmotes.ApplyConfiguredEmoteSlots() local currentData = RivalsEmotes.ResolveCurrentData() local equippedEmotes = RivalsEmotes.ResolveEquippedEmotes(currentData) if not currentData or type(equippedEmotes) ~= 'table' then return false end local changed = false for slotIndex = 1, RivalsEmotes.ResolveEmoteSlotCount() do local emoteName = RivalsEmotes.ResolveSelectedEmoteForSlot(slotIndex) if emoteName then local slotKey = tostring(slotIndex) local slotData = equippedEmotes[slotKey] if not RivalsEmotes.IsValidEquippedEmoteData(slotData) or slotData.Name ~= emoteName then if not RivalsCosmeticsState.OriginalEquippedEmotes then RivalsCosmeticsState.OriginalEquippedEmotes = RivalsEmotes.CloneEquippedEmotes(equippedEmotes) end equippedEmotes[slotIndex] = nil equippedEmotes[slotKey] = RivalsEmotes.BuildEquippedEmoteData(emoteName) changed = true end end end if changed and type(currentData.Replicate) == 'function' then pcall(function() currentData:Replicate('EquippedEmotes') end) end return changed end function RivalsEmotes.SetEquippedEmote(slotKey, emoteName) local currentData = RivalsEmotes.ResolveCurrentData() local equippedEmotes = RivalsEmotes.ResolveEquippedEmotes(currentData) local numericSlot = tonumber(slotKey) if not currentData or type(equippedEmotes) ~= 'table' or not numericSlot then return false end numericSlot = math.floor(numericSlot) if numericSlot < 1 or numericSlot > RivalsEmotes.ResolveEmoteSlotCount() then return false end if not RivalsCosmeticsState.OriginalEquippedEmotes then RivalsCosmeticsState.OriginalEquippedEmotes = RivalsEmotes.CloneEquippedEmotes(equippedEmotes) end local normalizedSlotKey = tostring(numericSlot) local nextData = nil if RivalsCosmetics.IsCosmeticValueSelected(emoteName) then nextData = RivalsEmotes.BuildEquippedEmoteData(emoteName) end equippedEmotes[numericSlot] = nil equippedEmotes[normalizedSlotKey] = nextData if type(currentData.Replicate) == 'function' then pcall(function() currentData:Replicate('EquippedEmotes') end) end if type(RivalsCosmetics.QueueCosmeticPresetAutoSave) == 'function' then RivalsCosmetics.QueueCosmeticPresetAutoSave() end return true end function RivalsEmotes.RestoreEquippedEmotes() local originalEquippedEmotes = RivalsCosmeticsState.OriginalEquippedEmotes if type(originalEquippedEmotes) ~= 'table' then return false end local currentData = RivalsEmotes.ResolveCurrentData() local equippedEmotes = RivalsEmotes.ResolveEquippedEmotes(currentData) if not currentData or type(equippedEmotes) ~= 'table' then RivalsCosmeticsState.OriginalEquippedEmotes = nil return false end for key in pairs(equippedEmotes) do equippedEmotes[key] = nil end for key, value in pairs(originalEquippedEmotes) do equippedEmotes[key] = value end RivalsCosmeticsState.OriginalEquippedEmotes = nil if type(currentData.Replicate) == 'function' then pcall(function() currentData:Replicate('EquippedEmotes') end) end return true end function RivalsEmotes.ResolveLocalEntity() local fighter = ResolveLocalFighter() return fighter and fighter.Entity or nil end function RivalsEmotes.ShouldUseLocalPlayback(emoteName) return RivalsEmotes.IsEnabled() and RivalsEmotes.IsCatalogEmoteName(emoteName) and not RivalsEmotes.IsNaturallyOwnedEmote(emoteName) end function RivalsEmotes.PlayLocalEmoteByName(emoteName) if not RivalsEmotes.IsCatalogEmoteName(emoteName) then return false end local entity = RivalsEmotes.ResolveLocalEntity() if type(entity) ~= 'table' or type(entity.PlayEmote) ~= 'function' then return false end local emoteData = RivalsEmotes.BuildEquippedEmoteData(emoteName) if type(emoteData) ~= 'table' then return false end local ok = pcall(function() entity:PlayEmote(emoteData) end) return ok == true end function RivalsEmotes.EnsureEmoteControllerHook() if RivalsCosmeticsState.EmoteControllerHooked then return true end local emoteController = RivalsEmotes.ResolveEmoteController() if type(emoteController) ~= 'table' or type(emoteController.EquipEmote) ~= 'function' or type(emoteController.UseEmoteByName) ~= 'function' then return false end local originalEquipEmote = emoteController.EquipEmote local originalUseEmoteByName = emoteController.UseEmoteByName emoteController.EquipEmote = function(self, slotKey, emoteName, ...) if RivalsEmotes.IsEnabled() and not RivalsEmotes.IsNaturallyOwnedEmote(emoteName) then if RivalsEmotes.SetEquippedEmote(slotKey, emoteName) then return end end return originalEquipEmote(self, slotKey, emoteName, ...) end emoteController.UseEmoteByName = function(self, emoteName, ...) if RivalsEmotes.ShouldUseLocalPlayback(emoteName) and RivalsEmotes.PlayLocalEmoteByName(emoteName) then return end return originalUseEmoteByName(self, emoteName, ...) end RivalsCosmeticsState.HookedEmoteController = emoteController RivalsCosmeticsState.OriginalEmoteControllerEquipEmote = originalEquipEmote RivalsCosmeticsState.OriginalEmoteControllerUseEmoteByName = originalUseEmoteByName RivalsCosmeticsState.EmoteControllerHooked = true return true end function RivalsEmotes.RestoreEmoteControllerHook() local emoteController = RivalsCosmeticsState.HookedEmoteController local originalEquipEmote = RivalsCosmeticsState.OriginalEmoteControllerEquipEmote local originalUseEmoteByName = RivalsCosmeticsState.OriginalEmoteControllerUseEmoteByName if type(emoteController) == 'table' and type(originalEquipEmote) == 'function' then emoteController.EquipEmote = originalEquipEmote end if type(emoteController) == 'table' and type(originalUseEmoteByName) == 'function' then emoteController.UseEmoteByName = originalUseEmoteByName end RivalsCosmeticsState.HookedEmoteController = nil RivalsCosmeticsState.OriginalEmoteControllerEquipEmote = nil RivalsCosmeticsState.OriginalEmoteControllerUseEmoteByName = nil RivalsCosmeticsState.EmoteControllerHooked = false return true end function RivalsEmotes.ShouldUseRuntime() if RivalsCosmeticsState.CosmeticsUiLoaded ~= true then return false end if not RivalsEmotes.IsEnabled() then return false end if not ReplicatedStateReady then RivalsCosmetics.QueueRuntimeApplyWhenReady() return false end return true end function RivalsEmotes.ApplyAll() if not RivalsEmotes.ShouldUseRuntime() then return false end pcall(RivalsCosmetics.EnsurePlayerDataHook) pcall(RivalsEmotes.EnsureEmoteControllerHook) pcall(RivalsEmotes.ApplyConfiguredEmoteSlots) return true end function RivalsEmotes.RestoreAll() pcall(RivalsEmotes.RestoreEquippedEmotes) pcall(RivalsEmotes.RestoreEmoteControllerHook) if not RivalsCosmetics.IsEnabled() then pcall(RivalsCosmetics.RestorePlayerDataHook) end return true end function RivalsCosmetics.LoadSavedSelectionsFromConfig() local configPath = ScriptPaths.config_path(RivalsCosmeticsState.CosmeticsConfigName or 'RIVALS Default') if not isfile(configPath) then return false end local readOk, rawConfig = pcall(readfile, configPath) if not readOk or type(rawConfig) ~= 'string' then return false end local decodeOk, decoded = pcall(function() return HttpService:JSONDecode(rawConfig) end) local objects = decodeOk and type(decoded) == 'table' and decoded.objects or nil if type(objects) ~= 'table' then return false end local loadedAny = false local loadedPreviewWeaponName = nil local hasNativeCosmeticState = false for _, object in ipairs(objects) do if object and object.idx == RIVALS_FAVORITES_STATE_OPTION_ID and type(object.value) == 'table' and object.value.nativeVersion == 1 then hasNativeCosmeticState = true break end end RivalsCosmetics.RestoreAppliedCosmetics() RivalsCosmeticsState.SelectedSkinByWeapon = {} RivalsCosmeticsState.SelectedWrapByWeapon = {} RivalsCosmeticsState.SelectedCharmByWeapon = {} RivalsCosmeticsState.SelectedFinisherByWeapon = {} RivalsCosmeticsState.SelectedWrapInvertedByWeapon = {} RivalsCosmeticsState.StableRandomSelectionByWeapon = {} RivalsCosmeticsState.FavoritedCosmeticsByWeapon = {} RivalsCosmeticsState.OnlyUseFavoritesByWeapon = {} RivalsCosmeticsState.NativeFavoritesSeeded = false RivalsCosmeticsState.RankCharmOverridesBySeason = {} RivalsCosmeticsState.RefreshingDropdownValues = RivalsCosmeticsState.RefreshingDropdownValues + 1 for _, object in ipairs(objects) do local optionId = object and object.idx or nil local optionInfo = RivalsCosmetics.ResolveCosmeticOptionInfo(optionId) if optionInfo and hasNativeCosmeticState then loadedPreviewWeaponName = loadedPreviewWeaponName or optionInfo.weaponName if optionInfo.type == 'Toggle' then RivalsCosmetics.SetStoredSelection(optionInfo.weaponName, optionInfo.kind, object.value == true) else RivalsCosmetics.SetStoredSelection(optionInfo.weaponName, optionInfo.kind, object.value) end loadedAny = true elseif optionId == RIVALS_FAVORITES_STATE_OPTION_ID and hasNativeCosmeticState then local favoritesState = RivalsCosmetics.NormalizeFavoritesState(object.value) RivalsCosmeticsState.FavoritedCosmeticsByWeapon = favoritesState.favorites RivalsCosmeticsState.OnlyUseFavoritesByWeapon = favoritesState.onlyUse RivalsCosmeticsState.NativeFavoritesSeeded = type(object.value) == 'table' and object.value.nativeVersion == 1 or next(favoritesState.favorites) ~= nil or next(favoritesState.onlyUse) ~= nil loadedAny = true elseif optionId == RivalsCosmetics.ResolveEditorOptionId('Weapon') then RivalsCosmetics.SetSelectedEditorWeapon(object.value) end end if loadedPreviewWeaponName then RivalsCosmeticsState.SelectedEditorWeaponName = loadedPreviewWeaponName RivalsCosmetics.SetPreviewWeaponName(loadedPreviewWeaponName) end RivalsCosmetics.SyncActiveWeaponEditorFromState() RivalsCosmetics.SyncFavoriteEditorFromState() RivalsCosmetics.SyncRankCharmEditorFromState() RivalsCosmeticsState.RefreshingDropdownValues = math.max(RivalsCosmeticsState.RefreshingDropdownValues - 1, 0) return loadedAny end function RivalsCosmetics.CaptureSavedSelectionsBeforeConfigSave() RivalsCosmeticsState.SavedConfigCosmeticObjects = {} return false end function RivalsCosmetics.ResolveSelectionConfigObjectsById() local objectsById = {} for _, weaponName in ipairs(RivalsCosmetics.ResolveWeaponNames()) do local skinValue = RivalsCosmetics.ResolveStoredSelectionValue(weaponName, 'Skin') if skinValue ~= nil then local skinOptionId = RivalsCosmetics.ResolveSkinOptionId(weaponName) objectsById[skinOptionId] = { idx = skinOptionId, type = 'Dropdown', value = skinValue, } end local wrapValue = RivalsCosmetics.ResolveStoredSelectionValue(weaponName, 'Wrap') if wrapValue ~= nil then local wrapOptionId = RivalsCosmetics.ResolveWrapOptionId(weaponName) objectsById[wrapOptionId] = { idx = wrapOptionId, type = 'Dropdown', value = wrapValue, } end local charmValue = RivalsCosmetics.ResolveStoredSelectionValue(weaponName, 'Charm') if charmValue ~= nil then local charmOptionId = RivalsCosmetics.ResolveCharmOptionId(weaponName) objectsById[charmOptionId] = { idx = charmOptionId, type = 'Dropdown', value = charmValue, } end local finisherValue = RivalsCosmetics.ResolveStoredSelectionValue(weaponName, 'Finisher') if finisherValue ~= nil then local finisherOptionId = RivalsCosmetics.ResolveFinisherOptionId(weaponName) objectsById[finisherOptionId] = { idx = finisherOptionId, type = 'Dropdown', value = finisherValue, } end local invertedValue = RivalsCosmetics.ResolveStoredSelectionValue(weaponName, 'WrapInverted') if invertedValue ~= nil then local invertedOptionId = RivalsCosmetics.ResolveWrapInvertedOptionId(weaponName) objectsById[invertedOptionId] = { idx = invertedOptionId, type = 'Toggle', value = invertedValue == true, } end end local nativeFavoritesState = RivalsCosmetics.ResolveFavoritesState() nativeFavoritesState.nativeVersion = 1 objectsById[RIVALS_FAVORITES_STATE_OPTION_ID] = { idx = RIVALS_FAVORITES_STATE_OPTION_ID, type = 'CosmeticFavorites', value = nativeFavoritesState, } return objectsById end function RivalsCosmetics.SaveSelectionsToConfig() local configPath = ScriptPaths.config_path(RivalsCosmeticsState.CosmeticsConfigName or 'RIVALS Default') if not isfile(configPath) then local created = pcall(function() local folder = configPath:match('^(.*)[/\\][^/\\]+$') if folder and isfolder and not isfolder(folder) and makefolder then makefolder(folder) end writefile(configPath, HttpService:JSONEncode({ objects = {} })) end) if not created or not isfile(configPath) then RivalsCosmeticsState.SavedConfigCosmeticObjects = nil return false end end local readOk, rawConfig = pcall(readfile, configPath) if not readOk or type(rawConfig) ~= 'string' then RivalsCosmeticsState.SavedConfigCosmeticObjects = nil return false end local decodeOk, decoded = pcall(function() return HttpService:JSONDecode(rawConfig) end) if not decodeOk or type(decoded) ~= 'table' then RivalsCosmeticsState.SavedConfigCosmeticObjects = nil return false end local existingObjects = type(decoded.objects) == 'table' and decoded.objects or {} local cosmeticObjectsById = {} for optionId, object in pairs(RivalsCosmeticsState.SavedConfigCosmeticObjects or {}) do cosmeticObjectsById[optionId] = RivalsCosmetics.CloneConfigObject(object) end local selectedObjectsById = RivalsCosmetics.ResolveSelectionConfigObjectsById() for optionId, object in pairs(selectedObjectsById) do cosmeticObjectsById[optionId] = object end local mergedObjects = {} for _, object in ipairs(existingObjects) do local optionId = object and object.idx or nil if not RivalsCosmetics.IsCosmeticOptionId(optionId) then mergedObjects[#mergedObjects + 1] = object end end local cosmeticOptionIds = {} for optionId in pairs(cosmeticObjectsById) do cosmeticOptionIds[#cosmeticOptionIds + 1] = optionId end table.sort(cosmeticOptionIds) for _, optionId in ipairs(cosmeticOptionIds) do local object = cosmeticObjectsById[optionId] if object then mergedObjects[#mergedObjects + 1] = object end end decoded.objects = mergedObjects RivalsCosmeticsState.SavedConfigCosmeticObjects = nil return pcall(function() writefile(configPath, HttpService:JSONEncode(decoded)) end) end local _COSMETIC_CACHE_PATH = 'ZkxHub/CosmCache.json' function RivalsCosmetics.SaveCosmeticsCache() local objectsById = RivalsCosmetics.ResolveSelectionConfigObjectsById() local objects = {} for _, obj in pairs(objectsById) do if type(obj) == 'table' and obj.idx ~= nil then objects[#objects + 1] = obj end end table.sort(objects, function(a, b) return tostring(a.idx) < tostring(b.idx) end) local payload = { version = 1, objects = objects } local ok = pcall(function() if isfolder and not isfolder('ZkxHub') and makefolder then makefolder('ZkxHub') end writefile(_COSMETIC_CACHE_PATH, HttpService:JSONEncode(payload)) end) return ok end function RivalsCosmetics.LoadCosmeticsCache() if not (isfile and isfile(_COSMETIC_CACHE_PATH)) then return false end local readOk, raw = pcall(readfile, _COSMETIC_CACHE_PATH) if not readOk or type(raw) ~= 'string' then return false end local ok, decoded = pcall(function() return HttpService:JSONDecode(raw) end) if not ok or type(decoded) ~= 'table' then return false end local objects = type(decoded.objects) == 'table' and decoded.objects or {} local loadedAny = false RivalsCosmeticsState.RefreshingDropdownValues = RivalsCosmeticsState.RefreshingDropdownValues + 1 for _, object in ipairs(objects) do local optionId = object and object.idx or nil local optionInfo = RivalsCosmetics.ResolveCosmeticOptionInfo(optionId) if optionInfo then if optionInfo.type == 'Toggle' then RivalsCosmetics.SetStoredSelection(optionInfo.weaponName, optionInfo.kind, object.value == true) else RivalsCosmetics.SetStoredSelection(optionInfo.weaponName, optionInfo.kind, object.value) end loadedAny = true end end RivalsCosmeticsState.RefreshingDropdownValues = math.max(RivalsCosmeticsState.RefreshingDropdownValues - 1, 0) return loadedAny end function RivalsCosmetics.CloneSerializable(value, seen) if type(value) ~= 'table' then return value end seen = seen or {} if seen[value] then return nil end local clone = {} seen[value] = clone for key, child in pairs(value) do local clonedChild = RivalsCosmetics.CloneSerializable(child, seen) if clonedChild ~= nil then clone[key] = clonedChild end end return clone end function RivalsCosmetics.ResolveCosmeticPresetSettings(value) value = type(value) == 'table' and value or {} return { selectedName = type(value.selectedName) == 'string' and value.selectedName or '', autoLoad = value.autoLoad == true, autoLoadName = type(value.autoLoadName) == 'string' and value.autoLoadName or '', autoSave = value.autoSave == true, autoSaveName = type(value.autoSaveName) == 'string' and value.autoSaveName or '', } end function RivalsCosmetics.NormalizePresetSelectionTable(value, kind) local normalized = {} if type(value) ~= 'table' then return normalized end for weaponName, selection in pairs(value) do if RivalsCosmetics.IsKnownWeaponName(weaponName) then if kind == 'WrapInverted' then normalized[weaponName] = selection == true else selection = RivalsCosmetics.NormalizeSelectionValue(selection) local values = RivalsCosmetics.ResolveBaseSelectionValues(weaponName, kind) if RivalsCosmetics.ResolveValidDropdownValue(selection, values) == selection then normalized[weaponName] = selection end end end end return normalized end function RivalsCosmetics.ResolveCosmeticPresetEmotes() local emotes = {} for slotIndex = 1, RivalsEmotes.ResolveEmoteSlotCount() do local option = Options[RivalsEmotes.ResolveSlotOptionId(slotIndex)] emotes[tostring(slotIndex)] = option and option.Value or DEFAULT_RIVALS_COSMETIC_VALUE end return emotes end function RivalsCosmetics.NormalizeCosmeticPresetEmotes(value) local normalized = {} local available = RivalsEmotes.ResolveEmoteValues() value = type(value) == 'table' and value or {} for slotIndex = 1, RivalsEmotes.ResolveEmoteSlotCount() do local slotKey = tostring(slotIndex) local emoteName = value[slotKey] or value[slotIndex] or DEFAULT_RIVALS_COSMETIC_VALUE if RivalsCosmetics.ResolveValidDropdownValue(emoteName, available) ~= emoteName then emoteName = DEFAULT_RIVALS_COSMETIC_VALUE end normalized[slotKey] = emoteName end return normalized end function RivalsCosmetics.BuildDefaultCosmeticPresetSnapshot() local emotes = {} for slotIndex = 1, RivalsEmotes.ResolveEmoteSlotCount() do emotes[tostring(slotIndex)] = DEFAULT_RIVALS_COSMETIC_VALUE end return { version = 1, weaponCosmeticsEnabled = false, emotesEnabled = false, selectedWeapon = RIVALS_COSMETIC_ALL_WEAPONS, skins = {}, wraps = {}, charms = {}, finishers = {}, wrapInverted = {}, favorites = { favorites = {}, onlyUse = {} }, rankCharmOverrides = {}, emotes = emotes, } end function RivalsCosmetics.BuildCurrentCosmeticPresetSnapshot() return { version = 1, weaponCosmeticsEnabled = RivalsCosmetics.IsEnabled(), emotesEnabled = RivalsEmotes.IsEnabled(), selectedWeapon = RivalsCosmetics.ResolveSelectedEditorWeaponName(), skins = RivalsCosmetics.CloneSerializable(RivalsCosmeticsState.SelectedSkinByWeapon), wraps = RivalsCosmetics.CloneSerializable(RivalsCosmeticsState.SelectedWrapByWeapon), charms = RivalsCosmetics.CloneSerializable(RivalsCosmeticsState.SelectedCharmByWeapon), finishers = RivalsCosmetics.CloneSerializable(RivalsCosmeticsState.SelectedFinisherByWeapon), wrapInverted = RivalsCosmetics.CloneSerializable(RivalsCosmeticsState.SelectedWrapInvertedByWeapon), favorites = RivalsCosmetics.ResolveFavoritesState(), rankCharmOverrides = RivalsCosmetics.NormalizeRankCharmOverrides( RivalsCosmeticsState.RankCharmOverridesBySeason ), emotes = RivalsCosmetics.ResolveCosmeticPresetEmotes(), } end function RivalsCosmetics.NormalizeCosmeticPresetSnapshot(value) value = type(value) == 'table' and value or {} local favorites = RivalsCosmetics.NormalizeFavoritesState(value.favorites) local selectedWeapon = RivalsCosmetics.IsKnownEditorWeaponName(value.selectedWeapon) and value.selectedWeapon or RIVALS_COSMETIC_ALL_WEAPONS return { version = 1, weaponCosmeticsEnabled = value.weaponCosmeticsEnabled == true, emotesEnabled = value.emotesEnabled == true, selectedWeapon = selectedWeapon, skins = RivalsCosmetics.NormalizePresetSelectionTable(value.skins, 'Skin'), wraps = RivalsCosmetics.NormalizePresetSelectionTable(value.wraps, 'Wrap'), charms = RivalsCosmetics.NormalizePresetSelectionTable(value.charms, 'Charm'), finishers = RivalsCosmetics.NormalizePresetSelectionTable(value.finishers, 'Finisher'), wrapInverted = RivalsCosmetics.NormalizePresetSelectionTable(value.wrapInverted, 'WrapInverted'), favorites = favorites, rankCharmOverrides = RivalsCosmetics.NormalizeRankCharmOverrides(value.rankCharmOverrides), emotes = RivalsCosmetics.NormalizeCosmeticPresetEmotes(value.emotes), } end function RivalsCosmetics.LoadCosmeticPresetStore() if RivalsCosmeticsState.CosmeticPresetsLoaded then return true end RivalsCosmeticsState.CosmeticPresetsLoaded = true RivalsCosmeticsState.CosmeticPresets = {} RivalsCosmeticsState.CosmeticPresetSettings = RivalsCosmetics.ResolveCosmeticPresetSettings(nil) if not isfile(RivalsCosmetics.CosmeticPresetPath) then return true end local readOk, raw = pcall(readfile, RivalsCosmetics.CosmeticPresetPath) local decodeOk, decoded = pcall(function() return HttpService:JSONDecode(raw) end) if not readOk or not decodeOk or type(decoded) ~= 'table' then return false end for name, snapshot in pairs(type(decoded.presets) == 'table' and decoded.presets or {}) do if type(name) == 'string' and name ~= '' and type(snapshot) == 'table' then RivalsCosmeticsState.CosmeticPresets[name] = RivalsCosmetics.NormalizeCosmeticPresetSnapshot(snapshot) end end local settings = RivalsCosmetics.ResolveCosmeticPresetSettings(decoded.settings) for _, key in ipairs({ 'selectedName', 'autoLoadName', 'autoSaveName' }) do if RivalsCosmeticsState.CosmeticPresets[settings[key]] == nil then settings[key] = '' end end RivalsCosmeticsState.CosmeticPresetSettings = settings return true end function RivalsCosmetics.SaveCosmeticPresetStore() RivalsCosmetics.LoadCosmeticPresetStore() local payload = { version = 1, presets = RivalsCosmeticsState.CosmeticPresets, settings = RivalsCosmeticsState.CosmeticPresetSettings, } return pcall(function() if not isfolder('ZkxHub') then makefolder('ZkxHub') end writefile(RivalsCosmetics.CosmeticPresetPath, HttpService:JSONEncode(payload)) end) end function RivalsCosmetics.ResolveCosmeticPresetNames() RivalsCosmetics.LoadCosmeticPresetStore() local names = {} for name in pairs(RivalsCosmeticsState.CosmeticPresets) do names[#names + 1] = name end table.sort(names) return names end function RivalsCosmetics.RefreshCosmeticPresetControls() local names = RivalsCosmetics.ResolveCosmeticPresetNames() local settings = RivalsCosmeticsState.CosmeticPresetSettings RivalsCosmeticsState.RefreshingDropdownValues = RivalsCosmeticsState.RefreshingDropdownValues + 1 for _, optionId in ipairs({ 'P5PRESET_SELECTED', 'P5PRESET_AUTOLOAD_NAME', 'P5PRESET_AUTOSAVE_NAME' }) do local option = Options[optionId] if option and type(option.SetValues) == 'function' then option:SetValues(names) end end local selectedOption = Options.P5PRESET_SELECTED if selectedOption and type(selectedOption.SetValue) == 'function' then selectedOption:SetValue(settings.selectedName ~= '' and settings.selectedName or nil) end local autoLoadNameOption = Options.P5PRESET_AUTOLOAD_NAME if autoLoadNameOption and type(autoLoadNameOption.SetValue) == 'function' then autoLoadNameOption:SetValue(settings.autoLoadName ~= '' and settings.autoLoadName or nil) end local autoSaveNameOption = Options.P5PRESET_AUTOSAVE_NAME if autoSaveNameOption and type(autoSaveNameOption.SetValue) == 'function' then autoSaveNameOption:SetValue(settings.autoSaveName ~= '' and settings.autoSaveName or nil) end local autoLoadToggle = Toggles.P5PRESET_AUTOLOAD if autoLoadToggle and type(autoLoadToggle.SetValue) == 'function' then autoLoadToggle:SetValue(settings.autoLoad) end local autoSaveToggle = Toggles.P5PRESET_AUTOSAVE if autoSaveToggle and type(autoSaveToggle.SetValue) == 'function' then autoSaveToggle:SetValue(settings.autoSave) end RivalsCosmeticsState.RefreshingDropdownValues = math.max(RivalsCosmeticsState.RefreshingDropdownValues - 1, 0) return names end function RivalsCosmetics.SanitizeCosmeticPresetName(value) return type(value) == 'string' and (value:gsub('%s+', '')) or '' end function RivalsCosmetics.CreateCosmeticPreset(name) RivalsCosmetics.LoadCosmeticPresetStore() name = RivalsCosmetics.SanitizeCosmeticPresetName(name) if name == '' or RivalsCosmeticsState.CosmeticPresets[name] ~= nil then return false end local settings = RivalsCosmeticsState.CosmeticPresetSettings RivalsCosmeticsState.CosmeticPresets[name] = RivalsCosmetics.BuildDefaultCosmeticPresetSnapshot() local previousSelected = settings.selectedName settings.selectedName = name if not RivalsCosmetics.SaveCosmeticPresetStore() then RivalsCosmeticsState.CosmeticPresets[name] = nil settings.selectedName = previousSelected return false end RivalsCosmetics.RefreshCosmeticPresetControls() return true end function RivalsCosmetics.SetSelectedCosmeticPreset(name) RivalsCosmetics.LoadCosmeticPresetStore() if type(name) ~= 'string' or RivalsCosmeticsState.CosmeticPresets[name] == nil then return false end local settings = RivalsCosmeticsState.CosmeticPresetSettings local previous = settings.selectedName settings.selectedName = name if not RivalsCosmetics.SaveCosmeticPresetStore() then settings.selectedName = previous RivalsCosmetics.RefreshCosmeticPresetControls() return false end return true end function RivalsCosmetics.SaveSelectedCosmeticPreset() RivalsCosmetics.LoadCosmeticPresetStore() local settings = RivalsCosmeticsState.CosmeticPresetSettings local name = settings.selectedName if RivalsCosmeticsState.CosmeticPresets[name] == nil then return false end local previous = RivalsCosmeticsState.CosmeticPresets[name] RivalsCosmeticsState.CosmeticPresets[name] = RivalsCosmetics.BuildCurrentCosmeticPresetSnapshot() if not RivalsCosmetics.SaveCosmeticPresetStore() then RivalsCosmeticsState.CosmeticPresets[name] = previous return false end return true end function RivalsCosmetics.ApplyCosmeticPresetSnapshot(snapshot) snapshot = RivalsCosmetics.NormalizeCosmeticPresetSnapshot(snapshot) local previousRefreshing = RivalsCosmeticsState.RefreshingDropdownValues RivalsCosmeticsState.ApplyingCosmeticPreset = true local ok, result = xpcall(function() local cosmeticsToggle = Toggles.P5S1T2 local emotesToggle = Toggles.P5S1T3 if cosmeticsToggle and type(cosmeticsToggle.SetValue) == 'function' then cosmeticsToggle:SetValue(false) end if emotesToggle and type(emotesToggle.SetValue) == 'function' then emotesToggle:SetValue(false) end RivalsCosmetics.RestoreAppliedCosmetics() RivalsCosmeticsState.SelectedSkinByWeapon = snapshot.skins RivalsCosmeticsState.SelectedWrapByWeapon = snapshot.wraps RivalsCosmeticsState.SelectedCharmByWeapon = snapshot.charms RivalsCosmeticsState.SelectedFinisherByWeapon = snapshot.finishers RivalsCosmeticsState.SelectedWrapInvertedByWeapon = snapshot.wrapInverted RivalsCosmeticsState.FavoritedCosmeticsByWeapon = snapshot.favorites.favorites RivalsCosmeticsState.OnlyUseFavoritesByWeapon = snapshot.favorites.onlyUse RivalsCosmeticsState.RankCharmOverridesBySeason = snapshot.rankCharmOverrides RivalsCosmeticsState.StableRandomSelectionByWeapon = {} RivalsCosmeticsState.SelectedEditorWeaponName = snapshot.selectedWeapon RivalsCosmeticsState.RefreshingDropdownValues = RivalsCosmeticsState.RefreshingDropdownValues + 1 for slotIndex = 1, RivalsEmotes.ResolveEmoteSlotCount() do local option = Options[RivalsEmotes.ResolveSlotOptionId(slotIndex)] if option and type(option.SetValue) == 'function' then option:SetValue(snapshot.emotes[tostring(slotIndex)]) end end RivalsCosmeticsState.RefreshingDropdownValues = math.max(RivalsCosmeticsState.RefreshingDropdownValues - 1, 0) RivalsCosmetics.SyncActiveWeaponEditorFromState() RivalsCosmetics.SyncFavoriteEditorFromState() RivalsCosmetics.SyncRankCharmEditorFromState() pcall(RivalsCosmetics.UpdatePreviewViewport) if cosmeticsToggle and type(cosmeticsToggle.SetValue) == 'function' then cosmeticsToggle:SetValue(snapshot.weaponCosmeticsEnabled) end if emotesToggle and type(emotesToggle.SetValue) == 'function' then emotesToggle:SetValue(snapshot.emotesEnabled) end RivalsCosmetics.ApplyAll() RivalsEmotes.ApplyAll() return true end, function(err) return debug.traceback(string.format('[Cosmetics_ApplyPreset] %s', tostring(err)), 2) end) RivalsCosmeticsState.ApplyingCosmeticPreset = false RivalsCosmeticsState.RefreshingDropdownValues = previousRefreshing if not ok then error(result, 0) end return result == true end function RivalsCosmetics.LoadSelectedCosmeticPreset() RivalsCosmetics.LoadCosmeticPresetStore() local settings = RivalsCosmeticsState.CosmeticPresetSettings local snapshot = RivalsCosmeticsState.CosmeticPresets[settings.selectedName] if not snapshot then return false end return RivalsCosmetics.ApplyCosmeticPresetSnapshot(snapshot) end function RivalsCosmetics.DeleteSelectedCosmeticPreset() RivalsCosmetics.LoadCosmeticPresetStore() local settings = RivalsCosmeticsState.CosmeticPresetSettings local name = settings.selectedName local snapshot = RivalsCosmeticsState.CosmeticPresets[name] if not snapshot then return false end local previousSettings = RivalsCosmetics.CloneSerializable(settings) RivalsCosmeticsState.CosmeticPresets[name] = nil settings.selectedName = '' if settings.autoLoadName == name then settings.autoLoadName = '' end if settings.autoSaveName == name then settings.autoSaveName = '' end if not RivalsCosmetics.SaveCosmeticPresetStore() then RivalsCosmeticsState.CosmeticPresets[name] = snapshot RivalsCosmeticsState.CosmeticPresetSettings = previousSettings return false end RivalsCosmetics.RefreshCosmeticPresetControls() return true end function RivalsCosmetics.SetCosmeticPresetSetting(key, value) RivalsCosmetics.LoadCosmeticPresetStore() local settings = RivalsCosmeticsState.CosmeticPresetSettings local previous = settings[key] if key == 'autoLoad' or key == 'autoSave' then settings[key] = value == true elseif key == 'autoLoadName' or key == 'autoSaveName' then if type(value) ~= 'string' or RivalsCosmeticsState.CosmeticPresets[value] == nil then return false end settings[key] = value else return false end if not RivalsCosmetics.SaveCosmeticPresetStore() then settings[key] = previous RivalsCosmetics.RefreshCosmeticPresetControls() return false end return true end function RivalsCosmetics.ApplyCosmeticPresetAutoLoad() RivalsCosmetics.LoadCosmeticPresetStore() if RivalsCosmeticsState.CosmeticPresetAutoLoadApplied then return false end RivalsCosmeticsState.CosmeticPresetAutoLoadApplied = true local settings = RivalsCosmeticsState.CosmeticPresetSettings local snapshot = settings.autoLoad and RivalsCosmeticsState.CosmeticPresets[settings.autoLoadName] or nil if not snapshot then return false end settings.selectedName = settings.autoLoadName RivalsCosmetics.RefreshCosmeticPresetControls() return RivalsCosmetics.ApplyCosmeticPresetSnapshot(snapshot) end function RivalsCosmetics.QueueCosmeticPresetAutoSave() if RivalsCosmeticsState.ApplyingCosmeticPreset or RivalsCosmeticsState.CosmeticsUiLoaded ~= true then return false end RivalsCosmeticsState.CosmeticPresetSaveToken = RivalsCosmeticsState.CosmeticPresetSaveToken + 1 local token = RivalsCosmeticsState.CosmeticPresetSaveToken task.defer(GuardRivalsCallback('Cosmetics_PresetAutoSave', function() if token ~= RivalsCosmeticsState.CosmeticPresetSaveToken or RivalsCosmeticsState.ApplyingCosmeticPreset then return end RivalsCosmetics.LoadCosmeticPresetStore() local settings = RivalsCosmeticsState.CosmeticPresetSettings local name = settings.autoSave and settings.autoSaveName or '' if RivalsCosmeticsState.CosmeticPresets[name] == nil then return end local previous = RivalsCosmeticsState.CosmeticPresets[name] RivalsCosmeticsState.CosmeticPresets[name] = RivalsCosmetics.BuildCurrentCosmeticPresetSnapshot() if not RivalsCosmetics.SaveCosmeticPresetStore() then RivalsCosmeticsState.CosmeticPresets[name] = previous end end)) return true end function RivalsCosmetics.ResolveSkinSourceModel(weaponName, skinName) if type(weaponName) ~= 'string' or weaponName == '' or not RivalsCosmetics.IsCosmeticValueSelected(skinName) then return nil end local cacheKey = string.format('SkinSource_%s_%s', weaponName, skinName) local cachedSkinSource = RivalsCosmetics.Catalog[cacheKey] if cachedSkinSource and cachedSkinSource.Parent then return cachedSkinSource end local cosmeticLibrary = RivalsCosmetics.ResolveCosmeticLibrary() local cosmetics = cosmeticLibrary and cosmeticLibrary.Cosmetics or nil local cosmeticData = type(cosmetics) == 'table' and cosmetics[skinName] or nil if not cosmeticData or cosmeticData.Type ~= 'Skin' or cosmeticData.ItemName ~= weaponName then return nil end local viewModelsRoot = RivalsCosmetics.ResolveViewModelsRoot() if not viewModelsRoot then return nil end local folderName = cosmeticData.Folder if type(folderName) == 'string' and folderName ~= '' then local folder = viewModelsRoot:FindFirstChild(folderName) local folderSource = folder and folder:FindFirstChild(skinName, true) if folderSource and RivalsCosmetics.IsModelContainer(folderSource) then RivalsCosmetics.Catalog[cacheKey] = folderSource return folderSource end end local sourceModel = viewModelsRoot:FindFirstChild(skinName, true) if sourceModel and RivalsCosmetics.IsModelContainer(sourceModel) then RivalsCosmetics.Catalog[cacheKey] = sourceModel return sourceModel end return nil end function RivalsCosmetics.ClonePreviewObject(instance, previewName) if not instance then return nil end if instance:IsA('Model') or instance:IsA('BasePart') then local clone = RivalsCosmetics.SafeClone(instance) if not clone then return nil end clone.Name = previewName or clone.Name RivalsCosmetics.AnchorPreviewDescendants(clone) if not RivalsCosmetics.HasRenderableDescendant(clone) then clone:Destroy() return nil end return clone end if instance:IsA('Folder') then local previewModel = Instance.new('Model') previewModel.Name = previewName or instance.Name for _, child in ipairs(instance:GetChildren()) do local childClone = RivalsCosmetics.SafeClone(child) if childClone then childClone.Parent = previewModel end end if #previewModel:GetChildren() > 0 and RivalsCosmetics.HasRenderableDescendant(previewModel) then RivalsCosmetics.AnchorPreviewDescendants(previewModel) return previewModel end previewModel:Destroy() end return nil end function RivalsCosmetics.BuildPreviewModel(weaponName) local previewWeaponName = type(weaponName) == 'string' and weaponName ~= '' and weaponName or RivalsCosmetics.ResolvePreviewWeaponName() local selectedSkinName = previewWeaponName and RivalsCosmetics.ResolveSelectedSkinName(previewWeaponName) or nil local skinSelectionValue = previewWeaponName and RivalsCosmetics.ResolveSkinSelectionValue(previewWeaponName) or nil local previewSource = nil if selectedSkinName then previewSource = RivalsCosmetics.ResolveSkinSourceModel(previewWeaponName, selectedSkinName) elseif skinSelectionValue == RIVALS_COSMETIC_NONE then previewSource = RivalsCosmetics.ResolveDefaultWeaponTemplate(previewWeaponName) end previewSource = previewSource or (previewWeaponName and RivalsCosmetics.ResolveWeaponTemplate(previewWeaponName) or nil) local previewModel = RivalsCosmetics.ClonePreviewObject( previewSource, previewWeaponName and string.format('Preview_%s', previewWeaponName) or 'Preview' ) if not previewModel then previewModel = Instance.new('Part') previewModel.Name = 'PreviewPlaceholder' previewModel.Anchored = true previewModel.CanCollide = false previewModel.Transparency = 1 previewModel.Size = Vector3.new(1, 1, 1) end return previewModel end function RivalsCosmetics.ResolvePreviewViewportModel() local viewportModel = RivalsCosmeticsState.PreviewViewportModel if viewportModel and viewportModel:IsA('Model') then return viewportModel end viewportModel = Instance.new('Model') viewportModel.Name = 'RIVALS_CosmeticsPreview' RivalsCosmeticsState.PreviewViewportModel = viewportModel return viewportModel end function RivalsCosmetics.CopyPreviewModelIntoViewportModel(targetModel, sourceModel) if not targetModel or not targetModel:IsA('Model') or not sourceModel then return false end local stagedModel = Instance.new('Model') stagedModel.Name = sourceModel.Name if sourceModel:IsA('BasePart') then local childClone = RivalsCosmetics.SafeClone(sourceModel) if childClone then childClone.Parent = stagedModel end elseif sourceModel:IsA('Model') or sourceModel:IsA('Folder') then for _, child in ipairs(sourceModel:GetChildren()) do local childClone = RivalsCosmetics.SafeClone(child) if childClone then childClone.Parent = stagedModel end end end RivalsCosmetics.AnchorPreviewDescendants(stagedModel) if not RivalsCosmetics.HasVisibleRenderableDescendant(stagedModel) then stagedModel:Destroy() return false end targetModel.Name = stagedModel.Name for _, child in ipairs(targetModel:GetChildren()) do pcall(function() child:Destroy() end) end for _, child in ipairs(stagedModel:GetChildren()) do child.Parent = targetModel end stagedModel:Destroy() RivalsCosmetics.AnchorPreviewDescendants(targetModel) return true end function RivalsCosmetics.BuildPreviewViewportModel() local viewportModel = RivalsCosmetics.ResolvePreviewViewportModel() local previewModel = RivalsCosmetics.BuildPreviewModel(RivalsCosmetics.ResolvePreviewWeaponName()) if previewModel and RivalsCosmetics.HasVisibleRenderableDescendant(previewModel) then RivalsCosmetics.CopyPreviewModelIntoViewportModel(viewportModel, previewModel) end if previewModel then pcall(function() previewModel:Destroy() end) end return viewportModel end function RivalsCosmetics.ResolveFirstPersonFolder() local viewModels = Workspace:FindFirstChild('ViewModels') return viewModels and viewModels:FindFirstChild('FirstPerson') or nil end function RivalsCosmetics.ResolveFirstPersonModels(weaponName) local models = {} local firstPerson = RivalsCosmetics.ResolveFirstPersonFolder() if not firstPerson then return models end local nameNeedle = type(weaponName) == 'string' and weaponName ~= '' and string.format(' - %s - ', weaponName) or nil for _, child in ipairs(firstPerson:GetChildren()) do if child:IsA('Model') and (not nameNeedle or child.Name:find(nameNeedle, 1, true)) then models[#models + 1] = child end end return models end function RivalsCosmetics.ResolveWrapProperties(model) local originalWrapProperties = RivalsCosmeticsState.OriginalWrapProperties[model] if originalWrapProperties then return originalWrapProperties end local wrapController = RivalsCosmetics.ResolveWrapController() if not wrapController then return nil end originalWrapProperties = wrapController:RecordOriginalWrapProperties(model) RivalsCosmeticsState.OriginalWrapProperties[model] = originalWrapProperties return originalWrapProperties end function RivalsCosmetics.ApplyWrapToModel(model, wrapName, inverted) local wrapController = RivalsCosmetics.ResolveWrapController() if not wrapController or not model then return false end local originalWrapProperties = RivalsCosmetics.ResolveWrapProperties(model) if not originalWrapProperties then return false end if RivalsCosmetics.IsCosmeticValueSelected(wrapName) then wrapController:ApplyWrap(originalWrapProperties, { Name = wrapName, Inverted = inverted == true, }) else wrapController:ResetWrap(originalWrapProperties) end return true end function RivalsCosmetics.ResolveWorldItemOwnerAndName(instance) if not instance then return nil, nil end local attributeOwner = nil local placedByUserId = instance:GetAttribute('PlacedByUserID') if type(placedByUserId) == 'number' then attributeOwner = Players:GetPlayerByUserId(placedByUserId) end local objectId = instance:GetAttribute('ObjectID') if type(objectId) ~= 'string' then return attributeOwner, nil end local fighterController = ResolveFighterController() local objects = fighterController and fighterController.Objects or nil if type(objects) ~= 'table' then return attributeOwner, nil end local ok, objectOwner, itemName = pcall(function() for _, fighter in pairs(objects) do local items = fighter and fighter.Items or nil if type(items) == 'table' then for _, item in pairs(items) do local data = item and item.Data or nil local itemObjectId = data and data.ObjectID or ReadItemValue(item, 'ObjectID') if itemObjectId == objectId then local itemName = (data and data.Name) or item.Name or ReadItemValue(item, 'Name') return fighter.Player, itemName end end end end return nil, nil end) if not ok then return attributeOwner, nil end return attributeOwner or objectOwner, itemName end function RivalsCosmetics.ResolveSpawnedCosmeticAssetFolder(folderName) local playerScripts = LP:FindFirstChild('PlayerScripts') if not playerScripts then return nil end if folderName == 'SmokeClouds' then local modules = playerScripts:FindFirstChild('Modules') return modules and modules:FindFirstChild('SmokeClouds') or nil end local assets = playerScripts:FindFirstChild('Assets') local misc = assets and assets:FindFirstChild('Misc') or nil return misc and misc:FindFirstChild(folderName) or nil end function RivalsCosmetics.HasSpawnedCosmeticAsset(folderName, skinName) if not RivalsCosmetics.IsCosmeticValueSelected(skinName) then return false end local folder = RivalsCosmetics.ResolveSpawnedCosmeticAssetFolder(folderName) return folder ~= nil and folder:FindFirstChild(skinName) ~= nil end function RivalsCosmetics.RefreshSpawnedCosmeticTag(instance, tagName) local collectionService = game:GetService('CollectionService') if not instance or not instance.Parent or not collectionService:HasTag(instance, tagName) then return false end local retagToken = {} RivalsCosmeticsState.SpawnedWorldCosmeticRetagging[instance] = retagToken collectionService:RemoveTag(instance, tagName) local readdDelay = tagName == 'FireHitbox' and 1.8 or 0 local function readdTag() if instance.Parent and not collectionService:HasTag(instance, tagName) then collectionService:AddTag(instance, tagName) end end if readdDelay > 0 then task.delay(readdDelay, readdTag) else readdTag() end task.delay(readdDelay + 0.5, function() if RivalsCosmeticsState.SpawnedWorldCosmeticRetagging[instance] == retagToken then RivalsCosmeticsState.SpawnedWorldCosmeticRetagging[instance] = nil end end) return true end function RivalsCosmetics.CaptureJumpPadOriginalProperty(entry, descendant) if not descendant or entry.OriginalProperties[descendant] ~= nil then return end local properties = nil if descendant:IsA('BasePart') then properties = { Transparency = descendant.Transparency, CastShadow = descendant.CastShadow, } descendant.Transparency = 1 descendant.CastShadow = false elseif descendant:IsA('Decal') or descendant:IsA('Texture') then properties = { Transparency = descendant.Transparency } descendant.Transparency = 1 elseif descendant:IsA('ParticleEmitter') or descendant:IsA('Trail') or descendant:IsA('Beam') then properties = { Enabled = descendant.Enabled } descendant.Enabled = false elseif descendant:IsA('Sound') then properties = { Volume = descendant.Volume } descendant.Volume = 0 end if properties then entry.OriginalProperties[descendant] = properties end end function RivalsCosmetics.RestoreSpawnedWorldCosmetic(instance) local entry = RivalsCosmeticsState.SpawnedWorldCosmeticEntries[instance] if not entry then return end if entry.ChildAdded then pcall(function() entry.ChildAdded:Disconnect() end) end for _, model in ipairs(entry.WrapTargets or {}) do pcall(RivalsCosmetics.ApplyWrapToModel, model, nil) end if entry.Kind == 'FireHitbox' then pcall(function() instance:SetAttribute('ViewModelName', entry.OriginalViewModelName) end) RivalsCosmetics.RefreshSpawnedCosmeticTag(instance, 'FireHitbox') elseif entry.Kind == 'SmokeCloud' then pcall(function() instance.Name = entry.OriginalName end) RivalsCosmetics.RefreshSpawnedCosmeticTag(instance, 'SmokeCloud') elseif entry.Kind == 'JumpPadHitbox' then for child in pairs(entry.CustomChildren or {}) do pcall(function() child:Destroy() end) end for descendant, properties in pairs(entry.OriginalProperties or {}) do pcall(function() for propertyName, value in pairs(properties) do descendant[propertyName] = value end end) end pcall(function() instance:SetAttribute('ViewModelName', entry.OriginalViewModelName) instance:SetAttribute('SoundID', entry.OriginalSoundID) instance:SetAttribute('SoundVolume', entry.OriginalSoundVolume) end) end RivalsCosmeticsState.SpawnedWorldCosmeticEntries[instance] = nil end function RivalsCosmetics.WaitForLocalWorldItem(instance, requireItemName, token) local expiresAt = os.clock() + 3 repeat if not instance.Parent or RivalsCosmeticsState.SpawnedWorldCosmeticApplyTokens[instance] ~= token then return nil end local owner, itemName = RivalsCosmetics.ResolveWorldItemOwnerAndName(instance) if owner then if owner ~= LP then return nil end if not requireItemName or type(itemName) == 'string' then return itemName or true end end task.wait(0.1) until os.clock() >= expiresAt return nil end function RivalsCosmetics.ApplyFireHitboxCosmetic(instance) if not instance or RivalsCosmeticsState.SpawnedWorldCosmeticRetagging[instance] then return end RivalsCosmetics.RestoreSpawnedWorldCosmetic(instance) RivalsCosmeticsState.SpawnedWorldCosmeticApplyTokens[instance] = ( RivalsCosmeticsState.SpawnedWorldCosmeticApplyTokens[instance] or 0 ) + 1 local token = RivalsCosmeticsState.SpawnedWorldCosmeticApplyTokens[instance] task.spawn(GuardRivalsCallback('Cosmetics_ApplyFireHitbox', function() local itemName = RivalsCosmetics.WaitForLocalWorldItem(instance, true, token) if type(itemName) ~= 'string' or not RivalsCosmetics.IsEnabled() then return end local skinName = RivalsCosmetics.ResolveSelectedSkinName(itemName) local wrapName = RivalsCosmetics.ResolveSelectedWrapName(itemName) local hasSkin = RivalsCosmetics.HasSpawnedCosmeticAsset('FireHitboxes', skinName) if not hasSkin and not wrapName then return end local entry = { Kind = 'FireHitbox', OriginalViewModelName = instance:GetAttribute('ViewModelName'), WrapTargets = {}, } RivalsCosmeticsState.SpawnedWorldCosmeticEntries[instance] = entry if hasSkin then instance:SetAttribute('ViewModelName', skinName) end if wrapName then local expectedVisualName = hasSkin and skinName or (entry.OriginalViewModelName or 'Default') local inverted = RivalsCosmetics.ResolveWrapInvertedValue(itemName) entry.ChildAdded = Workspace.ChildAdded:Connect(GuardRivalsCallback('Cosmetics_FireHitboxVisualAdded', function(child) if RivalsCosmeticsState.SpawnedWorldCosmeticEntries[instance] ~= entry or RivalsCosmeticsState.SpawnedWorldCosmeticApplyTokens[instance] ~= token or not child:IsA('Model') or child.Name ~= expectedVisualName then return end local primary = child:FindFirstChild('Primary') local pivotOk, pivot = pcall(child.GetPivot, child) if not primary or not primary:IsA('BasePart') or not pivotOk or (pivot.Position - instance.Position).Magnitude > 0.5 or (primary.Size - instance.Size).Magnitude > 0.01 then return end if RivalsCosmetics.ApplyWrapToModel(child, wrapName, inverted) then entry.WrapTargets[#entry.WrapTargets + 1] = child if entry.ChildAdded then entry.ChildAdded:Disconnect() entry.ChildAdded = nil end end end)) task.delay(2.5, function() if RivalsCosmeticsState.SpawnedWorldCosmeticEntries[instance] == entry and entry.ChildAdded then entry.ChildAdded:Disconnect() entry.ChildAdded = nil end end) end RivalsCosmetics.RefreshSpawnedCosmeticTag(instance, 'FireHitbox') end)) end function RivalsCosmetics.ApplySmokeCloudCosmetic(instance) if not instance or RivalsCosmeticsState.SpawnedWorldCosmeticRetagging[instance] then return end RivalsCosmetics.RestoreSpawnedWorldCosmetic(instance) RivalsCosmeticsState.SpawnedWorldCosmeticApplyTokens[instance] = ( RivalsCosmeticsState.SpawnedWorldCosmeticApplyTokens[instance] or 0 ) + 1 local token = RivalsCosmeticsState.SpawnedWorldCosmeticApplyTokens[instance] local skinName = RivalsCosmetics.ResolveSelectedSkinName('Smoke Grenade') if not RivalsCosmetics.HasSpawnedCosmeticAsset('SmokeClouds', skinName) then return end task.spawn(GuardRivalsCallback('Cosmetics_ApplySmokeCloud', function() if not RivalsCosmetics.WaitForLocalWorldItem(instance, false, token) or not RivalsCosmetics.IsEnabled() then return end local entry = { Kind = 'SmokeCloud', OriginalName = instance.Name, WrapTargets = {}, } RivalsCosmeticsState.SpawnedWorldCosmeticEntries[instance] = entry instance.Name = skinName RivalsCosmetics.RefreshSpawnedCosmeticTag(instance, 'SmokeCloud') end)) end function RivalsCosmetics.ApplyJumpPadCosmetic(instance) if not instance or RivalsCosmeticsState.SpawnedWorldCosmeticRetagging[instance] then return end RivalsCosmetics.RestoreSpawnedWorldCosmetic(instance) RivalsCosmeticsState.SpawnedWorldCosmeticApplyTokens[instance] = ( RivalsCosmeticsState.SpawnedWorldCosmeticApplyTokens[instance] or 0 ) + 1 local token = RivalsCosmeticsState.SpawnedWorldCosmeticApplyTokens[instance] local skinName = RivalsCosmetics.ResolveSelectedSkinName('Jump Pad') local wrapName = RivalsCosmetics.ResolveSelectedWrapName('Jump Pad') local hasSkin = RivalsCosmetics.HasSpawnedCosmeticAsset('JumpPads', skinName) if not hasSkin and not wrapName then return end task.spawn(GuardRivalsCallback('Cosmetics_ApplyJumpPad', function() if not RivalsCosmetics.WaitForLocalWorldItem(instance, false, token) or not RivalsCosmetics.IsEnabled() then return end task.wait(0.35) if not instance.Parent or RivalsCosmeticsState.SpawnedWorldCosmeticApplyTokens[instance] ~= token then return end local entry = { Kind = 'JumpPadHitbox', OriginalViewModelName = instance:GetAttribute('ViewModelName'), OriginalSoundID = instance:GetAttribute('SoundID'), OriginalSoundVolume = instance:GetAttribute('SoundVolume'), OriginalChildren = setmetatable({}, { __mode = 'k' }), OriginalProperties = setmetatable({}, { __mode = 'k' }), CustomChildren = setmetatable({}, { __mode = 'k' }), WrapTargets = {}, CaptureCustomChildren = false, ChildAdded = nil, } RivalsCosmeticsState.SpawnedWorldCosmeticEntries[instance] = entry for _, child in ipairs(instance:GetChildren()) do entry.OriginalChildren[child] = true if child:IsA('Model') then if hasSkin then for _, descendant in ipairs(child:GetDescendants()) do RivalsCosmetics.CaptureJumpPadOriginalProperty(entry, descendant) end elseif wrapName then entry.WrapTargets[#entry.WrapTargets + 1] = child RivalsCosmetics.ApplyWrapToModel( child, wrapName, RivalsCosmetics.ResolveWrapInvertedValue('Jump Pad') ) end elseif hasSkin and child:IsA('Sound') then RivalsCosmetics.CaptureJumpPadOriginalProperty(entry, child) end end if not hasSkin then if #entry.WrapTargets == 0 then RivalsCosmeticsState.SpawnedWorldCosmeticEntries[instance] = nil end return end entry.ChildAdded = instance.ChildAdded:Connect(GuardRivalsCallback('Cosmetics_JumpPadChildAdded', function(child) if not entry.CaptureCustomChildren or entry.OriginalChildren[child] then return end if child:IsA('Model') or child:IsA('Sound') then entry.CustomChildren[child] = true if child:IsA('Model') and wrapName then entry.WrapTargets[#entry.WrapTargets + 1] = child task.defer( RivalsCosmetics.ApplyWrapToModel, child, wrapName, RivalsCosmetics.ResolveWrapInvertedValue('Jump Pad') ) end end end)) entry.CaptureCustomChildren = true instance:SetAttribute('ViewModelName', skinName) RivalsCosmetics.RefreshSpawnedCosmeticTag(instance, 'JumpPadHitbox') task.wait(0.35) entry.CaptureCustomChildren = false end)) end function RivalsCosmetics.BindSpawnedWorldCosmetics() if not Connections or not RivalsCosmetics.IsEnabled() then return end local collectionService = game:GetService('CollectionService') local definitions = { { Tag = 'FireHitbox', Apply = RivalsCosmetics.ApplyFireHitboxCosmetic }, { Tag = 'SmokeCloud', Apply = RivalsCosmetics.ApplySmokeCloudCosmetic }, { Tag = 'JumpPadHitbox', Apply = RivalsCosmetics.ApplyJumpPadCosmetic }, } for _, definition in ipairs(definitions) do local tagName = definition.Tag local apply = definition.Apply Connections:register( 'Cosmetics_' .. tagName .. 'Added', collectionService :GetInstanceAddedSignal(tagName) :Connect(GuardRivalsCallback('Cosmetics_' .. tagName .. 'Added', apply)) ) Connections:register( 'Cosmetics_' .. tagName .. 'Removed', collectionService :GetInstanceRemovedSignal(tagName) :Connect(GuardRivalsCallback('Cosmetics_' .. tagName .. 'Removed', function(instance) if RivalsCosmeticsState.SpawnedWorldCosmeticRetagging[instance] then return end RivalsCosmeticsState.SpawnedWorldCosmeticApplyTokens[instance] = ( RivalsCosmeticsState.SpawnedWorldCosmeticApplyTokens[instance] or 0 ) + 1 RivalsCosmetics.RestoreSpawnedWorldCosmetic(instance) end)) ) for _, instance in ipairs(collectionService:GetTagged(tagName)) do apply(instance) end end end function RivalsCosmetics.RestoreSpawnedWorldCosmetics() if Connections then Connections:disconnect({ 'Cosmetics_FireHitboxAdded', 'Cosmetics_FireHitboxRemoved', 'Cosmetics_SmokeCloudAdded', 'Cosmetics_SmokeCloudRemoved', 'Cosmetics_JumpPadHitboxAdded', 'Cosmetics_JumpPadHitboxRemoved', }) end for instance in pairs(RivalsCosmeticsState.SpawnedWorldCosmeticEntries) do RivalsCosmeticsState.SpawnedWorldCosmeticApplyTokens[instance] = ( RivalsCosmeticsState.SpawnedWorldCosmeticApplyTokens[instance] or 0 ) + 1 RivalsCosmetics.RestoreSpawnedWorldCosmetic(instance) end end function RivalsCosmetics.RestoreWorldTripmine(tripmine) local entry = RivalsCosmeticsState.WorldTripmineEntries[tripmine] if not entry then return end if entry.DescendantAdded then pcall(function() entry.DescendantAdded:Disconnect() end) end if entry.WrapTarget then pcall(RivalsCosmetics.ApplyWrapToModel, entry.WrapTarget, nil) end for descendant, properties in pairs(entry.OriginalProperties) do if descendant then pcall(function() for propertyName, value in pairs(properties) do descendant[propertyName] = value end end) end end if entry.Clone then pcall(function() entry.Clone:Destroy() end) end RivalsCosmeticsState.WorldTripmineEntries[tripmine] = nil end function RivalsCosmetics.HideWorldTripmineDescendant(entry, descendant) if not descendant or descendant == entry.Hitbox or descendant:IsDescendantOf(entry.Hitbox) or (entry.Clone and (descendant == entry.Clone or descendant:IsDescendantOf(entry.Clone))) then return end local properties = nil if descendant:IsA('BasePart') then properties = { Transparency = descendant.Transparency, CastShadow = descendant.CastShadow, } descendant.Transparency = 1 descendant.CastShadow = false elseif descendant:IsA('Decal') or descendant:IsA('Texture') then properties = { Transparency = descendant.Transparency } descendant.Transparency = 1 elseif descendant:IsA('ParticleEmitter') or descendant:IsA('Trail') or descendant:IsA('Beam') then properties = { Enabled = descendant.Enabled } descendant.Enabled = false end if properties and entry.OriginalProperties[descendant] == nil then entry.OriginalProperties[descendant] = properties end end function RivalsCosmetics.ApplyWorldTripmineNow(tripmine, hitbox, skinName, wrapName, inverted) if not tripmine.Parent or tonumber(tripmine:GetAttribute('PlacedByUserID')) ~= LP.UserId or not RivalsCosmetics.IsEnabled() then return false end local entry = { Clone = nil, Hitbox = hitbox, WrapTarget = tripmine, OriginalProperties = setmetatable({}, { __mode = 'k' }), DescendantAdded = nil, } RivalsCosmeticsState.WorldTripmineEntries[tripmine] = entry if skinName then local sourceModel = RivalsCosmetics.ResolveSkinSourceModel('Subspace Tripmine', skinName) local clone = RivalsCosmetics.ClonePreviewObject(sourceModel, '__ZkxHubTripmineSkin') if clone then entry.Clone = clone entry.WrapTarget = clone pcall(function() clone:PivotTo(hitbox.CFrame) end) clone.Parent = tripmine for _, descendant in ipairs(clone:GetDescendants()) do if descendant:IsA('BasePart') then descendant.Anchored = false descendant.CanCollide = false descendant.CanTouch = false descendant.CanQuery = false descendant.Massless = true local weld = Instance.new('WeldConstraint') weld.Part0 = hitbox weld.Part1 = descendant weld.Parent = descendant end end for _, descendant in ipairs(tripmine:GetDescendants()) do RivalsCosmetics.HideWorldTripmineDescendant(entry, descendant) end entry.DescendantAdded = tripmine.DescendantAdded:Connect( GuardRivalsCallback( 'Cosmetics_TripmineDescendantAdded', function(descendant) RivalsCosmetics.HideWorldTripmineDescendant(entry, descendant) end ) ) end end if wrapName then RivalsCosmetics.ApplyWrapToModel(entry.WrapTarget, wrapName, inverted) end return entry.Clone ~= nil or wrapName ~= nil end function RivalsCosmetics.ApplyWorldTripmine(tripmine) if not tripmine then return end RivalsCosmetics.RestoreWorldTripmine(tripmine) RivalsCosmeticsState.WorldTripmineApplyTokens[tripmine] = ( RivalsCosmeticsState.WorldTripmineApplyTokens[tripmine] or 0 ) + 1 local token = RivalsCosmeticsState.WorldTripmineApplyTokens[tripmine] local skinName = RivalsCosmetics.ResolveSelectedSkinName('Subspace Tripmine') local wrapName = RivalsCosmetics.ResolveSelectedWrapName('Subspace Tripmine') if not skinName and not wrapName then return end local inverted = RivalsCosmetics.ResolveWrapInvertedValue('Subspace Tripmine') task.spawn(GuardRivalsCallback('Cosmetics_ApplyWorldTripmine', function() local hitbox = tripmine:WaitForChild('Hitbox', 5) if not hitbox or RivalsCosmeticsState.WorldTripmineApplyTokens[tripmine] ~= token then return end RivalsCosmetics.ApplyWorldTripmineNow(tripmine, hitbox, skinName, wrapName, inverted) end)) end function RivalsCosmetics.BindWorldTripmines() if not Connections or not RivalsCosmetics.IsEnabled() then return end local collectionService = game:GetService('CollectionService') Connections:register( 'Cosmetics_SubspaceTripmineAdded', collectionService :GetInstanceAddedSignal('SubspaceTripmine') :Connect(GuardRivalsCallback('Cosmetics_SubspaceTripmineAdded', RivalsCosmetics.ApplyWorldTripmine)) ) Connections:register( 'Cosmetics_SubspaceTripmineRemoved', collectionService :GetInstanceRemovedSignal('SubspaceTripmine') :Connect(GuardRivalsCallback('Cosmetics_SubspaceTripmineRemoved', function(tripmine) RivalsCosmeticsState.WorldTripmineApplyTokens[tripmine] = ( RivalsCosmeticsState.WorldTripmineApplyTokens[tripmine] or 0 ) + 1 RivalsCosmetics.RestoreWorldTripmine(tripmine) end)) ) for _, tripmine in ipairs(collectionService:GetTagged('SubspaceTripmine')) do RivalsCosmetics.ApplyWorldTripmine(tripmine) end end function RivalsCosmetics.RestoreWorldTripmines() if Connections then Connections:disconnect({ 'Cosmetics_SubspaceTripmineAdded', 'Cosmetics_SubspaceTripmineRemoved', }) end for tripmine in pairs(RivalsCosmeticsState.WorldTripmineEntries) do RivalsCosmeticsState.WorldTripmineApplyTokens[tripmine] = ( RivalsCosmeticsState.WorldTripmineApplyTokens[tripmine] or 0 ) + 1 RivalsCosmetics.RestoreWorldTripmine(tripmine) end end function RivalsCosmetics.ResolveWrapTargetsForWeapon(weaponName) local targets = {} local seenTargets = {} for _, model in ipairs(RivalsCosmetics.ResolveFirstPersonModels(weaponName)) do if seenTargets[model] ~= true then seenTargets[model] = true targets[#targets + 1] = model end end return targets end function RivalsCosmetics.CaptureOriginalItemCosmetics(item) if type(item) ~= 'table' or RivalsCosmeticsState.OriginalInjectedItemCosmetics[item] then return end RivalsCosmeticsState.OriginalInjectedItemCosmetics[item] = { Skin = item.Skin ~= nil and item.Skin or ReadItemValue(item, 'Skin'), Wrap = item.Wrap ~= nil and item.Wrap or ReadItemValue(item, 'Wrap'), Charm = item.Charm ~= nil and item.Charm or ReadItemValue(item, 'Charm'), Finisher = item.Finisher ~= nil and item.Finisher or ReadItemValue(item, 'Finisher'), } end function RivalsCosmetics.ResolveSelectedCosmeticApplyData(weaponName, kind, existingData) local selectionValue = nil if kind == 'Skin' then selectionValue = RivalsCosmetics.ResolveSkinSelectionValue(weaponName) elseif kind == 'Wrap' then selectionValue = RivalsCosmetics.ResolveWrapSelectionValue(weaponName) elseif kind == 'Charm' then selectionValue = RivalsCosmetics.ResolveCharmSelectionValue(weaponName) elseif kind == 'Finisher' then selectionValue = RivalsCosmetics.ResolveFinisherSelectionValue(weaponName) else return nil, false end if RivalsCosmetics.IsCosmeticValueSelected(selectionValue) then local data = RivalsCosmetics.BuildCosmeticData(existingData, selectionValue, kind) if kind == 'Wrap' then data.Inverted = RivalsCosmetics.ResolveWrapInvertedValue(weaponName) == true elseif kind == 'Charm' then data = RivalsCosmetics.PatchRankCharmPayload(data) or data end return data, true end if selectionValue == RIVALS_COSMETIC_NONE then return nil, true end if kind == 'Charm' then local patchedCharm = RivalsCosmetics.PatchRankCharmPayload(existingData) if patchedCharm then return patchedCharm, true end end return nil, false end function RivalsCosmetics.InjectWeaponData(weaponName) local playerDataController = RivalsCosmetics.ResolvePlayerDataController() local currentData = playerDataController and playerDataController.CurrentData or nil if type(weaponName) ~= 'string' or weaponName == '' or not currentData or type(currentData.Get) ~= 'function' then return false end local ok, inventory = pcall(function() return currentData:Get('WeaponInventory') end) if not ok or type(inventory) ~= 'table' then return false end for _, item in pairs(inventory) do if type(item) == 'table' and item.Name == weaponName then local changed = false local skinData, shouldSetSkin = RivalsCosmetics.ResolveSelectedCosmeticApplyData(weaponName, 'Skin', item.Skin) if shouldSetSkin then RivalsCosmetics.CaptureOriginalItemCosmetics(item) item.Skin = skinData changed = true end local wrapData, shouldSetWrap = RivalsCosmetics.ResolveSelectedCosmeticApplyData(weaponName, 'Wrap', item.Wrap) if shouldSetWrap then RivalsCosmetics.CaptureOriginalItemCosmetics(item) item.Wrap = wrapData changed = true end local charmData, shouldSetCharm = RivalsCosmetics.ResolveSelectedCosmeticApplyData(weaponName, 'Charm', item.Charm) if shouldSetCharm then RivalsCosmetics.CaptureOriginalItemCosmetics(item) item.Charm = charmData changed = true end local finisherData, shouldSetFinisher = RivalsCosmetics.ResolveSelectedCosmeticApplyData(weaponName, 'Finisher', item.Finisher) if shouldSetFinisher then RivalsCosmetics.CaptureOriginalItemCosmetics(item) item.Finisher = finisherData changed = true end if changed and type(currentData.Replicate) == 'function' then pcall(function() currentData:Replicate('WeaponInventory') end) end return changed end end return false end function RivalsCosmetics.InjectAllWeaponData() local injected = false for _, weaponName in ipairs(RivalsCosmetics.ResolveWeaponNames()) do injected = RivalsCosmetics.InjectWeaponData(weaponName) or injected end return injected end function RivalsCosmetics.EncodeGameTable(value) local encoded = {} for key, entry in pairs(value) do local encodedKey = type(key) == 'string' and RivalsCosmetics.EncodeGameKey(key) or key encoded[encodedKey] = entry end return encoded end function RivalsCosmetics.ResolveClientItemForWeapon(weaponName) local fighter = ResolveLocalFighter() if type(fighter) ~= 'table' then return nil, nil end local equippedItem = rawget(fighter, 'EquippedItem') if type(equippedItem) == 'table' and rawget(equippedItem, 'Name') == weaponName then return equippedItem, fighter end for _, fieldName in ipairs({ 'Loadout', 'Slots', 'Items', 'EquippedItems' }) do local items = rawget(fighter, fieldName) if type(items) == 'table' then for _, item in pairs(items) do if type(item) == 'table' and rawget(item, 'Name') == weaponName then return item, fighter end end end end return nil, fighter end function RivalsCosmetics.RunAtIdentity2(callback) local previousIdentity = type(getthreadidentity) == 'function' and getthreadidentity() or nil if type(setthreadidentity) == 'function' then setthreadidentity(2) end local results = table.pack(pcall(callback)) if previousIdentity ~= nil and type(setthreadidentity) == 'function' then setthreadidentity(previousIdentity) end if not results[1] then return false, results[2] end return true, unpack(results, 2, results.n) end function RivalsCosmetics.BuildViewModelSelectionSignature(weaponName, selection) if type(selection) ~= 'table' then return weaponName .. '|restore' end local wrap = selection.wrap return table.concat({ weaponName, tostring(selection.skin), tostring(type(wrap) == 'table' and wrap.name or nil), tostring(type(wrap) == 'table' and wrap.inverted == true or false), tostring(selection.charm), }, '|') end function RivalsCosmetics.ReloadWeaponViewModel(weaponName, force, restoreOnly) pcall(RivalsCosmetics.RefreshWeaponCardIcons) local item, fighter = RivalsCosmetics.ResolveClientItemForWeapon(weaponName) local viewModel = type(item) == 'table' and rawget(item, 'ViewModel') or nil local viewModelData = type(viewModel) == 'table' and rawget(viewModel, 'Data') or nil if type(item) ~= 'table' or type(viewModel) ~= 'table' or type(viewModelData) ~= 'table' then return false end local selection = restoreOnly == true and nil or RivalsCosmetics.BuildZkhHubItemSelection(weaponName) local signature = RivalsCosmetics.BuildViewModelSelectionSignature(weaponName, selection) local appliedSelection = RivalsCosmeticsState.ViewModelSelectionSignatureByItem[item] if force ~= true and type(appliedSelection) == 'table' and appliedSelection.Signature == signature and appliedSelection.ViewModel == viewModel then return true end if type(selection) == 'table' then RivalsCosmetics.EnsureClientItemRestoreData(LP, weaponName, viewModelData) end local restoreData = RivalsCosmetics.GetClientItemRestoreData(LP, weaponName) local nextData if type(selection) == 'table' then local baseData = {} baseData[RivalsCosmetics.EncodeGameKey('Name')] = weaponName nextData = RivalsCosmetics.ApplyZkhHubViewModelSelection(weaponName, baseData, selection) else nextData = type(restoreData) == 'table' and table.clone(restoreData) or table.clone(viewModelData) end local clientViewModel = RivalsCosmetics.ResolveClientViewModelClass() local destroy = type(clientViewModel) == 'table' and clientViewModel.Destroy or nil if type(destroy) ~= 'function' then return false end RivalsCosmetics.RunAtIdentity2(function() destroy(viewModel) end) local createViewModel = item._CreateViewModel if type(createViewModel) ~= 'function' then return false end local info = RivalsCosmetics.EncodeGameTable({ Data = RivalsCosmetics.EncodeGameTable(nextData), }) local created, newViewModel = RivalsCosmetics.RunAtIdentity2(function() return createViewModel(item, info) end) if not created or type(newViewModel) ~= 'table' then return false end rawset(item, 'ViewModel', newViewModel) local getArmsData = type(fighter) == 'table' and fighter.GetArmsData or nil local setArmsData = newViewModel.SetArmsData local equip = newViewModel.Equip local armsData = nil if type(getArmsData) == 'function' then armsData = table.pack(getArmsData(fighter)) end RivalsCosmetics.RunAtIdentity2(function() if type(setArmsData) == 'function' and armsData ~= nil then setArmsData(newViewModel, unpack(armsData, 1, armsData.n)) end if type(equip) == 'function' then equip(newViewModel, true) end end) local spring = rawget(newViewModel, '_equip_spring') if type(spring) == 'table' then rawset(spring, '_position0', 0) rawset(spring, '_velocity0', 0) end RivalsCosmeticsState.ViewModelSelectionSignatureByItem[item] = { Signature = signature, ViewModel = newViewModel, } return true end function RivalsCosmetics.RestoreWeaponViewModels() local byWeapon = RivalsCosmeticsState.ClientItemRestoreData[LP] if type(byWeapon) == 'table' then for weaponName in pairs(byWeapon) do RivalsCosmetics.ReloadWeaponViewModel(weaponName, true, true) end end RivalsCosmeticsState.ClientItemRestoreData = {} RivalsCosmeticsState.ViewModelSelectionSignatureByItem = setmetatable({}, { __mode = 'k' }) return true end function RivalsCosmetics.ApplyActiveWeaponCosmetics() local fighter = ResolveLocalFighter() local item = fighter and fighter.EquippedItem or nil local weaponName = item and item.Name or nil if type(weaponName) ~= 'string' or weaponName == '' then return false end return RivalsCosmetics.ReloadWeaponViewModel(weaponName) end function RivalsCosmetics.ApplySelectedWeaponCosmetics() local applied = false for _, weaponName in ipairs(RivalsCosmetics.ResolveWeaponNames()) do if RivalsCosmetics.BuildZkhHubItemSelection(weaponName) ~= nil then applied = RivalsCosmetics.ReloadWeaponViewModel(weaponName) or applied end end return applied end function RivalsCosmetics.RestoreInjectedWeaponData() local restored = false for item, original in pairs(RivalsCosmeticsState.OriginalInjectedItemCosmetics) do if type(item) == 'table' and type(original) == 'table' then item.Skin = original.Skin item.Wrap = original.Wrap item.Charm = original.Charm item.Finisher = original.Finisher if type(item.Set) == 'function' then pcall(item.Set, item, 'Skin', original.Skin) pcall(item.Set, item, 'Wrap', original.Wrap) pcall(item.Set, item, 'Charm', original.Charm) pcall(item.Set, item, 'Finisher', original.Finisher) end local viewModel = rawget(item, 'ViewModel') if type(viewModel) == 'table' then if type(viewModel.SetSkin) == 'function' then pcall(viewModel.SetSkin, viewModel, original.Skin) end if type(viewModel.SetWrap) == 'function' then pcall(viewModel.SetWrap, viewModel, original.Wrap) end if type(viewModel.SetCharm) == 'function' then pcall(viewModel.SetCharm, viewModel, original.Charm) end if type(viewModel._UpdateSkin) == 'function' then pcall(viewModel._UpdateSkin, viewModel) end if type(viewModel._UpdateWrap) == 'function' then pcall(viewModel._UpdateWrap, viewModel) end if type(viewModel._UpdateCharm) == 'function' then pcall(viewModel._UpdateCharm, viewModel) end end restored = true end end RivalsCosmeticsState.OriginalInjectedItemCosmetics = setmetatable({}, { __mode = 'k' }) if restored then local playerDataController = RivalsCosmetics.ResolvePlayerDataController() local currentData = playerDataController and playerDataController.CurrentData or nil if currentData and type(currentData.Replicate) == 'function' then pcall(function() currentData:Replicate('WeaponInventory') end) end end return restored end function RivalsCosmetics.ApplyWraps() for _, weaponName in ipairs(RivalsCosmetics.ResolveWeaponNames()) do local selectedWrapName = RivalsCosmetics.ResolveSelectedWrapName(weaponName) local inverted = RivalsCosmetics.ResolveWrapInvertedValue(weaponName) for _, target in ipairs(RivalsCosmetics.ResolveWrapTargetsForWeapon(weaponName)) do pcall(function() RivalsCosmetics.ApplyWrapToModel(target, selectedWrapName, inverted) end) end end end function RivalsCosmetics.UpdatePreviewViewport() local previewViewport = Options.P5S3V1 if not previewViewport or type(previewViewport.SetObject) ~= 'function' then return false end local previewWeaponName = RivalsCosmetics.ResolvePreviewWeaponName() local previewModel = RivalsCosmetics.BuildPreviewModel(previewWeaponName) if not previewModel then return false end if not RivalsCosmetics.HasVisibleRenderableDescendant(previewModel) then pcall(function() previewModel:Destroy() end) return false end local viewportModel = RivalsCosmetics.ResolvePreviewViewportModel() local copiedPreview = RivalsCosmetics.CopyPreviewModelIntoViewportModel(viewportModel, previewModel) pcall(function() previewModel:Destroy() end) if not copiedPreview then return false end if previewViewport.Object ~= viewportModel then local previousObject = previewViewport.Object local viewportParent = nil pcall(function() viewportParent = previousObject and previousObject.Parent or nil end) if viewportParent and viewportModel.Parent ~= viewportParent then pcall(function() viewportModel.Parent = viewportParent end) end if previousObject and previousObject ~= viewportModel then pcall(function() previousObject.Parent = nil end) end previewViewport.Object = viewportModel end if type(previewViewport.Focus) == 'function' then pcall(function() previewViewport:Focus() end) end return true end function RivalsCosmetics.RefreshDropdownOptions() if RivalsCosmeticsState.CosmeticsUiLoaded ~= true then return false end local ok, result = xpcall( function() local synced = RivalsCosmetics.SyncActiveWeaponEditorFromState() RivalsCosmetics.SyncFavoriteEditorFromState() RivalsCosmetics.SyncRankCharmEditorFromState() RivalsCosmetics.RefreshCosmeticPresetControls() return synced end, function(err) return debug.traceback(string.format('[Cosmetics_RefreshDropdownOptions] %s', tostring(err)), 2) end ) if not ok then error(result, 0) end return result == true end function RivalsCosmetics.BindFirstPersonModels() if RivalsCosmeticsState.BoundFirstPersonModels then return end RivalsCosmeticsState.BoundFirstPersonModels = true task.spawn(GuardRivalsCallback('Cosmetics_BindFirstPersonModels', function() local viewModels = Workspace:WaitForChild('ViewModels') local firstPerson = viewModels:WaitForChild('FirstPerson') Connections:register( 'Cosmetics_FirstPersonChildAdded', firstPerson.ChildAdded:Connect(GuardRivalsCallback('Cosmetics_FirstPersonChildAdded', function(child) if not child or not child:IsA('Model') then return end task.defer(function() RivalsCosmetics.ApplyAll() end) end)) ) if #firstPerson:GetChildren() > 0 then task.defer(function() RivalsCosmetics.ApplyAll() end) end end)) end function RivalsCosmetics.RestoreAppliedCosmetics() RivalsCosmeticsState.PendingApplyToken = RivalsCosmeticsState.PendingApplyToken + 1 RivalsCosmeticsState.RuntimeApplyRetryPending = false RivalsCosmetics.RestoreSpawnedWorldCosmetics() RivalsCosmetics.RestoreWorldTripmines() for model in pairs(RivalsCosmeticsState.OriginalWrapProperties) do RivalsCosmetics.ApplyWrapToModel(model, nil) end RivalsCosmeticsState.OriginalWrapProperties = setmetatable({}, { __mode = 'k' }) if not RivalsEmotes.IsEnabled() then RivalsEmotes.RestoreEquippedEmotes() end RivalsCosmetics.RestoreWeaponViewModels() RivalsCosmetics.RestoreInjectedWeaponData() RivalsCosmeticsState.PendingSkinSelectionByWeapon = {} RivalsCosmeticsState.PendingWrapSelectionByWeapon = {} RivalsCosmeticsState.PendingCharmSelectionByWeapon = {} RivalsCosmeticsState.PendingFinisherSelectionByWeapon = {} RivalsCosmeticsState.WorldTripmineEntries = setmetatable({}, { __mode = 'k' }) RivalsCosmeticsState.WorldTripmineApplyTokens = setmetatable({}, { __mode = 'k' }) RivalsCosmeticsState.SpawnedWorldCosmeticEntries = setmetatable({}, { __mode = 'k' }) RivalsCosmeticsState.SpawnedWorldCosmeticApplyTokens = setmetatable({}, { __mode = 'k' }) RivalsCosmeticsState.SpawnedWorldCosmeticRetagging = setmetatable({}, { __mode = 'k' }) if not RivalsEmotes.IsEnabled() then RivalsCosmetics.RestorePlayerDataHook() RivalsCosmetics.RestoreNativeUiBindings() end local weaponUnlockToggle = Toggles and Toggles.P5S1T4 if weaponUnlockToggle and weaponUnlockToggle.Value then weaponUnlockToggle:SetValue(false) else pcall(RivalsCosmetics.SyncWeaponInventoryUnlock) end RivalsCosmetics.RestoreClientViewModelHook() RivalsCosmetics.RestoreFinisherHook() RivalsCosmetics.RestoreViewModelImageHook() if not RivalsEmotes.IsEnabled() then RivalsEmotes.RestoreEmoteControllerHook() end pcall(RivalsCosmetics.RefreshNativeCosmeticsScene) return true end function RivalsCosmetics.ResetState() RivalsCosmetics.RestoreAppliedCosmetics() RivalsCosmeticsState.WeaponInventoryUnlockCache = setmetatable({}, { __mode = 'k' }) RivalsCosmeticsState.PreviewWeaponName = nil RivalsCosmeticsState.SelectedEditorWeaponName = nil RivalsCosmeticsState.SelectedSkinByWeapon = {} RivalsCosmeticsState.SelectedWrapByWeapon = {} RivalsCosmeticsState.SelectedCharmByWeapon = {} RivalsCosmeticsState.SelectedFinisherByWeapon = {} RivalsCosmeticsState.SelectedWrapInvertedByWeapon = {} RivalsCosmeticsState.StableRandomSelectionByWeapon = {} RivalsCosmeticsState.FavoritedCosmeticsByWeapon = {} RivalsCosmeticsState.OnlyUseFavoritesByWeapon = {} RivalsCosmeticsState.RankCharmOverridesBySeason = {} RivalsCosmeticsState.SelectedRankCharmSeason = nil RivalsCosmeticsState.PendingApplyToken = 0 RivalsCosmeticsState.RuntimeApplyRetryPending = false RivalsCosmeticsState.SavedConfigCosmeticObjects = nil RivalsCosmeticsState.CosmeticPresetsLoaded = false RivalsCosmeticsState.CosmeticPresets = {} RivalsCosmeticsState.CosmeticPresetSettings = {} RivalsCosmeticsState.CosmeticPresetAutoLoadApplied = false RivalsCosmeticsState.CosmeticPresetSaveToken = RivalsCosmeticsState.CosmeticPresetSaveToken + 1 RivalsCosmeticsState.ApplyingCosmeticPreset = false RivalsCosmeticsState.CosmeticsUiLoaded = false RivalsCosmeticsState.CosmeticsUiLoading = false RivalsCosmeticsState.CosmeticsConfigName = 'RIVALS Default' RivalsCosmeticsState.NativeUnlockedInventory = nil RivalsCosmeticsState.NativeFavoritesSeeded = false RivalsCosmeticsState.NativeSaveToken = RivalsCosmeticsState.NativeSaveToken + 1 if RivalsCosmeticsState.PreviewViewportModel then pcall(function() RivalsCosmeticsState.PreviewViewportModel:Destroy() end) end RivalsCosmeticsState.PreviewViewportModel = nil RivalsCosmeticsState.BoundFirstPersonModels = false end function RivalsCosmetics.ApplyAll() if not RivalsCosmetics.ShouldUseCosmeticsRuntime() then return false end pcall(RivalsCosmetics.EnsurePlayerDataHook) pcall(RivalsCosmetics.BuildNativeUnlockedInventory) pcall(RivalsCosmetics.EnsureNativeUiBindings) pcall(RivalsCosmetics.EnsureClientViewModelHook) pcall(RivalsCosmetics.EnsureWeaponCardSkinHook) pcall(RivalsCosmetics.BindFirstPersonModels) pcall(RivalsCosmetics.ApplySelectedWeaponCosmetics) pcall(RivalsCosmetics.ApplyActiveWeaponCosmetics) pcall(RivalsCosmetics.EnsureFinisherHook) pcall(RivalsCosmetics.EnsureViewModelImageHook) pcall(RivalsCosmetics.RefreshWeaponCardIcons) pcall(RivalsEmotes.ApplyAll) pcall(RivalsCosmetics.ApplyWraps) pcall(RivalsCosmetics.BindSpawnedWorldCosmetics) pcall(RivalsCosmetics.BindWorldTripmines) pcall(RivalsCosmetics.UpdatePreviewViewport) return true end function RivalsCosmetics.ApplyPendingSelection(previewWeaponName, skinName, wrapName, charmName, finisherName) local previousSkinName = nil local previousWrapName = nil local previousCharmName = nil local previousFinisherName = nil local hasWeapon = type(previewWeaponName) == 'string' and previewWeaponName ~= '' if hasWeapon then previousSkinName = RivalsCosmeticsState.PendingSkinSelectionByWeapon[previewWeaponName] previousWrapName = RivalsCosmeticsState.PendingWrapSelectionByWeapon[previewWeaponName] previousCharmName = RivalsCosmeticsState.PendingCharmSelectionByWeapon[previewWeaponName] previousFinisherName = RivalsCosmeticsState.PendingFinisherSelectionByWeapon[previewWeaponName] if skinName ~= nil then RivalsCosmeticsState.PendingSkinSelectionByWeapon[previewWeaponName] = skinName end if wrapName ~= nil then RivalsCosmeticsState.PendingWrapSelectionByWeapon[previewWeaponName] = wrapName end if charmName ~= nil then RivalsCosmeticsState.PendingCharmSelectionByWeapon[previewWeaponName] = charmName end if finisherName ~= nil then RivalsCosmeticsState.PendingFinisherSelectionByWeapon[previewWeaponName] = finisherName end RivalsCosmetics.SetPreviewWeaponName(previewWeaponName) end local previewOk, previewResult = pcall(RivalsCosmetics.UpdatePreviewViewport) if hasWeapon then RivalsCosmeticsState.PendingSkinSelectionByWeapon[previewWeaponName] = previousSkinName RivalsCosmeticsState.PendingWrapSelectionByWeapon[previewWeaponName] = previousWrapName RivalsCosmeticsState.PendingCharmSelectionByWeapon[previewWeaponName] = previousCharmName RivalsCosmeticsState.PendingFinisherSelectionByWeapon[previewWeaponName] = previousFinisherName end RivalsCosmeticsState.PendingApplyToken = RivalsCosmeticsState.PendingApplyToken + 1 local pendingApplyToken = RivalsCosmeticsState.PendingApplyToken task.defer(GuardRivalsCallback('Cosmetics_ApplyPendingSelection_ApplyAll', function() if RivalsCosmeticsState.PendingApplyToken ~= pendingApplyToken then return nil end if hasWeapon then if skinName ~= nil then RivalsCosmeticsState.PendingSkinSelectionByWeapon[previewWeaponName] = skinName end if wrapName ~= nil then RivalsCosmeticsState.PendingWrapSelectionByWeapon[previewWeaponName] = wrapName end if charmName ~= nil then RivalsCosmeticsState.PendingCharmSelectionByWeapon[previewWeaponName] = charmName end if finisherName ~= nil then RivalsCosmeticsState.PendingFinisherSelectionByWeapon[previewWeaponName] = finisherName end end local applyResult = RivalsCosmetics.ApplyAll() if hasWeapon then RivalsCosmeticsState.PendingSkinSelectionByWeapon[previewWeaponName] = previousSkinName RivalsCosmeticsState.PendingWrapSelectionByWeapon[previewWeaponName] = previousWrapName RivalsCosmeticsState.PendingCharmSelectionByWeapon[previewWeaponName] = previousCharmName RivalsCosmeticsState.PendingFinisherSelectionByWeapon[previewWeaponName] = previousFinisherName end return applyResult end)) if not previewOk then error(previewResult, 0) end return previewOk end RivalsRuntimeBridge.ResolveRivalsCosmeticWeaponValues = RivalsCosmetics.ResolveEditorWeaponNames RivalsRuntimeBridge.ResolveRivalsCosmeticSkinValues = RivalsCosmetics.ResolveWeaponSkinValues RivalsRuntimeBridge.ResolveRivalsCosmeticSkinOptionId = RivalsCosmetics.ResolveSkinOptionId RivalsRuntimeBridge.ResolveRivalsCosmeticWrapValues = RivalsCosmetics.ResolveWrapValues RivalsRuntimeBridge.ResolveRivalsCosmeticWrapOptionId = RivalsCosmetics.ResolveWrapOptionId RivalsRuntimeBridge.ResolveRivalsCosmeticCharmValues = RivalsCosmetics.ResolveCharmValues RivalsRuntimeBridge.ResolveRivalsCosmeticCharmOptionId = RivalsCosmetics.ResolveCharmOptionId RivalsRuntimeBridge.ResolveRivalsCosmeticFinisherValues = RivalsCosmetics.ResolveFinisherValues RivalsRuntimeBridge.ResolveRivalsCosmeticFinisherOptionId = RivalsCosmetics.ResolveFinisherOptionId RivalsRuntimeBridge.ResolveRivalsCosmeticWrapInvertedOptionId = RivalsCosmetics.ResolveWrapInvertedOptionId RivalsRuntimeBridge.ResolveRivalsCosmeticWrapInvertedValue = RivalsCosmetics.ResolveWrapInvertedValue RivalsRuntimeBridge.ResolveRivalsCosmeticEditorOptionId = RivalsCosmetics.ResolveEditorOptionId RivalsRuntimeBridge.RefreshRivalsCosmetics = RivalsCosmetics.RefreshDropdownOptions RivalsRuntimeBridge.BuildRivalsCosmeticsPreviewModel = RivalsCosmetics.BuildPreviewModel RivalsRuntimeBridge.BuildRivalsCosmeticsPreviewViewportModel = RivalsCosmetics.BuildPreviewViewportModel RivalsRuntimeBridge.UpdateRivalsCosmeticsPreview = RivalsCosmetics.UpdatePreviewViewport RivalsRuntimeBridge.SetRivalsCosmeticsPreviewWeapon = RivalsCosmetics.SetPreviewWeaponName RivalsRuntimeBridge.ApplyRivalsCosmeticsPendingSelection = RivalsCosmetics.ApplyPendingSelection RivalsRuntimeBridge.SetRivalsCosmeticsSelectedWeapon = RivalsCosmetics.SetSelectedEditorWeapon RivalsRuntimeBridge.SetRivalsCosmeticsSelectionForSelectedWeapon = RivalsCosmetics.SetSelectionForSelectedEditorWeapon RivalsRuntimeBridge.RerollRivalsCosmeticsRandom = RivalsCosmetics.RerollRandomSelections RivalsRuntimeBridge.SetRivalsCosmeticFavoriteKind = RivalsCosmetics.SetSelectedFavoriteKind RivalsRuntimeBridge.ToggleRivalsCosmeticFavorite = RivalsCosmetics.ToggleSelectedFavorite RivalsRuntimeBridge.SetRivalsCosmeticOnlyUseFavorites = RivalsCosmetics.SetSelectedOnlyUseFavorites RivalsRuntimeBridge.ResolveRivalsRankCharmSeasonValues = RivalsCosmetics.ResolveRankCharmSeasonValues RivalsRuntimeBridge.ResolveRivalsRankCharmRankValues = RivalsCosmetics.ResolveRankCharmRankValues RivalsRuntimeBridge.SetRivalsRankCharmSelectedSeason = RivalsCosmetics.SetSelectedRankCharmSeason RivalsRuntimeBridge.UpdateRivalsRankCharmOverride = RivalsCosmetics.UpdateSelectedRankCharmOverride RivalsRuntimeBridge.SyncRivalsRankCharmEditor = RivalsCosmetics.SyncRankCharmEditorFromState RivalsRuntimeBridge.ResolveRivalsEmoteValues = RivalsEmotes.ResolveEmoteValues RivalsRuntimeBridge.ResolveRivalsEmoteSlotOptionId = RivalsEmotes.ResolveSlotOptionId RivalsRuntimeBridge.SetRivalsEmoteSlot = RivalsEmotes.SetEquippedEmote RivalsRuntimeBridge.ApplyRivalsEmotes = RivalsEmotes.ApplyAll RivalsRuntimeBridge.RestoreRivalsEmotes = RivalsEmotes.RestoreAll RivalsRuntimeBridge.ResolveRivalsCosmeticPresetNames = RivalsCosmetics.ResolveCosmeticPresetNames RivalsRuntimeBridge.RefreshRivalsCosmeticPresets = RivalsCosmetics.RefreshCosmeticPresetControls RivalsRuntimeBridge.CreateRivalsCosmeticPreset = RivalsCosmetics.CreateCosmeticPreset RivalsRuntimeBridge.SetSelectedRivalsCosmeticPreset = RivalsCosmetics.SetSelectedCosmeticPreset RivalsRuntimeBridge.SaveSelectedRivalsCosmeticPreset = RivalsCosmetics.SaveSelectedCosmeticPreset RivalsRuntimeBridge.LoadSelectedRivalsCosmeticPreset = RivalsCosmetics.LoadSelectedCosmeticPreset RivalsRuntimeBridge.DeleteSelectedRivalsCosmeticPreset = RivalsCosmetics.DeleteSelectedCosmeticPreset RivalsRuntimeBridge.SetRivalsCosmeticPresetSetting = RivalsCosmetics.SetCosmeticPresetSetting RivalsRuntimeBridge.ApplyRivalsCosmeticPresetAutoLoad = RivalsCosmetics.ApplyCosmeticPresetAutoLoad RivalsRuntimeBridge.QueueRivalsCosmeticPresetAutoSave = RivalsCosmetics.QueueCosmeticPresetAutoSave RivalsRuntimeBridge.SetRivalsCosmeticsConfigName = RivalsCosmetics.SetConfigName RivalsRuntimeBridge.RebuildRivalsWeaponUnlock = function() RivalsCosmeticsState.WeaponInventoryUnlockCache = setmetatable({}, { __mode = 'k' }) if RivalsCosmetics.EnsurePlayerDataHook then pcall(RivalsCosmetics.EnsurePlayerDataHook) end pcall(RivalsCosmetics.SyncWeaponInventoryUnlock) RivalsCosmetics.TriggerNativeDataChanged('WeaponInventory') if RivalsCosmetics.ApplyAll then pcall(RivalsCosmetics.ApplyAll) end end RivalsRuntimeBridge.SetRivalsWeaponUnlockAll = function(enabled) local toggle = Toggles and Toggles.P5S1T4 if toggle then toggle:SetValue(enabled == true) end end RivalsRuntimeBridge.LoadSavedRivalsCosmeticsSelections = RivalsCosmetics.LoadSavedSelectionsFromConfig RivalsRuntimeBridge.CaptureSavedRivalsCosmeticsSelections = RivalsCosmetics.CaptureSavedSelectionsBeforeConfigSave RivalsRuntimeBridge.SaveRivalsCosmeticsSelections = RivalsCosmetics.SaveSelectionsToConfig RivalsRuntimeBridge.SaveCosmeticsCache = RivalsCosmetics.SaveCosmeticsCache RivalsRuntimeBridge.LoadCosmeticsCache = RivalsCosmetics.LoadCosmeticsCache RivalsRuntimeBridge.BindRivalsCosmetics = RivalsCosmetics.BindFirstPersonModels RivalsRuntimeBridge.ApplyRivalsCosmetics = RivalsCosmetics.ApplyAll RivalsRuntimeBridge.RestoreRivalsCosmetics = RivalsCosmetics.RestoreAppliedCosmetics RivalsRuntimeBridge.RefreshNativeRivalsCosmetics = RivalsCosmetics.RefreshNativeCosmeticsScene RivalsRuntimeBridge.ResetRivalsCosmetics = RivalsCosmetics.ResetState end -- ====================================================================== -- World ESP: items, drops and sounds -- ====================================================================== local WorldESPState = {} do local WORLD_ITEM_NAMES = { ['Throwable - Grenade'] = { Color = Color3.fromRGB(255, 80, 80), Label = 'GRENADE' }, ['Throwable - Flashbang'] = { Color = Color3.fromRGB(255, 255, 100), Label = 'FLASH' }, ['Throwable - Molotov'] = { Color = Color3.fromRGB(255, 120, 0), Label = 'MOLOTOV' }, ['Throwable - Smoke'] = { Color = Color3.fromRGB(200, 200, 200), Label = 'SMOKE' }, ['Smoke Grenade'] = { Color = Color3.fromRGB(200, 200, 200), Label = 'SMOKE' }, ['Satchel'] = { Color = Color3.fromRGB(255, 50, 50), Label = 'SATCHEL' }, ['SubspaceTripmineHitbox'] = { Color = Color3.fromRGB(255, 50, 50), Label = 'TRIPMINE' }, ['Flare Gun'] = { Color = Color3.fromRGB(255, 50, 50), Label = 'FLARE GUN' }, } local ThrowableESP = {} local function IsTrackedItem(instance) return instance and (instance:IsA('BasePart') or instance:IsA('Model')) and WORLD_ITEM_NAMES[instance.Name] ~= nil end local function CreateThrowableESP(part) if ThrowableESP[part] then return end local label = Drawing.new('Text') label.Center = true label.Outline = true label.Font = 2 label.Visible = false label.Size = 12 label.Transparency = PLAYER_CARD_THEME.ThrowableTextTransparency ThrowableESP[part] = { label = label, lastRoundedDistance = nil, lastLabelText = nil, lastLabelSize = nil, lastLabelColor = nil, lastLabelPositionX = nil, lastLabelPositionY = nil, labelVisible = false, } end local function DestroyThrowableESP(part) local entry = ThrowableESP[part] if not entry then return end pcall(function() entry.label:Remove() end) ThrowableESP[part] = nil end function WorldESPState.DestroyThrowable(part) DestroyThrowableESP(part) end function WorldESPState.DestroyAllThrowable() for part in pairs(ThrowableESP) do DestroyThrowableESP(part) end end function WorldESPState.RegisterThrowable(part) if not IsTrackedItem(part) then return end CreateThrowableESP(part) end function WorldESPState.UpdateThrowable() local enabled = EspRenderSettings.ShowThrowable if not enabled then for _, entry in pairs(ThrowableESP) do if entry.labelVisible then entry.label.Visible = false entry.labelVisible = false end end return end local cam = Workspace.CurrentCamera if not cam then return end local maxDist = DEFAULT_ESP_MAX_DISTANCE local textSize = math.max(DEFAULT_ESP_TEXT_SIZE - 2, 10) local rootPosition = HumanoidRootPart and HumanoidRootPart.Position or nil local maxDistSquared = maxDist * maxDist for part in pairs(ThrowableESP) do if not part.Parent or not part:IsDescendantOf(Workspace) or not IsTrackedItem(part) then DestroyThrowableESP(part) end end for part, entry in pairs(ThrowableESP) do repeat local info = WORLD_ITEM_NAMES[part.Name] if not info then break end local pos = part:IsA('BasePart') and part.Position or part:GetPivot().Position local screenPos, onScreen = cam:WorldToViewportPoint(pos) if not onScreen or screenPos.Z <= 0 then if entry.labelVisible then entry.label.Visible = false entry.labelVisible = false end break end local dist = 0 if rootPosition then local rootOffset = pos - rootPosition local worldDistanceSquared = rootOffset:Dot(rootOffset) if worldDistanceSquared > maxDistSquared then if entry.labelVisible then entry.label.Visible = false entry.labelVisible = false end break end dist = math.sqrt(worldDistanceSquared) end if not entry.labelVisible then entry.label.Visible = true entry.labelVisible = true end if entry.lastLabelSize ~= textSize then entry.lastLabelSize = textSize entry.label.Size = textSize end local roundedDistance = math.floor(dist) if entry.lastRoundedDistance ~= roundedDistance or entry.lastLabelText ~= info.Label then entry.lastRoundedDistance = roundedDistance entry.lastLabelText = info.Label entry.label.Text = string.format('%s | %dm', info.Label, roundedDistance) end local screenX = math.round(screenPos.X) local screenY = math.round(screenPos.Y) if entry.lastLabelPositionX ~= screenX or entry.lastLabelPositionY ~= screenY then entry.lastLabelPositionX = screenX entry.lastLabelPositionY = screenY entry.label.Position = Vector2.new(screenX, screenY) end if entry.lastLabelColor ~= info.Color then entry.lastLabelColor = info.Color entry.label.Color = info.Color end until true end end end do local WORLD_HAZARD_NAMES = { ['MolotovHitbox'] = { Color = Color3.fromRGB(255, 120, 0), BoxTransparency = 0.78, }, } local HazardESP = {} local function IsTrackedHazard(instance) return instance and instance:IsA('BasePart') and WORLD_HAZARD_NAMES[instance.Name] ~= nil end local function HideHazardESP(entry) if not entry then return end entry.box.Visible = false entry.outline.Visible = false end local function DestroyHazardAdornment(entry) if not entry then return end pcall(function() entry.box:Destroy() end) pcall(function() entry.outline:Destroy() end) end local function CreateHazardESP(part) if HazardESP[part] then return end local box = Instance.new('BoxHandleAdornment') box.AlwaysOnTop = true box.Adornee = part box.Visible = false box.ZIndex = 10 box.Parent = HazardAdornmentContainer local outline = Instance.new('SelectionBox') outline.Adornee = part outline.Visible = false outline.LineThickness = 0.04 outline.Parent = HazardAdornmentContainer HazardESP[part] = { box = box, outline = outline, } end local function DestroyHazardESP(part) local entry = HazardESP[part] if not entry then return end DestroyHazardAdornment(entry) HazardESP[part] = nil end function WorldESPState.DestroyHazard(part) DestroyHazardESP(part) end function WorldESPState.DestroyAllHazard() for part in pairs(HazardESP) do DestroyHazardESP(part) end end function WorldESPState.RegisterHazard(part) if not IsTrackedHazard(part) then return end CreateHazardESP(part) end function WorldESPState.UpdateHazard() local enabled = EspRenderSettings.ShowThrowable if not enabled then for _, entry in pairs(HazardESP) do HideHazardESP(entry) end return end local maxDist = DEFAULT_ESP_MAX_DISTANCE local rootPosition = HumanoidRootPart and HumanoidRootPart.Position or nil local maxDistSquared = maxDist * maxDist for part, entry in pairs(HazardESP) do repeat local info = WORLD_HAZARD_NAMES[part.Name] if not part.Parent or not part:IsDescendantOf(Workspace) or not info then DestroyHazardESP(part) break end if rootPosition then local rootOffset = part.Position - rootPosition local worldDistanceSquared = rootOffset:Dot(rootOffset) if worldDistanceSquared > maxDistSquared then HideHazardESP(entry) break end end entry.box.Adornee = part entry.box.Size = part.Size entry.box.Color3 = info.Color entry.box.Transparency = info.BoxTransparency entry.box.Visible = true entry.outline.Adornee = part entry.outline.Color3 = BlendColor(info.Color, Color3.fromRGB(255, 255, 255), 0.18) entry.outline.SurfaceTransparency = 1 entry.outline.Visible = true until true end end end -- ====================================================================== -- World ESP: sound visualizer -- ====================================================================== do WorldESPState.SoundVisualizer = { Tracked = setmetatable({}, { __mode = 'k' }), ActiveEffects = {}, PooledEffects = {}, Enabled = false, MaxEffects = 32, } function WorldESPState.DisconnectSound(sound) local trackedSounds = WorldESPState.SoundVisualizer.Tracked local tracked = trackedSounds[sound] if not tracked then return end for _, connection in pairs(tracked.Connections) do pcall(function() connection:Disconnect() end) end trackedSounds[sound] = nil end function WorldESPState.ResolveSoundEmitterPart(sound) local parent = sound and sound.Parent or nil if not parent then return nil end if parent:IsA('BasePart') then return parent end if parent:IsA('Attachment') and parent.Parent and parent.Parent:IsA('BasePart') then return parent.Parent end return parent:FindFirstAncestorWhichIsA('BasePart') end function WorldESPState.ResolveSoundSourcePlayer(emitterPart) local model = emitterPart and emitterPart:FindFirstAncestorOfClass('Model') or nil while model do local player = Players:GetPlayerFromCharacter(model) if player then return player end model = model.Parent and model.Parent:FindFirstAncestorOfClass('Model') or nil end return nil end function WorldESPState.IsSoundSourceAllowed(sourcePlayer) local sourceMode = Options.P1S3D1 and Options.P1S3D1.Value or 'Enemies' if sourcePlayer and sourcePlayer ~= LP and not IsPlayerInLocalEnvironment(sourcePlayer) then return false end if sourceMode == 'All' then return true elseif sourceMode == 'Self' then return sourcePlayer == LP elseif sourceMode == 'Team' then return sourcePlayer ~= nil and sourcePlayer ~= LP and not ShouldShow(sourcePlayer, false) end return sourcePlayer ~= nil and sourcePlayer ~= LP and ShouldShow(sourcePlayer, false) end function WorldESPState.IsFootstepSound(sound, emitterPart) local descriptor = string.lower(table.concat({ tostring(sound and sound.Name or ''), tostring(emitterPart and emitterPart.Name or ''), tostring(sound and sound.Parent and sound.Parent.Name or ''), }, ' ')) return descriptor:find('foot', 1, true) ~= nil or descriptor:find('step', 1, true) ~= nil or descriptor:find('running', 1, true) ~= nil or descriptor:find('landing', 1, true) ~= nil or ( emitterPart and emitterPart.Name == 'HumanoidRootPart' and sound.Looped == false and sound.TimeLength > 0 and sound.TimeLength <= 1 ) end function WorldESPState.CreateSoundEffect() local anchor = Instance.new('Part') anchor.Name = '__ZkxHubSoundPulse' anchor.Anchored = true anchor.CanCollide = false anchor.CanQuery = false anchor.CanTouch = false anchor.CastShadow = false anchor.Transparency = 1 anchor.Size = Vector3.new(0.05, 0.05, 0.05) anchor.Parent = Workspace local pulse = Instance.new('SphereHandleAdornment') pulse.Name = '__ZkxHubSoundPulseAdornment' pulse.Adornee = anchor pulse.AlwaysOnTop = true pulse.Visible = false pulse.ZIndex = 9 pulse.Parent = anchor return { Anchor = anchor, Pulse = pulse, StartedAt = 0, Duration = 0, StartRadius = 0, EndRadius = 0, } end function WorldESPState.DeactivateSoundEffect(index, destroy) local soundVisualizer = WorldESPState.SoundVisualizer local effect = table.remove(soundVisualizer.ActiveEffects, index) if not effect then return end effect.Pulse.Visible = false if destroy then effect.Anchor:Destroy() else soundVisualizer.PooledEffects[#soundVisualizer.PooledEffects + 1] = effect end end function WorldESPState.SpawnSoundEffect(sound) local soundVisualizer = WorldESPState.SoundVisualizer if not soundVisualizer.Enabled or not sound or not sound.Parent then return end local minVolume = Options.P1S3S1 and Options.P1S3S1.Value or 0.05 if sound.Volume < minVolume then return end local emitterPart = WorldESPState.ResolveSoundEmitterPart(sound) if not emitterPart or not WorldESPState.IsSoundSourceAllowed(WorldESPState.ResolveSoundSourcePlayer(emitterPart)) then return end if HumanoidRootPart and sound.RollOffMaxDistance > 0 and (emitterPart.Position - HumanoidRootPart.Position).Magnitude > sound.RollOffMaxDistance then return end local isFootstep = WorldESPState.IsFootstepSound(sound, emitterPart) if isFootstep and not (Toggles.P1S3T2 and Toggles.P1S3T2.Value) then return end if not isFootstep and not (Toggles.P1S3T3 and Toggles.P1S3T3.Value) then return end if #soundVisualizer.ActiveEffects >= soundVisualizer.MaxEffects then WorldESPState.DeactivateSoundEffect(1, false) end local effect = table.remove(soundVisualizer.PooledEffects) or WorldESPState.CreateSoundEffect() local loudnessScale = math.clamp(sound.Volume, 0.25, 2) effect.Anchor.CFrame = CFrame.new(emitterPart.Position) effect.StartedAt = tick() effect.Duration = isFootstep and 0.65 or 0.9 effect.StartRadius = isFootstep and 0.45 or 0.8 effect.EndRadius = (isFootstep and 5 or 9) * loudnessScale effect.Pulse.Color3 = isFootstep and (Options.P1S3C1 and Options.P1S3C1.Value or Color3.fromRGB(255, 210, 80)) or (Options.P1S3C2 and Options.P1S3C2.Value or Color3.fromRGB(255, 90, 90)) effect.Pulse.Radius = effect.StartRadius effect.Pulse.Transparency = 0.28 effect.Pulse.Visible = true soundVisualizer.ActiveEffects[#soundVisualizer.ActiveEffects + 1] = effect end function WorldESPState.RegisterSound(instance) local soundVisualizer = WorldESPState.SoundVisualizer if not soundVisualizer.Enabled or not instance or not instance:IsA('Sound') or soundVisualizer.Tracked[instance] then return end local tracked = { LastPlayedAt = 0, Connections = {}, } soundVisualizer.Tracked[instance] = tracked tracked.Connections.Played = instance.Played:Connect(GuardRivalsCallback('SoundVisualizer_Played', function() local now = tick() if (now - tracked.LastPlayedAt) < 0.03 then return end tracked.LastPlayedAt = now WorldESPState.SpawnSoundEffect(instance) end)) tracked.Connections.Destroying = instance.Destroying:Connect(function() WorldESPState.DisconnectSound(instance) end) end function WorldESPState.DestroySound(instance) if instance and instance:IsA('Sound') then WorldESPState.DisconnectSound(instance) end end function WorldESPState.UpdateSoundVisualizer() local soundVisualizer = WorldESPState.SoundVisualizer if not soundVisualizer.Enabled then return end local now = tick() for index = #soundVisualizer.ActiveEffects, 1, -1 do local effect = soundVisualizer.ActiveEffects[index] local alpha = math.clamp((now - effect.StartedAt) / effect.Duration, 0, 1) if alpha >= 1 then WorldESPState.DeactivateSoundEffect(index, false) else local easedAlpha = 1 - ((1 - alpha) * (1 - alpha)) effect.Pulse.Radius = effect.StartRadius + ((effect.EndRadius - effect.StartRadius) * easedAlpha) effect.Pulse.Transparency = 0.28 + (alpha * 0.72) end end end function WorldESPState.SetSoundVisualizerEnabled(enabled) local soundVisualizer = WorldESPState.SoundVisualizer enabled = enabled == true if enabled == soundVisualizer.Enabled then return end if enabled then if not Connections then return end soundVisualizer.Enabled = true Connections:register( 'SoundVisualizer_Added', Workspace.DescendantAdded:Connect(GuardRivalsCallback('SoundVisualizer_Added', WorldESPState.RegisterSound)) ) Connections:register( 'SoundVisualizer_Removed', Workspace.DescendantRemoving:Connect( GuardRivalsCallback('SoundVisualizer_Removed', WorldESPState.DestroySound) ) ) Connections:register( 'SoundVisualizer_Render', RunService.RenderStepped:Connect( GuardRivalsCallback('SoundVisualizer_Render', WorldESPState.UpdateSoundVisualizer) ) ) for _, descendant in ipairs(Workspace:GetDescendants()) do WorldESPState.RegisterSound(descendant) end return end soundVisualizer.Enabled = false if Connections then Connections:disconnect({ 'SoundVisualizer_Added', 'SoundVisualizer_Removed', 'SoundVisualizer_Render', }) end local sounds = {} for sound in pairs(soundVisualizer.Tracked) do sounds[#sounds + 1] = sound end for _, sound in ipairs(sounds) do WorldESPState.DisconnectSound(sound) end while #soundVisualizer.ActiveEffects > 0 do WorldESPState.DeactivateSoundEffect(#soundVisualizer.ActiveEffects, false) end end function WorldESPState.DestroyAllSoundVisualizer() local soundVisualizer = WorldESPState.SoundVisualizer soundVisualizer.Enabled = false if Connections then Connections:disconnect({ 'SoundVisualizer_Added', 'SoundVisualizer_Removed', 'SoundVisualizer_Render', }) end local sounds = {} for sound in pairs(soundVisualizer.Tracked) do sounds[#sounds + 1] = sound end for _, sound in ipairs(sounds) do WorldESPState.DisconnectSound(sound) end while #soundVisualizer.ActiveEffects > 0 do WorldESPState.DeactivateSoundEffect(#soundVisualizer.ActiveEffects, true) end for _, effect in ipairs(soundVisualizer.PooledEffects) do effect.Anchor:Destroy() end table.clear(soundVisualizer.PooledEffects) end end -- ====================================================================== -- ESP preview (the live preview in the ESP tab) -- ====================================================================== do local EspClassic = RivalsRuntimeBridge.ESPClassic local function CreateEspPreviewPart(model, name, size, position, color) local part = Instance.new('Part') part.Name = name part.Size = size part.CFrame = CFrame.new(position) part.Anchored = true part.CanCollide = false part.CanQuery = false part.CanTouch = false part.CastShadow = false part.Material = Enum.Material.SmoothPlastic part.Color = color part.Parent = model return part end local function BuildEspPreviewModel() local model = Instance.new('Model') model.Name = 'ZkxHubEspPreview' local bodyColor = Color3.fromRGB(116, 124, 137) local limbColor = Color3.fromRGB(82, 90, 104) local torso = CreateEspPreviewPart(model, 'Torso', Vector3.new(2, 2.2, 1), Vector3.new(0, 0.65, 0), bodyColor) CreateEspPreviewPart( model, 'Head', Vector3.new(1.45, 1.45, 1.45), Vector3.new(0, 2.55, 0), Color3.fromRGB(152, 158, 168) ) CreateEspPreviewPart(model, 'LeftArm', Vector3.new(0.75, 2.1, 0.8), Vector3.new(-1.4, 0.7, 0), limbColor) CreateEspPreviewPart(model, 'RightArm', Vector3.new(0.75, 2.1, 0.8), Vector3.new(1.4, 0.7, 0), limbColor) CreateEspPreviewPart(model, 'LeftLeg', Vector3.new(0.85, 2.2, 0.9), Vector3.new(-0.52, -1.55, 0), limbColor) CreateEspPreviewPart(model, 'RightLeg', Vector3.new(0.85, 2.2, 0.9), Vector3.new(0.52, -1.55, 0), limbColor) model.PrimaryPart = torso local highlight = Instance.new('Highlight') highlight.Name = 'PreviewHighlight' highlight.Adornee = model highlight.DepthMode = Enum.HighlightDepthMode.AlwaysOnTop highlight.Parent = model return model, highlight end local function CreateEspPreviewPane(parent, anchorPoint, position, size, horizontalAlignment, verticalAlignment) local pane = Instance.new('Frame') pane.AnchorPoint = anchorPoint pane.Position = position pane.Size = size pane.BackgroundTransparency = 1 pane.ZIndex = 23 pane.Parent = parent local layout = Instance.new('UIListLayout') layout.Padding = UDim.new(0, 2) layout.FillDirection = Enum.FillDirection.Vertical layout.HorizontalAlignment = horizontalAlignment layout.VerticalAlignment = verticalAlignment layout.SortOrder = Enum.SortOrder.LayoutOrder layout.Parent = pane return pane end local function CreateEspPreviewLabel(parent, layoutOrder, textXAlignment) local label = Instance.new('TextLabel') label.LayoutOrder = layoutOrder label.Size = UDim2.new(1, 0, 0, 14) label.BackgroundTransparency = 1 label.Font = Enum.Font.Code label.TextColor3 = PLAYER_CARD_THEME.BarTextColor label.TextSize = 13 label.TextStrokeColor3 = PLAYER_CARD_THEME.Classic.BoxOutlineColor label.TextStrokeTransparency = 0 label.TextXAlignment = textXAlignment or Enum.TextXAlignment.Center label.ZIndex = 24 label.Parent = parent return label end local function CreateEspPreviewSegment(parent) local segment = Instance.new('Frame') segment.BackgroundColor3 = Color3.new(1, 1, 1) segment.BorderColor3 = PLAYER_CARD_THEME.Classic.BoxOutlineColor segment.BorderSizePixel = 1 segment.ZIndex = 22 segment.Visible = false segment.Parent = parent return segment end local function SetEspPreviewSegment(segment, visible, position, size, color) segment.Visible = visible if not visible then return end segment.Position = position segment.Size = size segment.BackgroundColor3 = color end local function UpdateEspPreviewBox(state, visible, color) local segments = state.BoxSegments if not visible then for _, segment in ipairs(segments) do segment.Visible = false end return end SetEspPreviewSegment(segments[1], true, UDim2.fromScale(0, 0), UDim2.new(0.28, 0, 0, 2), color) SetEspPreviewSegment(segments[2], true, UDim2.new(0.72, 0, 0, 0), UDim2.new(0.28, 0, 0, 2), color) SetEspPreviewSegment(segments[3], true, UDim2.new(0, 0, 1, -2), UDim2.new(0.28, 0, 0, 2), color) SetEspPreviewSegment(segments[4], true, UDim2.new(0.72, 0, 1, -2), UDim2.new(0.28, 0, 0, 2), color) SetEspPreviewSegment(segments[5], true, UDim2.fromScale(0, 0), UDim2.new(0, 2, 0.28, 0), color) SetEspPreviewSegment(segments[6], true, UDim2.new(0, 0, 0.72, 0), UDim2.new(0, 2, 0.28, 0), color) SetEspPreviewSegment(segments[7], true, UDim2.new(1, -2, 0, 0), UDim2.new(0, 2, 0.28, 0), color) SetEspPreviewSegment(segments[8], true, UDim2.new(1, -2, 0.72, 0), UDim2.new(0, 2, 0.28, 0), color) end -- the preview plays the bar and the ammo like a live target: health sweeps 0 to 100 and back, the magazine empties and reloads local function ApplyEspPreviewHealth(state, settings) local pct = state.HealthPct or 0.75 local number = math.floor(pct * 100 + 0.5) if settings.UseVerticalHealth then state.HealthFill.Size = UDim2.fromScale(1, pct) state.HealthNumber.Text = tostring(number) state.HealthFill.BackgroundColor3 = EspClassic.GetReactiveBarColor(pct) else state.HealthFill.Size = UDim2.fromScale(pct, 1) state.HealthNumber.Text = number .. ' HP' state.HealthFill.BackgroundColor3 = GetHealthBarColor(pct) end end local function ApplyEspPreviewAmmo(state, settings) local ammo = state.PreviewAmmo or 20 state.AmmoNumber.Text = ammo .. '/30' state.AmmoNumber.TextColor3 = (ammo <= 0 or state.PreviewReloading) and settings.EmptyAmmoColor or PLAYER_CARD_THEME.BarTextColor state.HeldWeapon.Text = state.PreviewReloading and '*Reloading*' or 'Assault Rifle' end RivalsRuntimeBridge.UpdateEspPreview = function() local state = RivalsRuntimeBridge.EspPreviewState if not state or not state.Overlay or state.Overlay.Parent == nil then return end local settings = EspRenderSettings local color = settings.VisibleColor state.Overlay.Visible = settings.Enabled state.Highlight.Enabled = settings.Enabled and settings.ShowHighlight state.Highlight.FillColor = color state.Highlight.OutlineColor = BlendColor(color, PLAYER_CARD_THEME.FrameColor, 0.24) state.Highlight.FillTransparency = settings.FillAlpha state.Highlight.OutlineTransparency = settings.OutlineAlpha UpdateEspPreviewBox(state, settings.ShowBox, color) state.Name.Visible = settings.ShowName state.Name.Text = LP and LP.Name or 'Player' local useHorizontalHealth = settings.ShowHealth and not settings.UseVerticalHealth state.TopPane.Position = UDim2.new(0.5, 0, 0, useHorizontalHealth and -21 or -2) state.HealthRail.Visible = settings.ShowHealth state.HealthNumber.Visible = settings.ShowHealth if settings.UseVerticalHealth then state.HealthRailCorner.CornerRadius = UDim.new(0, 0) state.HealthFillCorner.CornerRadius = UDim.new(0, 0) state.HealthRail.AnchorPoint = Vector2.new(1, 0) state.HealthRail.Position = UDim2.new(0, -6, 0, 0) state.HealthRail.Size = UDim2.new(0, 4, 1, 0) state.HealthFill.AnchorPoint = Vector2.new(0, 1) state.HealthFill.Position = UDim2.fromScale(0, 1) state.HealthFill.Size = UDim2.fromScale(1, 0.75) state.HealthNumber.AnchorPoint = Vector2.new(1, 0.5) state.HealthNumber.Position = UDim2.new(0, -10, 0.25, 0) state.HealthNumber.Size = UDim2.new(0, 30, 0, 14) state.HealthNumber.Text = '75' else state.HealthRailCorner.CornerRadius = UDim.new(1, 0) state.HealthFillCorner.CornerRadius = UDim.new(1, 0) state.HealthRail.AnchorPoint = Vector2.new(0.5, 1) state.HealthRail.Position = UDim2.new(0.5, 0, 0, -3) state.HealthRail.Size = UDim2.new(0, 60, 0, 14) state.HealthFill.AnchorPoint = Vector2.new(0, 0) state.HealthFill.Position = UDim2.fromScale(0, 0) state.HealthFill.Size = UDim2.fromScale(0.75, 1) state.HealthNumber.AnchorPoint = Vector2.new(0.5, 0.5) state.HealthNumber.Position = UDim2.new(0.5, 0, 0, -10) state.HealthNumber.Size = UDim2.new(0, 54, 0, 14) state.HealthNumber.Text = '75 HP' end state.HealthFill.BackgroundColor3 = settings.UseVerticalHealth and EspClassic.GetReactiveBarColor(0.75) or GetHealthBarColor(0.75) ApplyEspPreviewHealth(state, settings) state.AmmoNumber.Visible = settings.ShowAmmoText state.AmmoNumber.Text = '20/30' ApplyEspPreviewAmmo(state, settings) state.BottomPane.Position = UDim2.new(0.5, 0, 1, 3) state.LookDirectionLine.Visible = settings.ShowLookDir state.LookDirectionLine.BackgroundColor3 = settings.LookDirColor state.HeldWeapon.Visible = settings.ShowWeapon state.HeldWeapon.Text = 'Assault Rifle' if state.PreviewReloading then state.HeldWeapon.Text = "*Reloading*" end state.Distance.Visible = settings.ShowDistance state.Distance.Text = '42m' state.Deflecting.Visible = settings.ShowDeflecting state.Deflecting.Text = 'DEFLECTING' state.Rank.Visible = settings.ShowRank state.Rank.Text = 'DIAMOND' state.WinStreak.Visible = settings.ShowWinStreak state.WinStreak.Text = '5 WS' local previewFont = Enum.Font.Code local mainTextSize = 16 local flagTextSize = 12 for _, label in ipairs({ state.Name, state.AmmoNumber, state.HeldWeapon, state.Distance }) do label.Font = previewFont label.TextSize = mainTextSize end state.HealthNumber.Font = previewFont state.HealthNumber.TextSize = mainTextSize for _, label in ipairs({ state.Deflecting, state.Rank, state.WinStreak }) do label.Font = previewFont label.TextSize = flagTextSize end for _, pane in ipairs({ state.TopPane, state.BottomPane, state.RightPane }) do local layout = pane:FindFirstChildOfClass('UIListLayout') if layout then layout.Padding = UDim.new(0, 1) end end end RivalsRuntimeBridge.BuildEspPreview = function(groupbox) local function BuildEspPreviewAvatarModel() local descriptionOk, description = pcall( function() return Players:GetHumanoidDescriptionFromUserIdAsync(LP.UserId) end ) if not descriptionOk or description == nil then return nil end local modelOk, avatarModel = pcall( function() return Players:CreateHumanoidModelFromDescriptionAsync(description, Enum.HumanoidRigType.R15) end ) if not modelOk or avatarModel == nil then return nil end avatarModel:PivotTo(CFrame.identity) local animate = avatarModel:FindFirstChild('Animate') if animate then animate:Destroy() end return avatarModel end local EspPreviewOrbit = { Rotation = math.rad(225), Elevation = math.rad(20), DistanceMultiplier = 4, ModelCenter = Vector3.zero, ModelSize = Vector3.zero, ModelRadius = 4, } local function MeasureEspPreviewModel(previewModel) local minCorner, maxCorner for _, descendant in ipairs(previewModel:GetDescendants()) do if descendant:IsA('BasePart') and not descendant:FindFirstAncestorOfClass('Accessory') then local halfSize = descendant.Size * 0.5 local lowCorner = descendant.Position - halfSize local highCorner = descendant.Position + halfSize if not minCorner then minCorner, maxCorner = lowCorner, highCorner else minCorner = Vector3.new( math.min(minCorner.X, lowCorner.X), math.min(minCorner.Y, lowCorner.Y), math.min(minCorner.Z, lowCorner.Z) ) maxCorner = Vector3.new( math.max(maxCorner.X, highCorner.X), math.max(maxCorner.Y, highCorner.Y), math.max(maxCorner.Z, highCorner.Z) ) end end end if not minCorner then return end local size = maxCorner - minCorner EspPreviewOrbit.ModelCenter = (minCorner + maxCorner) * 0.5 EspPreviewOrbit.ModelSize = size EspPreviewOrbit.ModelRadius = math.max(size.X, size.Y, size.Z) * 0.5 end local UpdateEspPreviewOverlayRect = nil local function UpdateEspPreviewCamera(camera) local distance = math.max(EspPreviewOrbit.ModelRadius * 2.4 * EspPreviewOrbit.DistanceMultiplier, 4) local elevation = math.clamp(EspPreviewOrbit.Elevation, -math.pi / 2 + 0.1, math.pi / 2 - 0.1) local planar = math.cos(elevation) local offset = Vector3.new( math.cos(EspPreviewOrbit.Rotation) * distance * planar, math.sin(elevation) * distance, math.sin(EspPreviewOrbit.Rotation) * distance * planar ) camera.CFrame = CFrame.new(EspPreviewOrbit.ModelCenter + offset, EspPreviewOrbit.ModelCenter) if UpdateEspPreviewOverlayRect then UpdateEspPreviewOverlayRect() end end local model, highlight = BuildEspPreviewModel() local viewport = groupbox:AddViewport('P1S4V1', { Object = model, Clone = false, AutoFocus = false, Interactive = false, Height = 400, }) if not viewport then model:Destroy() return end viewport.Camera.FieldOfView = 18 MeasureEspPreviewModel(model) UpdateEspPreviewCamera(viewport.Camera) local viewportFrame = viewport.Object.Parent viewportFrame.LightDirection = Vector3.new(-1, -1, -1).Unit viewportFrame.LightColor = Color3.fromRGB(255, 255, 255) viewportFrame.Ambient = Color3.fromRGB(140, 140, 150) viewportFrame.ClipsDescendants = true local previewWorldModel = Instance.new('WorldModel') previewWorldModel.Parent = viewportFrame model.Parent = previewWorldModel local backgroundFrame = Instance.new('Frame') backgroundFrame.Name = 'EspPreviewBackground' backgroundFrame.Size = UDim2.fromScale(1, 1) backgroundFrame.BackgroundColor3 = Color3.fromRGB(255, 255, 255) backgroundFrame.BorderSizePixel = 0 backgroundFrame.ZIndex = 0 backgroundFrame.Parent = viewportFrame.Parent local backgroundGradient = Instance.new('UIGradient') backgroundGradient.Rotation = 90 backgroundGradient.Color = ColorSequence.new(Library.Scheme.MainColor, Library.Scheme.BackgroundColor) backgroundGradient.Parent = backgroundFrame local backgroundCorner = Instance.new('UICorner') backgroundCorner.CornerRadius = UDim.new(0, 6) backgroundCorner.Parent = backgroundFrame local viewportCorner = Instance.new('UICorner') viewportCorner.CornerRadius = UDim.new(0, 6) viewportCorner.Parent = viewportFrame local orbitButton = Instance.new('TextButton') orbitButton.Name = 'EspPreviewOrbitButton' orbitButton.Size = UDim2.fromScale(1, 1) orbitButton.BackgroundTransparency = 1 orbitButton.Text = '' orbitButton.AutoButtonColor = false orbitButton.ZIndex = 19 orbitButton.Parent = viewportFrame local orbitDragging = false local orbitLastPosition = nil orbitButton.MouseEnter:Connect(GuardRivalsCallback('EspPreview_HoverScroll', function() for _, side in ipairs(groupbox.Tab.Sides) do side.ScrollingEnabled = false end end)) orbitButton.MouseLeave:Connect(GuardRivalsCallback('EspPreview_HoverScroll', function() for _, side in ipairs(groupbox.Tab.Sides) do side.ScrollingEnabled = true end end)) orbitButton.InputBegan:Connect(GuardRivalsCallback('EspPreview_OrbitDrag', function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then orbitDragging = true orbitLastPosition = nil end end)) orbitButton.InputChanged:Connect(GuardRivalsCallback('EspPreview_OrbitZoom', function(input) if input.UserInputType ~= Enum.UserInputType.MouseWheel then return end local direction = 1 if input.Position.Z > 0 then direction = -1 end EspPreviewOrbit.DistanceMultiplier = math.clamp(EspPreviewOrbit.DistanceMultiplier * 0.85 ^ -direction, 0.2, 6) UpdateEspPreviewCamera(viewport.Camera) end)) Library:GiveSignal(UserInputService.InputChanged:Connect(GuardRivalsCallback('EspPreview_OrbitDrag', function(input) if not orbitDragging then return end if input.UserInputType ~= Enum.UserInputType.MouseMovement and input.UserInputType ~= Enum.UserInputType.Touch then return end local position = Vector2.new(input.Position.X, input.Position.Y) if orbitLastPosition then local delta = position - orbitLastPosition EspPreviewOrbit.Rotation = EspPreviewOrbit.Rotation - delta.X * 0.012 EspPreviewOrbit.Elevation = math.clamp(EspPreviewOrbit.Elevation - delta.Y * 0.012, -math.pi / 2 + 0.1, math.pi / 2 - 0.1) UpdateEspPreviewCamera(viewport.Camera) end orbitLastPosition = position end))) Library:GiveSignal(UserInputService.InputEnded:Connect(GuardRivalsCallback('EspPreview_OrbitDrag', function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then orbitDragging = false orbitLastPosition = nil end end))) local overlay = Instance.new('Frame') overlay.Name = 'EspPreviewOverlay' overlay.Size = UDim2.fromScale(1, 1) overlay.BackgroundTransparency = 1 overlay.ZIndex = 20 overlay.Parent = viewportFrame local target = Instance.new('Frame') target.Name = 'TargetRect' target.AnchorPoint = Vector2.new(0.5, 0.5) target.Position = UDim2.fromScale(0.5, 0.5) target.Size = UDim2.fromScale(0.3, 0.6) target.BackgroundTransparency = 1 target.ZIndex = 21 target.Parent = overlay UpdateEspPreviewOverlayRect = function() local viewportSize = viewportFrame.AbsoluteSize if viewportSize.X < 1 or viewportSize.Y < 1 then return end local camera = viewport.Camera local cameraCFrame = camera.CFrame local tanHalfFov = math.tan(math.rad(camera.FieldOfView) * 0.5) local aspect = viewportSize.X / viewportSize.Y local center = EspPreviewOrbit.ModelCenter local halfSize = EspPreviewOrbit.ModelSize * 0.5 local minX, minY, maxX, maxY for xSign = -1, 1, 2 do for ySign = -1, 1, 2 do for zSign = -1, 1, 2 do local corner = center + Vector3.new(halfSize.X * xSign, halfSize.Y * ySign, halfSize.Z * zSign) local relative = cameraCFrame:PointToObjectSpace(corner) local depth = math.max(-relative.Z, 0.05) local screenX = (relative.X / (depth * tanHalfFov * aspect) * 0.5 + 0.5) * viewportSize.X local screenY = (0.5 - relative.Y / (depth * tanHalfFov) * 0.5) * viewportSize.Y if not minX or screenX < minX then minX = screenX end if not maxX or screenX > maxX then maxX = screenX end if not minY or screenY < minY then minY = screenY end if not maxY or screenY > maxY then maxY = screenY end end end end target.Position = UDim2.fromOffset(math.round((minX + maxX) * 0.5), math.round((minY + maxY) * 0.5)) target.Size = UDim2.fromOffset(math.round(maxX - minX), math.round(maxY - minY)) end UpdateEspPreviewOverlayRect() viewportFrame :GetPropertyChangedSignal('AbsoluteSize') :Connect(GuardRivalsCallback('EspPreview_Layout', UpdateEspPreviewOverlayRect)) local boxSegments = table.create(8) for segmentIndex = 1, 8 do boxSegments[segmentIndex] = CreateEspPreviewSegment(target) end local topPane = CreateEspPreviewPane( target, Vector2.new(0.5, 1), UDim2.new(0.5, 0, 0, -2), UDim2.new(3, 0, 0, 18), Enum.HorizontalAlignment.Center, Enum.VerticalAlignment.Bottom ) local bottomPane = CreateEspPreviewPane( target, Vector2.new(0.5, 0), UDim2.new(0.5, 0, 1, 3), UDim2.new(3, 0, 0, 48), Enum.HorizontalAlignment.Center, Enum.VerticalAlignment.Top ) local rightPane = CreateEspPreviewPane( target, Vector2.new(0, 0), UDim2.new(1, 6, 0, 0), UDim2.new(2, 0, 1, 0), Enum.HorizontalAlignment.Left, Enum.VerticalAlignment.Top ) local healthRail = Instance.new('Frame') healthRail.BackgroundColor3 = PLAYER_CARD_THEME.BarBackgroundColor healthRail.BorderColor3 = PLAYER_CARD_THEME.Classic.BoxOutlineColor healthRail.BorderSizePixel = 1 healthRail.ZIndex = 23 healthRail.Parent = target local healthFill = Instance.new('Frame') healthFill.BackgroundColor3 = EspClassic.GetReactiveBarColor(0.75) healthFill.BorderSizePixel = 0 healthFill.ZIndex = 24 healthFill.Parent = healthRail local healthRailCorner = Instance.new('UICorner') healthRailCorner.Parent = healthRail local healthFillCorner = Instance.new('UICorner') healthFillCorner.Parent = healthFill local healthNumber = CreateEspPreviewLabel(target, 0, Enum.TextXAlignment.Center) local lookDirectionLine = Instance.new('Frame') lookDirectionLine.Name = 'LookDirectionLine' lookDirectionLine.AnchorPoint = Vector2.new(0, 0.5) lookDirectionLine.Position = UDim2.new(0.5, 0, 0.16, 0) lookDirectionLine.Size = UDim2.new(0, 38, 0, 2) lookDirectionLine.Rotation = -12 lookDirectionLine.BackgroundColor3 = Color3.fromRGB(255, 255, 0) lookDirectionLine.BorderSizePixel = 0 lookDirectionLine.ZIndex = 25 lookDirectionLine.Parent = target RivalsRuntimeBridge.EspPreviewState = { Viewport = viewport, Overlay = overlay, Target = target, Highlight = highlight, BoxSegments = boxSegments, TopPane = topPane, BottomPane = bottomPane, RightPane = rightPane, Name = CreateEspPreviewLabel(topPane, 1), AmmoNumber = CreateEspPreviewLabel(bottomPane, 1), HeldWeapon = CreateEspPreviewLabel(bottomPane, 2), Distance = CreateEspPreviewLabel(bottomPane, 3), Deflecting = CreateEspPreviewLabel(rightPane, 1, Enum.TextXAlignment.Left), Rank = CreateEspPreviewLabel(rightPane, 2, Enum.TextXAlignment.Left), WinStreak = CreateEspPreviewLabel(rightPane, 3, Enum.TextXAlignment.Left), HealthRail = healthRail, HealthFill = healthFill, HealthRailCorner = healthRailCorner, HealthFillCorner = healthFillCorner, HealthNumber = healthNumber, LookDirectionLine = lookDirectionLine, } RivalsRuntimeBridge.UpdateEspPreview() local previewClock = 0 local previewVisibleCheckAt = 0 local previewOnScreen = false Library:GiveSignal(RunService.RenderStepped:Connect(GuardRivalsCallback("EspPreview_Animate", function(deltaTime) local state = RivalsRuntimeBridge.EspPreviewState if not state or not state.Overlay or not state.Overlay.Parent or not Library.Toggled then return end previewClock = previewClock + deltaTime -- is the ESP page the one on screen? (checked a few times a second) if previewClock >= previewVisibleCheckAt then previewVisibleCheckAt = previewClock + 0.25 previewOnScreen = viewportFrame.AbsoluteSize.X > 1 local node = viewportFrame while previewOnScreen and node do if node:IsA("GuiObject") and not node.Visible then previewOnScreen = false end node = node.Parent end end if not previewOnScreen then return end -- a slow turn (one lap in 10 s), paused while it is being dragged if not orbitDragging then EspPreviewOrbit.Rotation = EspPreviewOrbit.Rotation + deltaTime * math.rad(36) UpdateEspPreviewCamera(viewport.Camera) end state.HealthPct = math.abs((previewClock / 4) % 2 - 1) local cycle = previewClock % 7.2 state.PreviewReloading = cycle >= 6 state.PreviewAmmo = state.PreviewReloading and 0 or math.ceil(30 * (1 - cycle / 6)) local settings = EspRenderSettings ApplyEspPreviewHealth(state, settings) ApplyEspPreviewAmmo(state, settings) end))) task.spawn(GuardRivalsCallback('EspPreview_Avatar', function() local avatarModel = BuildEspPreviewAvatarModel() if avatarModel == nil then return end if Library.Unloaded or viewport.Destroyed then avatarModel:Destroy() return end viewport:SetObject(avatarModel) avatarModel.Parent = previewWorldModel avatarModel:PivotTo(CFrame.identity) local avatarHighlight = Instance.new('Highlight') avatarHighlight.Name = 'PreviewHighlight' avatarHighlight.Adornee = avatarModel avatarHighlight.DepthMode = Enum.HighlightDepthMode.AlwaysOnTop avatarHighlight.Parent = avatarModel local state = RivalsRuntimeBridge.EspPreviewState if state then state.Highlight = avatarHighlight end task.wait(0.2) MeasureEspPreviewModel(avatarModel) EspPreviewOrbit.Rotation = math.rad(225) UpdateEspPreviewCamera(viewport.Camera) RivalsRuntimeBridge.UpdateEspPreview() end)) end end -- UI local Window = GameBootstrap.create_window({ library = Library, script_paths = ScriptPaths, auto_show_config = AutoShowConfig, position = UDim2.new(0, 230, 0, 200), title = TitleTemplate, center = true, }) __ZkhHub_genv.ZkxLoad(0.30, 'Loading game data') -- ====================================================================== -- Player chams styles (Flat, Glow, Fresnel, Glass, Chrome, Wireframe) -- ====================================================================== -- Driven from the ESP render loop: for every player the ESP is showing it gets the player and the colour the ESP picked -- (Visible colour while the player is in line of sight, Hidden colour behind cover, team colour for teammates). -- Every part it touches is restored when the style changes, the switch goes off, the player leaves or the ESP stops -- calling for him (the sweep below), so nothing is left behind. do local RunService = game:GetService('RunService') local Chams = { Entries = {}, Names = { 'Flat', 'Glow', 'Fresnel', 'Glass', 'Chrome', 'Wireframe', 'Forcefield' }, } RivalsRuntimeBridge.PlayerChams = Chams local function toggleOn(id) return Toggles ~= nil and Toggles[id] ~= nil and Toggles[id].Value == true end local function optionValue(id, fallback) local option = Options and Options[id] if option == nil or option.Value == nil then return fallback end return option.Value end local function usable(part) return part:IsA('BasePart') and part.Name ~= 'HumanoidRootPart' and part.Name ~= 'FakeMass' and not part.Name:find('Hitbox') and part.Transparency < 0.95 end -- ---------------------------------------------------------------- what each style does -- material = override for the real parts (only seen while the player is in sight); fill/outline = the see-through highlight local Styles = { Flat = { Fill = 0, Outline = 1 }, Glow = { Material = Enum.Material.Neon, Strip = true, Fill = 0.55, Outline = 0 }, Fresnel = { Fill = 0.88, Outline = 0 }, Glass = { Material = Enum.Material.Glass, Strip = true, Reflectance = 0.15, Fill = 0.78, Outline = 0.35 }, Chrome = { Material = Enum.Material.Metal, Strip = true, Reflectance = 1, Lift = 0.5, Fill = 1, Outline = 0.2 }, Wireframe = { Fill = 1, Outline = 1, Boxes = true }, Forcefield = { Material = Enum.Material.ForceField, Strip = true, Fill = 0.8, Outline = 0.25 }, } local function snapshotPart(entry, part) if entry.Snaps[part] then return end entry.Snaps[part] = { Material = part.Material, Color = part.Color, Reflectance = part.Reflectance, } end local SKIN_PROPS = { Shirt = 'ShirtTemplate', Pants = 'PantsTemplate', ShirtGraphic = 'Graphic', MeshPart = 'TextureID', SpecialMesh = 'TextureId' } local function stripSkins(entry) if entry.Skinless then return end entry.Skinless = true for _, descendant in ipairs(entry.Character:GetDescendants()) do local prop = SKIN_PROPS[descendant.ClassName] if prop then local ok, value = pcall(function() return descendant[prop] end) if ok and value ~= '' then entry.Skins[descendant] = { Prop = prop, Value = value } pcall(function() descendant[prop] = '' end) end end end end local function restoreParts(entry) for part, snap in pairs(entry.Snaps) do pcall(function() part.Material = snap.Material part.Color = snap.Color part.Reflectance = snap.Reflectance end) end entry.Snaps = {} for instance, skin in pairs(entry.Skins) do pcall(function() instance[skin.Prop] = skin.Value end) end entry.Skins = {} entry.Skinless = false end function Chams.Clear(player) local entry = Chams.Entries[player] if not entry then return end restoreParts(entry) if entry.Highlight then pcall(function() entry.Highlight:Destroy() end) end for _, box in ipairs(entry.Boxes) do pcall(function() box:Destroy() end) end Chams.Entries[player] = nil end function Chams.Destroy() for player in pairs(Chams.Entries) do Chams.Clear(player) end if Connections then Connections:disconnect({ 'PlayerChams_Sweep' }) end Chams.SweepConnected = false end local function build(player, character, style) local spec = Styles[style] or Styles.Glow local entry = { Character = character, Style = style, Snaps = {}, Skins = {}, Boxes = {}, Parts = {}, StepAt = os.clock(), } local highlight = Instance.new('Highlight') highlight.Name = 'ZkxHubPlayerChams' highlight.Adornee = character highlight.Parent = character entry.Highlight = highlight for _, descendant in ipairs(character:GetDescendants()) do if usable(descendant) then entry.Parts[#entry.Parts + 1] = descendant if spec.Boxes then local box = Instance.new('SelectionBox') box.Name = 'ZkxHubPlayerChamsBox' box.Adornee = descendant box.LineThickness = 0.025 box.SurfaceTransparency = 1 box.Parent = descendant entry.Boxes[#entry.Boxes + 1] = box end end end Chams.Entries[player] = entry if not Chams.SweepConnected and Connections then Chams.SweepConnected = true Connections:register('PlayerChams_Sweep', RunService.Heartbeat:Connect(GuardRivalsCallback('PlayerChams_Sweep', Chams.Sweep))) end return entry end -- called every ESP frame for a player that is on screen function Chams.Step(player, character, color, visible, styleOverride) if not toggleOn('ZPCT1') then Chams.Clear(player) return end if typeof(character) ~= 'Instance' or not character.Parent or typeof(color) ~= 'Color3' then return end -- own colours instead of the ESP's (the Visible one while the player is in sight, the Hidden one behind cover) if toggleOn('ZPCT3') then local custom = optionValue(visible == false and 'ZPCC2' or 'ZPCC1', nil) if typeof(custom) == 'Color3' then color = custom end end local style = styleOverride or optionValue('ZPCD1', 'Glow') local entry = Chams.Entries[player] if entry and (entry.Character ~= character or entry.Style ~= style) then Chams.Clear(player) entry = nil end entry = entry or build(player, character, style) entry.StepAt = os.clock() local spec = Styles[style] or Styles.Glow local alpha = tonumber(optionValue('ZPCS1', 0.3)) or 0.3 local throughWalls = toggleOn('ZPCT2') local highlight = entry.Highlight highlight.DepthMode = throughWalls and Enum.HighlightDepthMode.AlwaysOnTop or Enum.HighlightDepthMode.Occluded highlight.FillColor = color highlight.OutlineColor = color -- the slider moves the highlight between the style's own look (0) and fully clear (1) highlight.FillTransparency = math.clamp(spec.Fill + (1 - spec.Fill) * alpha * 0.5, 0, 1) highlight.OutlineTransparency = spec.Outline if spec.Material and visible then if spec.Strip then stripSkins(entry) end local tint = color if spec.Lift then tint = color:Lerp(Color3.new(1, 1, 1), spec.Lift) end for _, part in ipairs(entry.Parts) do if part.Parent then snapshotPart(entry, part) part.Material = spec.Material part.Color = tint if spec.Reflectance then part.Reflectance = spec.Reflectance end end end elseif entry.Snaps and next(entry.Snaps) ~= nil then -- behind cover the real parts are not seen anyway: give them back restoreParts(entry) end for _, box in ipairs(entry.Boxes) do box.Color3 = color end end -- players the ESP stopped calling for (off screen, too far, master switch off, left) get restored function Chams.Sweep() local now = os.clock() for player, entry in pairs(Chams.Entries) do if now - entry.StepAt > 0.25 or not entry.Character.Parent or player.Parent == nil then Chams.Clear(player) end end end -- ---------------------------------------------------------------- menu (called from the ESP tab builder) function Chams.BuildMenu(tab) local group = tab:AddRightGroupbox('Player Chams') group:AddToggle('ZPCT1', { Text = 'Player Chams', Default = false, Tooltip = 'Re-skins the players the ESP shows. Uses the Visible / Hidden / team colours of the ESP.', }) local box = group:AddDependencyBox() box:AddDropdown('ZPCD1', { Text = 'Style', Values = Chams.Names, Default = 'Glow', Multi = false, Tooltip = 'Flat: solid colour, no detail. Glow: emissive neon. Fresnel: bright rim, clear centre. Glass: see-through glossy. Chrome: mirror metal. Wireframe: a box around every body part. Forcefield: the animated forcefield shell in your colour.', }) box:AddSlider('ZPCS1', { Text = 'Clearness', Default = 0.3, Min = 0, Max = 1, Rounding = 2, Compact = true, Tooltip = '0 = the style at full strength, 1 = see-through.', }) box:AddToggle('ZPCT3', { Text = 'Custom Color', Default = false, Tooltip = 'Uses your own colours for the chams instead of the ESP Visible / Hidden / team colours.', }) local customColors = box:AddDependencyBox() customColors:AddLabel('Visible Color'):AddColorPicker('ZPCC1', { Default = Color3.fromRGB(125, 85, 255), Title = 'Chams Visible Color', }) customColors:AddLabel('Hidden Color'):AddColorPicker('ZPCC2', { Default = Color3.fromRGB(255, 70, 70), Title = 'Chams Hidden Color', }) customColors:SetupDependencies({ { Toggles.ZPCT3, true } }) box:AddToggle('ZPCT2', { Text = 'Through Walls', Default = true, Tooltip = 'Keeps the chams visible behind cover (the Hidden colour). Off = only where the player is in sight.', }) box:SetupDependencies({ { Toggles.ZPCT1, true } }) end if type(getgenv) == 'function' then local probe = getgenv().__ZkxHubTest if type(probe) == 'table' then probe.PlayerChams = Chams end end end -- ====================================================================== -- Custom sounds / skyboxes dialogs -- ====================================================================== RivalsRuntimeBridge.CombatFeedback.OpenCustomSoundsDialog = function() local feedback = RivalsRuntimeBridge.CombatFeedback local dialog = Window:AddDialog('Rivals_CustomSounds', { Title = 'Manage Custom Sounds', Description = 'Add a named Roblox sound ID, local file, or direct URL, or remove a saved sound.', AutoDismiss = false, OutsideClickDismiss = false, FooterButtons = { Close = { Title = 'Close', Variant = 'Ghost', Order = 1, Callback = function(activeDialog) activeDialog:Dismiss() end, }, Remove = { Title = 'Remove Selected', Variant = 'Destructive', Order = 2, Callback = GuardRivalsCallback('CombatFeedback_RemoveCustomSound', function() if feedback.RemoveCustomSound(Options.P1S17D1 and Options.P1S17D1.Value) then Options.P1S17D1:SetValue(nil) Library:Notify('Removed custom sound.') else Library:Notify('Select a saved custom sound to remove.') end end), }, Add = { Title = 'Add Sound', Variant = 'Primary', Order = 3, Callback = GuardRivalsCallback('CombatFeedback_AddCustomSound', function() if feedback.AddCustomSound( Options.P1S17I1 and Options.P1S17I1.Value, Options.P1S17I2 and Options.P1S17I2.Value ) then Options.P1S17I1:SetValue('') Options.P1S17I2:SetValue('') Library:Notify('Added custom sound.') else Library:Notify('Use a unique name and a valid sound source.') end end), }, }, }) dialog:AddInput('P1S17I1', { Default = '', Text = 'Name', AllowEmpty = true, EmptyReset = '', }) dialog:AddInput('P1S17I2', { Default = '', Text = 'Sound ID / File / URL', AllowEmpty = true, EmptyReset = '', }) dialog:AddDropdown('P1S17D1', { Values = feedback.ResolveUserSoundNames(), Multi = false, Text = 'Saved Sounds', Searchable = true, AllowNull = true, }) return dialog end RivalsRuntimeBridge.WorldVisuals.OpenCustomSkyboxesDialog = function() local world = RivalsRuntimeBridge.WorldVisuals local dialog = Window:AddDialog('Rivals_CustomSkyboxes', { Title = 'Manage Custom Skyboxes', Description = 'Give the skybox a unique name and provide all six Roblox image asset IDs.', AutoDismiss = false, OutsideClickDismiss = false, FooterButtons = { Close = { Title = 'Close', Variant = 'Ghost', Order = 1, Callback = function(activeDialog) activeDialog:Dismiss() end, }, Remove = { Title = 'Remove Selected', Variant = 'Destructive', Order = 2, Callback = GuardRivalsCallback('WorldVisuals_RemoveCustomSkybox', function() if world.RemoveSelectedCustomSkybox() then Library:Notify('Removed custom skybox.') else Library:Notify('Select a saved custom skybox to remove.') end end), }, Add = { Title = 'Add Skybox', Variant = 'Primary', Order = 3, Callback = GuardRivalsCallback('WorldVisuals_AddCustomSkybox', function() if world.AddCustomSkyboxFromOptions() then Library:Notify('Added custom skybox.') else Library:Notify('Use a unique name and provide all six valid image IDs.') end end), }, }, }) dialog:AddInput('P1S12I7', { Default = '', Text = 'Name', AllowEmpty = true, EmptyReset = '', }) dialog:AddInput('P1S12I1', { Default = '', Text = 'Front', AllowEmpty = true, EmptyReset = '', }) dialog:AddInput('P1S12I2', { Default = '', Text = 'Back', AllowEmpty = true, EmptyReset = '', }) dialog:AddInput('P1S12I3', { Default = '', Text = 'Left', AllowEmpty = true, EmptyReset = '', }) dialog:AddInput('P1S12I4', { Default = '', Text = 'Right', AllowEmpty = true, EmptyReset = '', }) dialog:AddInput('P1S12I5', { Default = '', Text = 'Up', AllowEmpty = true, EmptyReset = '', }) dialog:AddInput('P1S12I6', { Default = '', Text = 'Down', AllowEmpty = true, EmptyReset = '', }) dialog:AddDropdown('P1S12D2', { Values = world.ResolveCustomSkyNames(), Multi = false, Text = 'Saved Skyboxes', Searchable = true, AllowNull = true, }) return dialog end -- ====================================================================== -- Menu: ESP tab -- ====================================================================== BuildRivalsEspUi = function(Window) local espTab = Window:AddTab('ESP', 'eye') local P1S1 = espTab:AddLeftGroupbox('ESP Options') P1S1:AddToggle('P1S1T1', { Text = 'ESP Enabled', Default = true, Tooltip = 'Turns player ESP on.', }) P1S1:AddToggle('P1S1T2', { Text = 'Highlight', Default = true, Tooltip = 'Shows the player highlight.', }) P1S1:AddToggle('P1S2T1', { Text = 'Box', Default = true, Tooltip = 'Draws a visibility-colored box around the player.', }) P1S1:AddToggle('P1S1T3', { Text = 'Names', Default = true, Tooltip = 'Shows player names.', }) P1S1:AddToggle('P1S1T4', { Text = 'Health Info', Default = true, Tooltip = 'Shows the health bar and HP value.', }) P1S1:AddToggle('P1S1T7', { Text = 'Vertical Health', Default = false, Tooltip = 'Off uses the rounded health pill above the player. On uses the slim bar on the left.', }) P1S1:AddToggle('P1S1T9', { Text = 'Weapon Info', Default = true, Tooltip = 'Shows the equipped weapon and reloading state.', }) P1S1:AddToggle('P1S2T2', { Text = 'Ammo Text', Default = true, Tooltip = 'Shows current/max ammo below the box using the same style as Weapon Info.', }) P1S1:AddToggle('P1S1T6', { Text = 'Distance', Default = true, Tooltip = 'Shows distance in studs.', }) P1S1:AddToggle('P1S1T8', { Text = 'Look Direction', Default = true, Tooltip = 'Shows where players are aiming.', }) P1S1:AddToggle('P1S1T5', { Text = 'Arrows', Default = true, Tooltip = 'Screen-edge arrows that point toward off-screen players.', }) P1S1:AddToggle('P1S1T10', { Text = 'Show Teammates', Default = false, Tooltip = 'Shows teammates too.', }) P1S1:AddToggle('P1S1T11', { Text = 'Throwable ESP', Default = true, Tooltip = 'Shows throwables such as Molotov, Grenade, Flashbang, etc.', }) P1S1:AddLabel('Visible Color'):AddColorPicker('P1S1C1', { Default = Color3.fromRGB(0, 255, 0), Title = 'Visible ESP Color', }) P1S1:AddLabel('Hidden Color'):AddColorPicker('P1S1C2', { Default = Color3.fromRGB(255, 0, 0), Title = 'Hidden ESP Color', }) P1S1:AddLabel('Look Direction Color'):AddColorPicker('P1S1C4', { Default = Color3.fromRGB(255, 255, 0), Title = 'Look Direction Line Color', }) P1S1:AddLabel('Empty Ammo Color'):AddColorPicker('P1S2C1', { Default = Color3.fromRGB(255, 70, 70), Title = 'Empty / Reloading Ammo Color', }) P1S1:AddSlider('P1S1S3', { Text = 'Fill Opacity', Default = 1, Min = 0, Max = 1, Rounding = 2, Compact = true, Tooltip = '0 = solid fill (chams), 1 = no fill', }) P1S1:AddSlider('P1S1S4', { Text = 'Outline Opacity', Default = 0, Min = 0, Max = 1, Rounding = 2, Compact = true, Tooltip = '0 = solid outline, 1 = invisible outline', }) P1S1:AddToggle('P1S2T6', { Text = 'Deflecting', Default = true, Tooltip = 'Shows DEFLECTING while the player has an active Katana deflect.', }) P1S1:AddToggle('P1S2T4', { Text = 'Rank', Default = true, Tooltip = "Shows the player's competitive rank.", }) P1S1:AddToggle('P1S2T5', { Text = 'Win Streak', Default = true, Tooltip = 'Shows the current duel win streak. WS means Win Streak.', }) local PX9 = espTab:AddLeftGroupbox('Text Style') PX9:AddToggle('PX9T1', { Text = 'Use Display Name', Default = false, Tooltip = "Shows the player's display name instead of their username.", }) PX9:AddDropdown('PX9D1', { Values = { 'UI', 'System', 'Plex', 'Monospace' }, Default = 'Monospace', Multi = false, Text = 'Font', }) PX9:AddSlider('PX9S1', { Text = 'Font Size', Default = 16, Min = 8, Max = 32, Rounding = 0, Compact = true, }) PX9:AddDropdown('PX9D2', { Values = { 'Normal', 'UPPER', 'lower' }, Default = 'Normal', Multi = false, Text = 'Text Case', Tooltip = 'Applies to player names and weapon text.', }) PX9:AddDropdown('PX9D3', { Values = { 'None', '[ ]', '( )', '< >', '- -', '* *' }, Default = 'None', Multi = false, Text = 'Name Surround', }) PX9:AddToggle('PX9T2', { Text = 'Distance Scaling', Default = false, Tooltip = 'Makes ESP text smaller on far players and bigger on close ones.', }) local DepEspDistanceScaling = PX9:AddDependencyBox() DepEspDistanceScaling:AddSlider('PX9S2', { Text = 'Reference Distance', Default = 60, Min = 10, Max = 300, Rounding = 0, Suffix = ' m', Compact = true, Tooltip = 'Players at this distance use the normal font size.', }) DepEspDistanceScaling:SetupDependencies({ { Toggles.PX9T2, true } }) local P1S4 = espTab:AddRightGroupbox('Live Preview') RivalsRuntimeBridge.BuildEspPreview(P1S4) RivalsRuntimeBridge.PlayerChams.BuildMenu(espTab) RivalsRuntimeBridge.Extras.BuildEspUi(espTab) end -- ====================================================================== -- Anti-aim logic (RivalsAntihit) -- ====================================================================== (function() local RivalsAntihit = {} do local Players = game:GetService('Players') local RunServiceAH = game:GetService('RunService') local Workspace = workspace local LocalPlayer = Players.LocalPlayer local function tog(id) local t = Toggles[id] return t ~= nil and t.Value == true end local function opt(id, dflt) local o = Options[id] if o == nil or o.Value == nil then return dflt end return o.Value end local function viewAngleModule() local b = RivalsRuntimeBridge if b == nil then return nil end local va = b.ViewAngle or b.RageViewAngle or b.AntiAim if va and (va.forge or va.Forge) then return va end return nil end local VOID_MIN, VOID_MAX = 110000, 140000 local _voidCF = nil local function voidAxis() local v = math.random(VOID_MIN, VOID_MAX) return (math.random(0, 1) == 0) and -v or v end local function rollVoid() return CFrame.new(voidAxis(), math.random(VOID_MIN, VOID_MAX), voidAxis()) end local function nextVoidCF(minStep) local prev = _voidCF local cf, tries = rollVoid(), 0 while prev and tries < 8 and (cf.Position - prev.Position).Magnitude < minStep do cf = rollVoid() tries = tries + 1 end _voidCF = cf return cf end local _realCF = nil local _realChar = nil local _displaced = false local _spinYaw = 0 local _forgeNext = 0 local function isSane(p) return p == p and math.abs(p.X) < 1e9 and math.abs(p.Y) < 1e9 and math.abs(p.Z) < 1e9 end local function getRoot() local ch = LocalPlayer.Character if not ch then return nil, nil end local hrp = ch:FindFirstChild('HumanoidRootPart') if not hrp or not hrp.Parent then return nil, ch end return hrp, ch end local function isAlive() local ch = LocalPlayer.Character local hu = ch and ch:FindFirstChildOfClass('Humanoid') return hu ~= nil and hu.Health > 0 end local function anyEnabled() return tog('P11S1T1') or tog('P11S2T1') end local function resolveYaw(dt) local mode = opt('P11S2D1', 'Spin') if mode == 'Spin' then _spinYaw = (_spinYaw + math.rad(opt('P11S2S1', 720)) * (dt or 0)) % (math.pi * 2) return _spinYaw elseif mode == 'Jitter' then local deg = opt('P11S2S2', 160) return math.rad((math.random(0, 1) == 0) and -deg or deg) elseif mode == 'Backwards' then local cam = Workspace.CurrentCamera if not cam then return math.pi end local lv = cam.CFrame.LookVector return math.atan2(-lv.X, -lv.Z) + math.pi end return math.rad(math.random(0, 359)) end function RivalsAntihit.Replicate(dt) if (not anyEnabled()) or tog('P8S4T1') or (RivalsRuntimeBridge.ZHubRagebotActive ~= nil and RivalsRuntimeBridge.ZHubRagebotActive() == true) then if _displaced then RivalsAntihit.RestoreVisible() _displaced = false end return end local hrp, ch = getRoot() if not hrp then return end if not isAlive() then return end if (not _displaced) and isSane(hrp.Position) then _realCF, _realChar = hrp.CFrame, ch elseif _realChar ~= ch and isSane(hrp.Position) then _realCF, _realChar = hrp.CFrame, ch end local base = _realCF or hrp.CFrame local pos = base.Position local rot = base - base.Position if tog('P11S1T1') then pos = nextVoidCF(opt('P11S1S1', 25000)).Position end if tog('P11S2T1') then rot = CFrame.Angles(0, resolveYaw(dt), 0) end pcall(function() hrp.CFrame = CFrame.new(pos) * rot end) _displaced = true if tog('P11S2T1') and tog('P11S2T2') then local now = os.clock() if now >= _forgeNext then _forgeNext = now + 0.05 local va = viewAngleModule() if va then local forge = va.forge or va.Forge pcall(forge, math.rad(opt('P11S2S3', 0)), resolveYaw(dt)) end end else local va = viewAngleModule() if va and (va.isForging and va.isForging()) then pcall(va.restore or va.Restore) end end end function RivalsAntihit.RestoreVisible() if not _displaced then return end local hrp = getRoot() if not hrp then _displaced = false return end local real = _realCF if real and isSane(real.Position) then local before = hrp.Position pcall(function() hrp.CFrame = real end) if tog('P11S1T2') then local cam = Workspace.CurrentCamera if cam then local delta = real.Position - before if isSane(delta) then pcall(function() cam.CFrame = cam.CFrame + delta end) end end end end _displaced = false end function RivalsAntihit.Panic() local hrp = getRoot() if hrp and _realCF then pcall(function() hrp.CFrame = _realCF end) end _displaced = false local va = viewAngleModule() if va then pcall(va.restore or va.Restore) end end LocalPlayer.CharacterAdded:Connect(function() _displaced, _realCF, _realChar, _voidCF = false, nil, nil, nil end) function RivalsAntihit.Start() if RivalsAntihit._started then return end RivalsAntihit._started = true RunServiceAH:BindToRenderStep('ZkxAntihit_Restore', Enum.RenderPriority.First.Value - 1, function() local ok, err = pcall(RivalsAntihit.RestoreVisible) if not ok then warn('[Antihit] restore:', err) end end) RivalsAntihit._hb = RunServiceAH.Heartbeat:Connect(function(dt) local ok, err = pcall(RivalsAntihit.Replicate, dt) if not ok then warn('[Antihit] replicate:', err) end end) end function RivalsAntihit.Stop() RivalsAntihit._started = false pcall(function() RunServiceAH:UnbindFromRenderStep('ZkxAntihit_Restore') end) if RivalsAntihit._hb then RivalsAntihit._hb:Disconnect() RivalsAntihit._hb = nil end RivalsAntihit.Panic() end end -- ====================================================================== -- Menu: anti-aim -- ====================================================================== BuildRivalsAntihitUi = function(TargetTab) local AH = TargetTab local voidspamGroup = AH:AddLeftGroupbox('Voidspam') local voidToggle = voidspamGroup:AddToggle('P11S1T1', { Text = 'Enabled', Default = false, Tooltip = 'Throws your REPLICATED root far into the void every server tick and restores your visible position each frame. Incoming shots resolve against the void copy and miss. Loud + bannable - dodge tool, not always-on.', Callback = function(v) if not v then RivalsAntihit.Panic() end end, }) voidToggle:AddKeyPicker('P11S1T1K', { Default = 'Unknown', Mode = 'Toggle', Modes = { 'Toggle', 'Always' }, Text = 'Voidspam', NoUI = false, }) voidspamGroup:AddSlider('P11S1S1', { Text = 'Min Jump', Default = 25000, Min = 2000, Max = 120000, Rounding = 0, Suffix = ' studs', Compact = true, Tooltip = 'Minimum distance between consecutive void teleports.', }) voidspamGroup:AddToggle('P11S1T2', { Text = 'Camera Anchor', Default = false, Tooltip = 'Extra safety: corrects the camera by the restore delta. Usually not needed - restore already runs before the camera updates.', }) local antiAimGroup = AH:AddRightGroupbox('Anti-Aim') local aaToggle = antiAimGroup:AddToggle('P11S2T1', { Text = 'Enabled', Default = false, Tooltip = 'Spoofs the yaw your root replicates with so human aim, peek logic and animation aim-assists resolve the wrong facing.', Callback = function(v) if not v then RivalsAntihit.Panic() end end, }) aaToggle:AddKeyPicker('P11S2T1K', { Default = 'Unknown', Mode = 'Toggle', Modes = { 'Toggle', 'Always' }, Text = 'Anti-Aim', NoUI = false, }) antiAimGroup:AddDropdown('P11S2D1', { Values = { 'Spin', 'Jitter', 'Backwards', 'Random' }, Default = 'Spin', Multi = false, Text = 'Mode', Tooltip = 'Spin: continuous yaw. Jitter: snap +/-. Backwards: face away from camera. Random: fresh yaw each tick.', }) local DepSpin = antiAimGroup:AddDependencyBox() DepSpin:AddSlider('P11S2S1', { Text = 'Spin Speed', Default = 720, Min = 90, Max = 2000, Rounding = 0, Suffix = ' deg/s', Compact = true, }) DepSpin:SetupDependencies({ { Options.P11S2D1, 'Spin' } }) local DepJit = antiAimGroup:AddDependencyBox() DepJit:AddSlider('P11S2S2', { Text = 'Jitter Angle', Default = 160, Min = 10, Max = 180, Rounding = 0, Suffix = ' deg', Compact = true, }) DepJit:SetupDependencies({ { Options.P11S2D1, 'Jitter' } }) antiAimGroup:AddToggle('P11S2T2', { Text = 'Server Look Forge', Default = false, Tooltip = 'Also forges the replicated CameraRotationRaw through the ragebot ViewAngle module (if this build exposes it). Silent no-op when unavailable.', }) local DepPitch = antiAimGroup:AddDependencyBox() DepPitch:AddSlider('P11S2S3', { Text = 'Fake Pitch', Default = 0, Min = -89, Max = 89, Rounding = 0, Suffix = ' deg', Compact = true, }) DepPitch:SetupDependencies({ { Toggles.P11S2T2, true } }) return AH end RivalsAntihit.Start() end)() -- ====================================================================== -- Menu: Combat tab -- ====================================================================== BuildRivalsAimbotUi = function(Window) local combatTab = Window:AddTab('Combat', 'crosshair') local P2S1 = combatTab:AddLeftGroupbox('Aimbot') local aimbotToggle = P2S1:AddToggle('P2S1T1', { Text = 'Enabled', Default = true, Tooltip = 'Enables the selected Silent or Camera aiming mode.', }) local aimbotKeypicker = aimbotToggle:AddKeyPicker('P2S1T1K', { Default = 'Unknown', Mode = 'Always', Text = 'Aimbot', NoUI = false, }) RefreshAimbotEnabledToggleKeypickerState = AimbotBridge.SyncAimbotToggleKeypickerToUi(aimbotToggle, aimbotKeypicker) P2S1:AddDropdown('P2S1D1', { Values = { 'Silent', 'Camera' }, Default = 'Silent', Multi = false, Text = 'Mode', Tooltip = 'Silent aims your shots without moving your view. Camera smoothly turns your view toward the target.', }) local CameraAimSettings = P2S1:AddDependencyBox() CameraAimSettings:AddToggle('PX19T1', { Text = 'Only While Aiming', Default = true, Tooltip = 'On: the camera only turns toward the target while you hold aim (right mouse). Off: it turns toward the target without aiming, whenever the aimbot is enabled.', }) CameraAimSettings:AddSlider('P2S2S1', { Text = 'Aim Speed', Default = 35, Min = 1, Max = 100, Rounding = 0, Compact = true, Tooltip = 'How fast the view turns toward the target.', }) CameraAimSettings:AddDropdown('PX7D1', { Values = { 'Smooth', 'Linear', 'Spring', 'Adaptive' }, Default = 'Smooth', Multi = false, Text = 'Smoothing', Tooltip = 'Smooth is the classic blend. Linear turns at a constant rate. Spring eases in and settles. Adaptive is fast when far and gentle up close.', }) CameraAimSettings:SetupDependencies({ { Options.P2S1D1, 'Camera' } }) local SilentAimSettings = P2S1:AddDependencyBox() SilentAimSettings:AddSlider('P2S1S13', { Text = 'Hit Chance', Default = 100, Min = 1, Max = 100, Rounding = 0, Suffix = '%', Compact = true, Tooltip = 'Chance that each Silent shot gets aimed at the target. The other shots fire exactly where you are aiming, so the aim looks less perfect.', }) SilentAimSettings:SetupDependencies({ { Options.P2S1D1, 'Silent' } }) P2S1:AddToggle('P2S1T3', { Text = 'Ignore FOV', Default = true, Tooltip = 'Allows targets anywhere around you, even when they are off-screen.', }) local DepFovRadius = P2S1:AddDependencyBox() DepFovRadius:AddSlider('P2S1S2', { Text = 'FOV Radius', Default = 282, Min = 1, Max = 500, Rounding = 0, Compact = true, Tooltip = 'Targets must be inside this radius from your mouse.', }) DepFovRadius:SetupDependencies({ { Toggles.P2S1T3, false } }) P2S1:AddToggle('P2S1T5', { Text = 'Show FOV', Default = true, Tooltip = 'Displays the mouse-based FOV circle used for target selection.', }) P2S1:AddDropdown('P2S1D2', { Values = { 'Auto', 'Head', 'Random' }, Default = 'Auto', Multi = false, Text = 'Aim Part', Tooltip = 'Auto prefers head-style hitboxes when available.', }) local flickbotToggle = P2S1:AddToggle('P2S1T12', { Text = 'Flickbot', Default = false, Tooltip = 'Flicks to the target with a human-looking motion. Uses the Aimbot target and FOV settings.', Callback = function(value) if not value then RivalsRuntimeBridge.ResetFlickbot() end end, }) flickbotToggle:AddKeyPicker('P2S1T12K', { Default = 'Unknown', Mode = 'Hold', Text = 'Flickbot', NoUI = false, }) local FlickbotSettings = P2S1:AddDependencyBox() FlickbotSettings:AddToggle('P2S1T13', { Text = 'Shoot', Default = false, Tooltip = 'Fires automatically once the flick lands.', }) FlickbotSettings:AddSlider('P2S1S8', { Text = 'Shot Delay', Default = 0, Min = 0, Max = 250, Rounding = 0, Suffix = ' ms', Compact = true, }) FlickbotSettings:AddSlider('P2S1S9', { Text = 'Cooldown', Default = 250, Min = 0, Max = 2000, Rounding = 0, Suffix = ' ms', Compact = true, }) FlickbotSettings:AddSlider('P2S1S10', { Text = 'Flick Duration', Default = 110, Min = 30, Max = 400, Rounding = 0, Suffix = ' ms', Compact = true, }) FlickbotSettings:AddSlider('P2S1S11', { Text = 'Curvature', Default = 12, Min = 0, Max = 50, Rounding = 0, Compact = true, }) FlickbotSettings:AddSlider('P2S1S12', { Text = 'Humanness', Default = 30, Min = 0, Max = 100, Rounding = 0, Compact = true, }) FlickbotSettings:SetupDependencies({ { Toggles.P2S1T12, true } }) local triggerbotToggle = P2S1:AddToggle('P2S1T7', { Text = 'Triggerbot', Default = false, Tooltip = 'Automatically fires when a valid target is visible.', }) triggerbotToggle:AddKeyPicker('P2S1T7K', { Default = 'Unknown', Mode = 'Always', Text = 'Triggerbot', NoUI = false, }) P2S1:AddSlider('P2S1S7', { Text = 'Trigger Reaction Time', Default = 0, Min = 0, Max = 0.1, Rounding = 2, Suffix = ' s', Compact = true, Tooltip = 'How long a valid target must remain acquired before Triggerbot starts firing. Resets when the resolved target changes or drops.', }) P2S1:AddToggle('P2S1T8', { Text = 'Check Katana Deflect', Default = true, Tooltip = 'Skips Katana-deflecting targets unless your equipped weapon pierces deflect.', }) P2S1:AddToggle('P2S1T9', { Text = 'Check Riot Shield', Default = true, Tooltip = 'Skips targets whose Riot Shield is currently blocking your shot angle unless your equipped weapon pierces shields.', }) P2S1:AddDropdown('P2S1D4', { Values = { 'Flare Gun', 'Freeze Ray', 'Grenade Launcher', 'RPG' }, Default = { 'Flare Gun', 'Freeze Ray', 'Grenade Launcher', 'RPG' }, Multi = true, Text = 'Ignore Aimbot Weapons', Tooltip = 'Keeps aimbot off for the selected weapons so you can aim them manually.', }) local PX6 = combatTab:AddRightGroupbox('Targeting') PX6:AddSlider('PX6S1', { Text = 'Reaction Delay', Default = 0, Min = 0, Max = 1, Rounding = 2, Suffix = ' s', Compact = true, Tooltip = 'How long a new target must stay in view before the aimbot reacts to it. 0 is instant.', }) PX6:AddSlider('PX6S2', { Text = 'Switch Delay', Default = 0, Min = 0, Max = 1, Rounding = 2, Suffix = ' s', Compact = true, Tooltip = 'How long a different target must stay the best choice before the aimbot switches to it. 0 is instant.', }) PX6:AddDropdown('PX6D2', { Values = { 'Crosshair', 'Lowest Health', 'Nearest', 'Cheaters First' }, Default = 'Crosshair', Multi = false, Text = 'Target Priority', Tooltip = 'Who the aimbot picks first. Crosshair is the classic closest-to-aim choice. Cheaters First needs the Cheater Detector on.', }) PX6:AddToggle('PX6T1', { Text = 'Target Lock', Default = false, Tooltip = 'Stays on your current target instead of jumping to whoever is closest, until they die or leave your view.', }) local DepTargetLock = PX6:AddDependencyBox() DepTargetLock:AddDropdown('PX6D1', { Values = { 'While Aiming', 'Until Invalid' }, Default = 'While Aiming', Multi = false, Text = 'Lock Mode', Tooltip = 'While Aiming drops the lock when you let go of aim (with Only While Aiming off in Camera mode there is no aim to let go of, so it behaves like Until Invalid). Until Invalid keeps it until the target dies or becomes invalid.', }) DepTargetLock:SetupDependencies({ { Toggles.PX6T1, true } }) PX6:AddDivider() PX6:AddToggle('PX8T1', { Text = 'Targeting Circle', Default = false, Tooltip = 'Draws a ring on the target the aimbot is on.', }) local DepTargetCircle = PX6:AddDependencyBox() DepTargetCircle:AddLabel('Circle Color'):AddColorPicker('PX8C1', { Default = Color3.fromRGB(255, 70, 70), Title = 'Targeting Circle Color', }) DepTargetCircle:AddSlider('PX8S1', { Text = 'Radius', Default = 18, Min = 4, Max = 80, Rounding = 0, Suffix = ' px', Compact = true, }) DepTargetCircle:AddSlider('PX8S2', { Text = 'Thickness', Default = 2, Min = 1, Max = 8, Rounding = 0, Compact = true, }) DepTargetCircle:AddToggle('PX8T2', { Text = 'Filled', Default = false }) DepTargetCircle:AddToggle('PX8T3', { Text = 'Pulse', Default = false }) DepTargetCircle:AddSlider('PX8S3', { Text = 'Pulse Speed', Default = 3, Min = 0.5, Max = 12, Rounding = 1, Compact = true, }) DepTargetCircle:SetupDependencies({ { Toggles.PX8T1, true } }) PX6:AddToggle('PX8T4', { Text = 'Snapline', Default = false, Tooltip = 'Draws a line from your crosshair to the part Aimbot or Silent Aim is on (head, torso ...). It shows as soon as you aim or fire, and with the Silent Aim rule that needs the scope up, the moment the scope is up.', }) local DepSnapline = PX6:AddDependencyBox() DepSnapline:AddLabel('Line Color'):AddColorPicker('PX8C2', { Default = Color3.fromRGB(255, 255, 255), Title = 'Snapline Color', }) DepSnapline:AddToggle('PX8T5', { Text = 'Rainbow', Default = false }) DepSnapline:AddSlider('PX8S4', { Text = 'Rainbow Speed', Default = 3, Min = 0.5, Max = 12, Rounding = 1, Compact = true, }) DepSnapline:AddSlider('PX8S5', { Text = 'Thickness', Default = 1, Min = 1, Max = 6, Rounding = 0, Compact = true, }) DepSnapline:AddSlider('PX8S6', { Text = 'Smoothing', Default = 35, Min = 0, Max = 100, Rounding = 0, Compact = true, Tooltip = 'The line always follows the target without delay. Smoothing only shapes how it appears: it slides between targets and fades in and out. 0 = hard snap.', }) DepSnapline:AddDropdown('PX8D1', { Text = 'From', Values = { 'Crosshair', 'Mouse', 'Bottom', 'Top' }, Default = 'Crosshair', Multi = false, }) DepSnapline:SetupDependencies({ { Toggles.PX8T4, true } }) local PX14 = combatTab:AddRightGroupbox('Combat Bot') local combatBotToggle = PX14:AddToggle('PX14T1', { Text = 'Combat Bot', Default = false, Tooltip = 'Fights on its own: aims and fires through your normal Silent Aim and Triggerbot settings, manages ammo, and dodges with ordinary movement. Only acts while you can actually fight.', }) combatBotToggle:AddKeyPicker('PX14T1K', { Default = 'Unknown', Mode = 'Always', Text = 'Combat Bot', NoUI = false, }) PX14:AddToggle('PX14T3', { Text = 'Manage Weapons', Default = true, Tooltip = 'Reloads or swaps to a loaded gun when your magazine runs empty.', }) PX14:AddDropdown('PX14D2', { Values = { 'Reload', 'Swap' }, Default = 'Reload', Multi = false, Text = 'On Empty', Tooltip = 'Reload presses R. Swap switches to your next gun that still has ammo, and reloads if none does.', }) PX14:AddDropdown('PX14D3', { Values = { 'Off', 'Strafe', 'Random' }, Default = 'Strafe', Multi = false, Text = 'Evasion', Tooltip = 'Strafe moves side to side. Random also steps forward and back. Only while a target is engaged and you are not steering yourself.', }) PX14:AddSlider('PX14S1', { Text = 'Direction Change', Default = 0.45, Min = 0.1, Max = 2, Rounding = 2, Suffix = ' s', Compact = true, Tooltip = 'Average time between direction changes.', }) PX14:AddSlider('PX14S2', { Text = 'Jump Chance', Default = 30, Min = 0, Max = 100, Rounding = 0, Suffix = '%', Compact = true, Tooltip = 'Chance of a hop each time the bot considers one.', }) PX14:AddToggle('PX14T2', { Text = 'Health Lead', Default = true, Tooltip = 'Dodges faster when you are behind on health and calms down when you are well ahead.', }) RivalsRuntimeBridge.Extras.BuildCombatUi(combatTab) return combatTab end -- ====================================================================== -- Menu: weapon mods -- ====================================================================== BuildRivalsModsUi = function(CombatTab) local P4S1 = CombatTab:AddRightGroupbox('Weapon Mods') P4S1:AddToggle('P4S1T1', { Text = 'Weapon Attack Speed', Default = true, Tooltip = 'Speeds up your shots and melee swings. If something starts feeling inconsistent, back the slider down a bit.', Callback = RivalsModsState.EnsureHooks, }) local DepWeaponSpeed = P4S1:AddDependencyBox() DepWeaponSpeed:AddSlider('P4S1S4', { Text = 'Speed Boost', Default = 85, Min = 0, Max = 85, Rounding = 0, Suffix = '%', Compact = true, Tooltip = 'Higher values make attacks come out faster, up to the 85 percent cap.', }) DepWeaponSpeed:SetupDependencies({ { Toggles.P4S1T1, true } }) P4S1:AddToggle('P4S1T2', { Text = 'Faster Reload', Default = true, Tooltip = 'Shortens reloads so you can get back into fights faster.', Callback = RivalsModsState.EnsureHooks, }) local DepReload = P4S1:AddDependencyBox() DepReload:AddSlider('P4S1S3', { Text = 'Reload Faster', Default = 20, Min = 0, Max = 20, Rounding = 0, Suffix = '%', Compact = true, Tooltip = 'Higher values shorten reload time more aggressively.', }) DepReload:SetupDependencies({ { Toggles.P4S1T2, true } }) P4S1:AddToggle('P4S1T3', { Text = 'No Spread', Default = false, Tooltip = 'Removes gun spread so your shots go exactly where you aim.', Callback = RivalsModsState.EnsureHooks, }) P4S1:AddToggle('P4S1T4', { Text = 'No Recoil', Default = false, Tooltip = 'Reduces recoil so tracking feels steadier.', Callback = RivalsModsState.EnsureHooks, }) local DepRecoil = P4S1:AddDependencyBox() DepRecoil:AddSlider('P4S1S2', { Text = 'Recoil Reduction', Default = 100, Min = 0, Max = 100, Rounding = 0, Suffix = '%', Compact = true, Tooltip = 'Higher values calm the kick more.', }) DepRecoil:SetupDependencies({ { Toggles.P4S1T4, true } }) P4S1:AddToggle('P4S2T2', { Text = 'Faster ADS', Default = true, Tooltip = 'Makes aiming in feel quicker.', Callback = RivalsModsState.EnsureHooks, }) local DepFasterAds = P4S1:AddDependencyBox() DepFasterAds:AddSlider('P4S2S2', { Text = 'ADS Faster', Default = 85, Min = 0, Max = 85, Rounding = 0, Suffix = '%', Compact = true, Tooltip = 'Higher values speed up aim-in more.', }) DepFasterAds:SetupDependencies({ { Toggles.P4S2T2, true } }) P4S1:AddToggle('P4S2T3', { Text = 'Faster Equip', Default = true, Tooltip = 'Cuts down the delay when swapping to a weapon.', Callback = RivalsModsState.EnsureHooks, }) local DepFasterEquip = P4S1:AddDependencyBox() DepFasterEquip:AddSlider('P4S2S3', { Text = 'Equip Faster', Default = 80, Min = 0, Max = 80, Rounding = 0, Suffix = '%', Compact = true, Tooltip = 'Higher values make weapon swaps finish sooner.', }) DepFasterEquip:SetupDependencies({ { Toggles.P4S2T3, true } }) P4S1:AddToggle('P4S1T5', { Text = 'Scythe Dash Cooldown', Default = false, Tooltip = 'Shortens the Scythe dash cooldown.', Callback = RivalsModsState.EnsureHooks, }) local DepDashCooldown = P4S1:AddDependencyBox() DepDashCooldown:AddSlider('P4S1S5', { Text = 'Cooldown Reduction', Default = 75, Min = 0, Max = 100, Rounding = 0, Suffix = '%', Compact = true, Tooltip = 'How much of the dash cooldown is removed. 100% removes the cooldown entirely.', Callback = RivalsModsState.EnsureHooks, }) DepDashCooldown:SetupDependencies({ { Toggles.P4S1T5, true } }) P4S1:AddToggle('KE_Enabled', { Text = 'Knife Extender', Default = false, Tooltip = "Lets knife swings land on targets past the knife's normal reach by moving the swing forward before it is sent. The server may still reject hits that are too far away.", Callback = RivalsModsState.EnsureHooks, }) local DepKnifeExtender = P4S1:AddDependencyBox() DepKnifeExtender:AddSlider('KE_Range', { Text = 'Extra Range', Default = 3, Min = 1, Max = 40, Rounding = 1, Suffix = ' studs', Compact = true, Tooltip = "How many studs past the weapon's normal reach a swing may land. Lower values look more natural.", Callback = RivalsModsState.EnsureHooks, }) DepKnifeExtender:AddDropdown('KE_Scope', { Values = { 'Knife', 'All melee' }, Default = 'Knife', Multi = false, Text = 'Weapons', Tooltip = 'Knife: only the Knife. All melee: every melee weapon.', Callback = RivalsModsState.EnsureHooks, }) DepKnifeExtender:AddToggle('KE_Heavy', { Text = 'Heavy Attack Too', Default = true, Tooltip = 'Also extends the heavy attack (the backstab swing).', Callback = RivalsModsState.EnsureHooks, }) DepKnifeExtender:SetupDependencies({ { Toggles.KE_Enabled, true } }) P4S1:AddToggle('P4S2T4', { Text = 'Automatic Weapon', Default = false, Tooltip = 'Lets semi-auto weapons keep firing while you hold the trigger.', Callback = RivalsModsState.EnsureHooks, }) P4S1:AddToggle('P4S1T6', { Text = 'Grenade Fuse', Default = false, Tooltip = 'Controls when cookable throwables detonate.', Callback = RivalsModsState.EnsureHooks, }) local DepGrenadeFuse = P4S1:AddDependencyBox() DepGrenadeFuse:AddDropdown('P4S1D1', { Values = { 'Impact', 'Throw' }, Default = 'Impact', Text = 'Explode On', Tooltip = 'Impact detonates where the throw arc lands; Throw detonates the moment it leaves your hand.', Callback = RivalsModsState.EnsureHooks, }) DepGrenadeFuse:AddToggle('P4S1T7', { Text = 'Remove Fuse', Default = false, Tooltip = 'Stops a cooked throwable from detonating in your hand.', Callback = RivalsModsState.EnsureHooks, }) DepGrenadeFuse:SetupDependencies({ { Toggles.P4S1T6, true } }) end -- ====================================================================== -- Menu: Client Effects, Crosshair and World tabs -- ====================================================================== BuildRivalsVisualsUi = function(Window) local VisualsTab = Window:AddTab('Client Effects', 'camera') local CrosshairTab = Window:AddTab('Crosshair', 'plus') local WorldTab = Window:AddTab('World', 'globe') local SoundGroup = VisualsTab:AddLeftGroupbox('Sound Visualizer') local soundPulsesToggle = SoundGroup:AddToggle('P1S3T1', { Text = 'Sound Pulses', Default = false, Tooltip = 'Shows expanding pulses where sounds come from.', }) soundPulsesToggle:OnChanged( GuardRivalsCallback( 'SoundVisualizer_ToggleChanged', function(value) WorldESPState.SetSoundVisualizerEnabled(value) end ) ) SoundGroup:AddDropdown('P1S3D1', { Values = { 'Enemies', 'Team', 'Self', 'All' }, Default = 'Enemies', Multi = false, Text = 'Source', Tooltip = 'Whose sounds create pulses.', }) SoundGroup:AddToggle('P1S3T2', { Text = 'Footsteps', Default = true, }) SoundGroup:AddToggle('P1S3T3', { Text = 'Other Sounds', Default = true, }) SoundGroup:AddSlider('P1S3S1', { Text = 'Minimum Volume', Default = 0.05, Min = 0, Max = 1, Rounding = 2, Compact = true, Tooltip = 'Ignores sounds quieter than this.', }) SoundGroup:AddLabel('Footstep Color'):AddColorPicker('P1S3C1', { Default = Color3.fromRGB(255, 210, 80), Title = 'Footstep Pulse Color', }) SoundGroup:AddLabel('Other Color'):AddColorPicker('P1S3C2', { Default = Color3.fromRGB(255, 90, 90), Title = 'Other Sound Pulse Color', }) local feedback = RivalsRuntimeBridge.CombatFeedback local customSoundNames = feedback.ResolveCustomSoundNames() local HitFeedbackGroup = VisualsTab:AddLeftGroupbox('Hit Feedback') local hitFeedbackToggle = HitFeedbackGroup:AddToggle('P1S15T1', { Text = 'Hit Feedback', Default = false, Tooltip = 'Plays effects when you damage someone: sound and chams.', }) hitFeedbackToggle:OnChanged(GuardRivalsCallback('CombatFeedback_HitToggle', feedback.RefreshBinding)) local HitSections = HitFeedbackGroup:AddDropdown('KPHSEC', { Text = 'Sections', Values = { 'Sound', 'Chams', 'Flash', 'Text' }, Default = { 'Sound' }, Multi = true, Tooltip = 'Tick the parts of the feedback you want to see and edit.', }) local HitSoundTab = HitFeedbackGroup:AddDependencyBox() local HitChamsTab = HitFeedbackGroup:AddDependencyBox() local HitFlashTab = HitFeedbackGroup:AddDependencyBox() local HitTextTab = HitFeedbackGroup:AddDependencyBox() HitSoundTab:AddToggle('P1S15T2', { Text = 'Play Sound', Default = false, Tooltip = 'Hitmarker sound. Headshots and body shots can use different sounds from Custom Sounds.', }) local HitSoundSettings = HitSoundTab:AddDependencyBox() HitSoundSettings:AddDropdown('P1S15D1', { Values = customSoundNames, Multi = false, Text = 'Head Sound', Searchable = true, }) HitSoundSettings:AddSlider('P1S15S1', { Text = 'Head Volume', Default = 1, Min = 0.1, Max = 1.5, Rounding = 1, Compact = true, }) HitSoundSettings:AddSlider('P1S15S2', { Text = 'Head Pitch', Default = 1, Min = 0.1, Max = 3, Rounding = 1, Compact = true, }) HitSoundSettings:AddDropdown('P1S15D2', { Values = customSoundNames, Multi = false, Text = 'Body Sound', Searchable = true, }) HitSoundSettings:AddSlider('P1S15S3', { Text = 'Body Volume', Default = 1, Min = 0.1, Max = 1.5, Rounding = 1, Compact = true, }) HitSoundSettings:AddSlider('P1S15S4', { Text = 'Body Pitch', Default = 1, Min = 0.1, Max = 3, Rounding = 1, Compact = true, }) HitSoundSettings:SetupDependencies({ { Toggles.P1S15T2, true } }) HitSoundTab:AddToggle('P1S15T3', { Text = 'Disable Game Sound', Default = false, Tooltip = "Mutes the game's own hitmarker sound so only your custom hit sound plays.", }):OnChanged(GuardRivalsCallback('NativeRemovals_GameHitsound', RivalsRuntimeBridge.NativeRemovals.RefreshAll)) HitSoundTab:SetupDependencies({ { Options.KPHSEC, 'Sound' } }) HitChamsTab:AddToggle('P1S15T4', { Text = 'Chams', Default = false, Tooltip = 'Briefly highlights the body of the player you hit.', }) local HitChamsSettings = HitChamsTab:AddDependencyBox() HitChamsSettings:AddLabel('Cham Color'):AddColorPicker('P1S15C1', { Default = Color3.fromRGB(255, 0, 0), Transparency = 0, Title = 'Hit Cham Color', }) HitChamsSettings:AddSlider('P1S15S5', { Text = 'Cham Duration', Default = 0.8, Min = 0.1, Max = 3, Rounding = 1, Compact = true, }) HitChamsSettings:AddDropdown('P1S15D3', { Values = feedback.MaterialNames, Default = 'Ghost', Multi = false, Text = 'Cham Material', }) HitChamsSettings:SetupDependencies({ { Toggles.P1S15T4, true } }) HitChamsTab:SetupDependencies({ { Options.KPHSEC, 'Chams' } }) HitFlashTab:AddToggle('PX4T1', { Text = 'Screen Flash', Default = false, Tooltip = 'Briefly tints the whole screen when you hit someone.', }) local HitFlashSettings = HitFlashTab:AddDependencyBox() HitFlashSettings:AddLabel('Flash Color'):AddColorPicker('PX4C1', { Default = Color3.fromRGB(255, 255, 255), Title = 'Hit Flash Color', }) HitFlashSettings:AddSlider('PX4S1', { Text = 'Flash Intensity', Default = 0.3, Min = 0.05, Max = 1, Rounding = 2, Compact = true, }) HitFlashSettings:AddSlider('PX4S2', { Text = 'Flash Duration', Default = 0.15, Min = 0.05, Max = 1, Rounding = 2, Compact = true, }) HitFlashSettings:SetupDependencies({ { Toggles.PX4T1, true } }) HitFlashTab:SetupDependencies({ { Options.KPHSEC, 'Flash' } }) HitTextTab:AddToggle('PX4T2', { Text = 'Notification', Default = false, Tooltip = 'Shows who you hit and for how much.', }) local HitTextSettings = HitTextTab:AddDependencyBox() HitTextSettings:AddInput('KPF2', { Text = 'Notification Text', Default = '', Placeholder = 'Hit %DNAME% (%NAME%) for %DMG% in %PART%', AllowEmpty = true, EmptyReset = '', Tooltip = 'Leave empty for the default text. Tokens: %NAME% %DNAME% %DMG% %PART%', }) HitTextSettings:SetupDependencies({ { Toggles.PX4T2, true } }) HitTextTab:SetupDependencies({ { Options.KPHSEC, 'Text' } }) RivalsRuntimeBridge.HitLog.BuildMenu(VisualsTab) RivalsRuntimeBridge.NameStyle.BuildMenu(VisualsTab) local KillFeedbackGroup = VisualsTab:AddRightGroupbox('Kill Feedback') local killFeedbackToggle = KillFeedbackGroup:AddToggle('P1S16T1', { Text = 'Kill Feedback', Default = false, Tooltip = 'Plays effects when you eliminate someone: sound and chams.', }) killFeedbackToggle:OnChanged(GuardRivalsCallback('CombatFeedback_KillToggle', feedback.RefreshBinding)) local KillSections = KillFeedbackGroup:AddDropdown('KPKSEC', { Text = 'Sections', Values = { 'Sound', 'Chams', 'Flash', 'Text' }, Default = { 'Sound' }, Multi = true, Tooltip = 'Tick the parts of the feedback you want to see and edit.', }) local KillSoundTab = KillFeedbackGroup:AddDependencyBox() local KillChamsTab = KillFeedbackGroup:AddDependencyBox() local KillFlashTab = KillFeedbackGroup:AddDependencyBox() local KillTextTab = KillFeedbackGroup:AddDependencyBox() KillSoundTab:AddToggle('P1S16T2', { Text = 'Play Sound', Default = false, Tooltip = 'Plays a sound on every elimination. Add your own in Custom Sounds.', }) local KillSoundSettings = KillSoundTab:AddDependencyBox() KillSoundSettings:AddDropdown('P1S16D1', { Values = customSoundNames, Multi = false, Text = 'Sound', Searchable = true, }) KillSoundSettings:AddSlider('P1S16S1', { Text = 'Volume', Default = 1, Min = 0.1, Max = 1.5, Rounding = 1, Compact = true, }) KillSoundSettings:AddSlider('P1S16S2', { Text = 'Pitch', Default = 1, Min = 0.1, Max = 3, Rounding = 1, Compact = true, }) KillSoundSettings:SetupDependencies({ { Toggles.P1S16T2, true } }) KillSoundTab:SetupDependencies({ { Options.KPKSEC, 'Sound' } }) KillChamsTab:AddToggle('P1S16T3', { Text = 'Chams', Default = false, Tooltip = 'Briefly highlights the body of the player you eliminated.', }) local KillChamsSettings = KillChamsTab:AddDependencyBox() KillChamsSettings:AddLabel('Cham Color'):AddColorPicker('P1S16C1', { Default = Color3.fromRGB(255, 0, 0), Transparency = 0, Title = 'Kill Cham Color', }) KillChamsSettings:AddSlider('P1S16S3', { Text = 'Cham Duration', Default = 0.8, Min = 0.1, Max = 3, Rounding = 1, Compact = true, }) KillChamsSettings:AddDropdown('P1S16D2', { Values = feedback.MaterialNames, Default = 'Ghost', Multi = false, Text = 'Cham Material', }) KillChamsSettings:SetupDependencies({ { Toggles.P1S16T3, true } }) KillChamsTab:SetupDependencies({ { Options.KPKSEC, 'Chams' } }) KillFlashTab:AddToggle('PX5T1', { Text = 'Screen Flash', Default = false, Tooltip = 'Briefly tints the whole screen when you eliminate someone.', }) local KillFlashSettings = KillFlashTab:AddDependencyBox() KillFlashSettings:AddLabel('Flash Color'):AddColorPicker('PX5C1', { Default = Color3.fromRGB(255, 60, 60), Title = 'Kill Flash Color', }) KillFlashSettings:AddSlider('PX5S1', { Text = 'Flash Intensity', Default = 0.4, Min = 0.05, Max = 1, Rounding = 2, Compact = true, }) KillFlashSettings:AddSlider('PX5S2', { Text = 'Flash Duration', Default = 0.3, Min = 0.05, Max = 1.5, Rounding = 2, Compact = true, }) KillFlashSettings:SetupDependencies({ { Toggles.PX5T1, true } }) KillFlashTab:SetupDependencies({ { Options.KPKSEC, 'Flash' } }) KillTextTab:AddToggle('PX5T2', { Text = 'Notification', Default = false, Tooltip = 'Shows who you eliminated.', }) local KillTextSettings = KillTextTab:AddDependencyBox() KillTextSettings:AddInput('KPF4', { Text = 'Notification Text', Default = '', Placeholder = 'Eliminated %DNAME% (%NAME%)', AllowEmpty = true, EmptyReset = '', Tooltip = 'Leave empty for the default text. Tokens: %NAME% %DNAME%', }) KillTextSettings:SetupDependencies({ { Toggles.PX5T2, true } }) KillTextTab:SetupDependencies({ { Options.KPKSEC, 'Text' } }) local CustomSoundsGroup = VisualsTab:AddLeftGroupbox('Custom Sounds') CustomSoundsGroup:AddButton({ Text = 'Manage Custom Sounds', Func = GuardRivalsCallback('CombatFeedback_OpenCustomSoundsDialog', feedback.OpenCustomSoundsDialog), Tooltip = 'Add and remove named sound IDs, local files, and direct URLs in a focused dialog.', }) local BulletTracerGroup = VisualsTab:AddLeftGroupbox('Bullet Tracers') local bulletTracerToggle = BulletTracerGroup:AddToggle('P1S18T1', { Text = 'Bullet Tracers', Default = false, Tooltip = "Draws a glowing beam along every shot - yours and everyone else's.", }) bulletTracerToggle:OnChanged(GuardRivalsCallback('BulletTracers_Toggle', feedback.RefreshBinding)) BulletTracerGroup:AddLabel('Color'):AddColorPicker('P1S18C1', { Default = Color3.fromRGB(120, 220, 255), Title = 'Bullet Tracer Color', }) BulletTracerGroup:AddDropdown('P1S18D1', { Values = feedback.TracerStyleNames, Default = 'Beam', Multi = false, Text = 'Style', Tooltip = 'Look of the beam. Styles other than Beam use a repeating pattern.', }) BulletTracerGroup:AddSlider('P1S18S1', { Text = 'Size', Default = 0.06, Min = 0.02, Max = 0.5, Rounding = 2, Compact = true, Tooltip = 'Beam thickness.', }) BulletTracerGroup:AddSlider('P1S18S2', { Text = 'Length', Default = 4, Min = 0.5, Max = 20, Rounding = 1, Compact = true, Tooltip = 'Length of one pattern segment for the textured styles.', }) BulletTracerGroup:AddSlider('P1S18S3', { Text = 'Speed', Default = 1, Min = 0, Max = 10, Rounding = 1, Compact = true, Tooltip = 'How fast the pattern scrolls along the beam.', }) BulletTracerGroup:AddSlider('P1S18S4', { Text = 'Emission', Default = 1, Min = 0, Max = 1, Rounding = 2, Compact = true, Tooltip = "How much the beam glows over what's behind it. 0 = solid, 1 = full glow.", }) BulletTracerGroup:AddSlider('P1S18S5', { Text = 'Glow', Default = 1, Min = 1, Max = 13, Rounding = 0, Compact = true, Tooltip = 'Beam brightness.', }) BulletTracerGroup:AddSlider('P1S18S6', { Text = 'Lifetime', Default = 0.6, Min = 0.1, Max = 0.7, Rounding = 1, Compact = true, }) BulletTracerGroup:AddSlider('P1S18S7', { Text = 'Fade Time', Default = 0.35, Min = 0, Max = 0.5, Rounding = 1, Compact = true, }) BulletTracerGroup:AddToggle('P1S18T2', { Text = 'Spring Expand', Default = true, Tooltip = 'The beam shoots out from the gun to the hit point with a springy stretch instead of appearing all at once.', }) local ExpandSettings = BulletTracerGroup:AddDependencyBox() ExpandSettings:AddSlider('P1S18S8', { Text = 'Expand Speed', Default = 18, Min = 1, Max = 60, Rounding = 0, Compact = true, Tooltip = 'How fast the beam stretches to the hit point.', }) ExpandSettings:AddSlider('P1S18S9', { Text = 'Expand Damper', Default = 0.7, Min = 0.1, Max = 1, Rounding = 2, Compact = true, Tooltip = 'How much the stretch bounces. 1 = smooth stop, lower = springier overshoot.', }) ExpandSettings:SetupDependencies({ { Toggles.P1S18T2, true } }) do local MotionBlurGroup = VisualsTab:AddLeftGroupbox('Motion Blur') MotionBlurGroup:AddToggle('PX2T1', { Text = 'Motion Blur', Default = false, Tooltip = 'Blurs the screen while your camera turns or moves fast.', }) local DepMotionBlur = MotionBlurGroup:AddDependencyBox() DepMotionBlur:AddDropdown('PX2D1', { Values = { 'Camera + Movement', 'Camera Turning', 'Movement' }, Default = 'Camera + Movement', Multi = false, Text = 'Blur From', Tooltip = 'What builds up the blur: turning the camera, moving, or both.', }) DepMotionBlur:AddSlider('PX2S1', { Text = 'Intensity', Default = 14, Min = 1, Max = 56, Rounding = 0, Compact = true, Tooltip = 'Strongest blur reached during a hard flick.', }) DepMotionBlur:AddSlider('PX2S2', { Text = 'Sensitivity', Default = 1, Min = 0.1, Max = 4, Rounding = 2, Compact = true, Tooltip = 'How quickly camera motion builds up blur.', }) DepMotionBlur:AddSlider('PX2S3', { Text = 'Threshold', Default = 0, Min = 0, Max = 0.9, Rounding = 2, Compact = true, Tooltip = 'Ignores slow motion. Raise it so only fast flicks blur.', }) DepMotionBlur:AddSlider('PX2S4', { Text = 'Build-Up Speed', Default = 14, Min = 1, Max = 60, Rounding = 0, Compact = true, Tooltip = 'How fast the blur ramps up. Lower = the blur creeps in.', }) DepMotionBlur:AddSlider('PX2S5', { Text = 'Release Speed', Default = 14, Min = 1, Max = 60, Rounding = 0, Compact = true, Tooltip = 'How fast the blur fades out. Lower = the blur lingers.', }) DepMotionBlur:SetupDependencies({ { Toggles.PX2T1, true } }) local CameraTiltGroup = VisualsTab:AddLeftGroupbox('Camera Tilt') CameraTiltGroup:AddToggle('PX17T1', { Text = 'Camera Tilt', Default = false, Tooltip = 'Rolls your camera into the strafe while you hold A or D.', }) local DepCameraTilt = CameraTiltGroup:AddDependencyBox() DepCameraTilt:AddSlider('PX17S1', { Text = 'Tilt Angle', Default = 5, Min = 0, Max = 20, Rounding = 1, Suffix = ' deg', Compact = true, }) DepCameraTilt:AddSlider('PX17S2', { Text = 'Smoothness', Default = 10, Min = 2, Max = 30, Rounding = 0, Compact = true, Tooltip = 'Higher snaps in and out faster.', }) DepCameraTilt:AddToggle('PX17T2', { Text = 'Invert', Default = false, Tooltip = 'Tilts away from the strafe instead of into it.', }) DepCameraTilt:SetupDependencies({ { Toggles.PX17T1, true } }) RivalsRuntimeBridge.CameraTilt.Start() local FreecamGroup = VisualsTab:AddLeftGroupbox('Freecam') local freecamToggle = FreecamGroup:AddToggle('PX18T1', { Text = 'Freecam', Default = false, Tooltip = 'Detaches the camera so you can fly around. WASD moves, Space or E rises, Ctrl or Q lowers, Shift boosts. Your character stays where it is and can still be hit.', }) freecamToggle:AddKeyPicker('PX18T1K', { Default = 'Unknown', Mode = 'Toggle', Text = 'Freecam', NoUI = false, SyncToggleState = true, }) freecamToggle:OnChanged( GuardRivalsCallback('Freecam_Toggle', function(value) RivalsRuntimeBridge.Freecam.Set(value) end) ) local DepFreecam = FreecamGroup:AddDependencyBox() DepFreecam:AddSlider('PX18S1', { Text = 'Speed', Default = 60, Min = 5, Max = 400, Rounding = 0, Suffix = ' st/s', Compact = true, }) DepFreecam:AddSlider('PX18S2', { Text = 'Boost (Shift)', Default = 3, Min = 1, Max = 10, Rounding = 1, Suffix = 'x', Compact = true, }) DepFreecam:AddSlider('PX18S3', { Text = 'Look Sensitivity', Default = 1, Min = 0.1, Max = 5, Rounding = 2, Compact = true, }) DepFreecam:AddSlider('PX18S4', { Text = 'Smoothing', Default = 14, Min = 0, Max = 40, Rounding = 0, Compact = true, Tooltip = 'Glide for movement and looking. 0 = instant.', }) DepFreecam:AddSlider('PX18S5', { Text = 'Zoom', Default = 1, Min = 0.5, Max = 8, Rounding = 2, Suffix = 'x', Compact = true, Tooltip = 'Narrows the field of view relative to the camera you left.', }) DepFreecam:SetupDependencies({ { Toggles.PX18T1, true } }) local ReportGroup = VisualsTab:AddLeftGroupbox('Executor Report') ReportGroup:AddLabel('A camera or viewmodel feature does nothing? Press this and send the result.') ReportGroup:AddButton({ Text = 'Copy Executor Report', Tooltip = 'Copies what this executor gives the camera and viewmodel features and saves it to workspace/ZkxHub/_executor_report.txt.', Func = GuardRivalsCallback('Diagnostics_Button', function() RivalsRuntimeBridge.Diagnostics.Run() end), }) end local CameraEffectsGroup = VisualsTab:AddRightGroupbox('Camera Effects') CameraEffectsGroup:AddToggle('P4S2T6', { Text = 'No Camera Shake', Default = false, Tooltip = 'Removes camera shake.', Callback = RivalsModsState.EnsureHooks, }) local ViewmodelEffectsGroup = VisualsTab:AddRightGroupbox('Viewmodel Effects') ViewmodelEffectsGroup:AddToggle('P1S31T1', { Text = 'No Motion', Default = false, Tooltip = 'Stops your viewmodel from swaying and bobbing.', Callback = RivalsModsState.RefreshViewmodelEffects, }) ViewmodelEffectsGroup:AddToggle('P1S31T2', { Text = 'No Shoot Animation', Default = false, Callback = RivalsModsState.RefreshViewmodelEffects, }) ViewmodelEffectsGroup:AddToggle('P1S31T3', { Text = 'No Sprint Animation', Default = false, Callback = RivalsModsState.RefreshViewmodelEffects, }) ViewmodelEffectsGroup:AddToggle('P1S31T4', { Text = 'No Equip Animation', Default = false, Callback = RivalsModsState.RefreshViewmodelEffects, }) ViewmodelEffectsGroup:AddToggle('P1S31T5', { Text = 'No Reload Animation', Default = false, Callback = RivalsModsState.RefreshViewmodelEffects, }) local CameraViewGroup = VisualsTab:AddRightGroupbox('Camera View') CameraViewGroup:AddToggle('P4S2T8', { Text = 'Third Person', Default = false, Tooltip = 'Puts your camera in third person.', Callback = RivalsModsState.EnsureHooks, }) CameraViewGroup:AddToggle('PX11T1', { Text = 'Third Person Offset', Default = false, Tooltip = 'Moves the third-person camera. Only applies while you are in third person.', }) local DepThirdPersonOffset = CameraViewGroup:AddDependencyBox() DepThirdPersonOffset:AddSlider('PX11S1', { Text = 'Offset X', Default = 3, Min = -10, Max = 10, Rounding = 1, Compact = true, Tooltip = 'Left and right. Mirrors automatically when the camera is on the other shoulder.', }) DepThirdPersonOffset:AddSlider('PX11S2', { Text = 'Offset Y', Default = 1.5, Min = -5, Max = 12, Rounding = 1, Compact = true, }) DepThirdPersonOffset:AddSlider('PX11S3', { Text = 'Offset Z', Default = 7.5, Min = 0, Max = 30, Rounding = 1, Compact = true, Tooltip = 'Distance behind you.', }) DepThirdPersonOffset:SetupDependencies({ { Toggles.PX11T1, true } }) CameraViewGroup:AddToggle('P4S2T9', { Text = 'Custom Camera FOV', Default = false, Tooltip = 'Changes your field of view. Game zoom effects still work on top of it.', Callback = RivalsModsState.EnsureHooks, }) local DepCameraFov = CameraViewGroup:AddDependencyBox() DepCameraFov:AddSlider('P4S2S4', { Text = 'Camera FOV', Default = 80, Min = 50, Max = 130, Rounding = 0, Compact = true, }) DepCameraFov:SetupDependencies({ { Toggles.P4S2T9, true } }) CameraViewGroup:AddToggle('P4S2T10', { Text = 'Viewmodel Offset', Default = false, Tooltip = 'Moves your arms and weapon on screen.', Callback = RivalsModsState.EnsureHooks, }) local DepViewmodelOffset = CameraViewGroup:AddDependencyBox() local function ViewmodelOffsetChanged() RivalsRuntimeBridge.NativeRemovals.SyncViewmodelOffsetHook() end DepViewmodelOffset:AddSlider('P4S2S5', { Text = 'Viewmodel X', Callback = ViewmodelOffsetChanged, Default = 0, Min = -3, Max = 3, Rounding = 2, Compact = true, }) DepViewmodelOffset:AddSlider('P4S2S6', { Text = 'Viewmodel Y', Callback = ViewmodelOffsetChanged, Default = 0, Min = -3, Max = 3, Rounding = 2, Compact = true, }) DepViewmodelOffset:AddSlider('P4S2S7', { Text = 'Viewmodel Z', Callback = ViewmodelOffsetChanged, Default = 0, Min = -3, Max = 3, Rounding = 2, Compact = true, }) DepViewmodelOffset:AddSlider('PX3S1', { Text = 'Viewmodel Pitch', Callback = ViewmodelOffsetChanged, Default = 0, Min = -180, Max = 180, Rounding = 0, Compact = true, }) DepViewmodelOffset:AddSlider('PX3S2', { Text = 'Viewmodel Yaw', Callback = ViewmodelOffsetChanged, Default = 0, Min = -180, Max = 180, Rounding = 0, Compact = true, }) DepViewmodelOffset:AddSlider('PX3S3', { Text = 'Viewmodel Roll', Callback = ViewmodelOffsetChanged, Default = 0, Min = -180, Max = 180, Rounding = 0, Compact = true, }) DepViewmodelOffset:SetupDependencies({ { Toggles.P4S2T10, true } }) local function BindViewmodelVisualOption(control) if control and type(control.OnChanged) == 'function' then control:OnChanged( GuardRivalsCallback('ViewmodelVisuals_OptionChanged', RivalsRuntimeBridge.ViewmodelVisuals.RefreshAll) ) end return control end local function AddChamControls(group, prefix, label, stripDefault) BindViewmodelVisualOption(group:AddToggle(prefix .. 'T1', { Text = label, Default = false, })) local settings = group:AddDependencyBox() BindViewmodelVisualOption(settings:AddDropdown(prefix .. 'D1', { Values = RivalsRuntimeBridge.ViewmodelVisuals.MaterialNames, Default = 'Ghost', Multi = false, Text = 'Material', })) BindViewmodelVisualOption(settings:AddLabel('Color'):AddColorPicker(prefix .. 'C1', { Default = Color3.new(1, 1, 1), Title = label .. ' Color', })) BindViewmodelVisualOption(settings:AddSlider(prefix .. 'S1', { Text = 'Transparency', Default = 0, Min = 0, Max = 1, Rounding = 2, Compact = true, })) BindViewmodelVisualOption(settings:AddToggle(prefix .. 'T2', { Text = 'Strip Textures', Default = stripDefault == true, })) settings:SetupDependencies({ { Toggles[prefix .. 'T1'], true } }) end local ModelChamsGroup = VisualsTab:AddRightGroupbox('Model Chams') AddChamControls(ModelChamsGroup, 'P1S19', 'Character Chams', false) AddChamControls(ModelChamsGroup, 'P1S20', 'Arms Chams', true) ModelChamsGroup:AddToggle('TXNoHands', { Text = 'No Hands', Default = false, Tooltip = 'Hides your arms in first person (the arms, their sleeves / gloves and wrapped arms). Your weapon stays. Needs no texture pack; off shows the arms again.', Callback = GuardRivalsCallback('NoHands_Toggle', function() if RivalsRuntimeBridge.SyncNoHands then RivalsRuntimeBridge.SyncNoHands() end end), }) AddChamControls(ModelChamsGroup, 'P1S21', 'Item Chams', false) local function AddHighlightControls(group, prefix, label) BindViewmodelVisualOption(group:AddToggle(prefix .. 'T1', { Text = label, Default = false, })) local settings = group:AddDependencyBox() BindViewmodelVisualOption(settings:AddLabel('Fill Color'):AddColorPicker(prefix .. 'C1', { Default = Color3.new(1, 1, 1), Title = label .. ' Fill Color', })) BindViewmodelVisualOption(settings:AddSlider(prefix .. 'S1', { Text = 'Fill Transparency', Default = 0.5, Min = 0, Max = 1, Rounding = 2, Compact = true, })) BindViewmodelVisualOption(settings:AddLabel('Outline Color'):AddColorPicker(prefix .. 'C2', { Default = Color3.new(1, 1, 1), Title = label .. ' Outline Color', })) BindViewmodelVisualOption(settings:AddSlider(prefix .. 'S2', { Text = 'Outline Transparency', Default = 0, Min = 0, Max = 1, Rounding = 2, Compact = true, })) settings:SetupDependencies({ { Toggles[prefix .. 'T1'], true } }) end local ViewmodelHighlightGroup = VisualsTab:AddRightGroupbox('Viewmodel Highlight') AddHighlightControls(ViewmodelHighlightGroup, 'P1S22', 'Arms Highlight') AddHighlightControls(ViewmodelHighlightGroup, 'P1S23', 'Item Highlight') local PX12 = VisualsTab:AddRightGroupbox('Viewmodel Wireframe') PX12:AddToggle('PX12T1', { Text = 'Arms Wireframe', Default = false, Tooltip = 'Outlines every part of your arms with thin lines.', }) PX12:AddToggle('PX12T2', { Text = 'Weapon Wireframe', Default = false, Tooltip = 'Outlines every part of your weapon with thin lines.', }) PX12:AddLabel('Line Color'):AddColorPicker('PX12C1', { Default = Color3.fromRGB(255, 255, 255), Title = 'Wireframe Color', }) PX12:AddSlider('PX12S1', { Text = 'Line Width', Default = 0.02, Min = 0.005, Max = 0.1, Rounding = 3, Compact = true, }) PX12:AddSlider('PX12S2', { Text = 'Line Transparency', Default = 0, Min = 0, Max = 0.9, Rounding = 2, Compact = true, }) local CrosshairPresetsGroup = CrosshairTab:AddLeftGroupbox('Presets') CrosshairPresetsGroup:AddDropdown('P1S26D6', { Values = RivalsRuntimeBridge.ViewmodelVisuals.CrosshairPresetOrder, Multi = false, Text = 'Preset', AllowNull = true, Searchable = true, Tooltip = 'Ready-made crosshairs styled after popular shooters. Picking one turns the custom crosshair on and fills in the settings below - tweak anything afterwards.', Callback = GuardRivalsCallback( 'ViewmodelVisuals_ApplyCrosshairPreset', function(value) RivalsRuntimeBridge.ViewmodelVisuals.ApplyCrosshairPreset(value) end ), }) local CrosshairGroup = CrosshairTab:AddLeftGroupbox('Custom Crosshair') BindViewmodelVisualOption(CrosshairGroup:AddToggle('P1S26T1', { Text = 'Custom Crosshair', Default = false, Tooltip = 'Replaces the game crosshair with your own.', })) BindViewmodelVisualOption(CrosshairGroup:AddDropdown('P1S26D1', { Values = { 'Lines', 'Image' }, Default = 'Lines', Multi = false, Text = 'Style', })) BindViewmodelVisualOption(CrosshairGroup:AddSlider('P1S26S1', { Text = 'Length', Default = 12, Min = 1, Max = 40, Rounding = 0, Compact = true, })) BindViewmodelVisualOption(CrosshairGroup:AddSlider('P1S26S2', { Text = 'Thickness', Default = 2, Min = 1, Max = 8, Rounding = 0, Compact = true, })) BindViewmodelVisualOption(CrosshairGroup:AddSlider('P1S26S3', { Text = 'Gap', Default = 6, Min = 0, Max = 40, Rounding = 0, Compact = true, })) BindViewmodelVisualOption(CrosshairGroup:AddToggle('P1S26T2', { Text = 'Outline', Default = false })) BindViewmodelVisualOption(CrosshairGroup:AddLabel('Outline Color'):AddColorPicker('P1S26C1', { Default = Color3.new(0, 0, 0), Title = 'Crosshair Outline Color', })) BindViewmodelVisualOption(CrosshairGroup:AddSlider('P1S26S11', { Text = 'Outline Thickness', Default = 1, Min = 0, Max = 4, Rounding = 0, Compact = true, })) for _, direction in ipairs({ { 'Top', 'P1S26T3', 'P1S26C2' }, { 'Bottom', 'P1S26T4', 'P1S26C3' }, { 'Left', 'P1S26T5', 'P1S26C4' }, { 'Right', 'P1S26T6', 'P1S26C5' }, }) do BindViewmodelVisualOption(CrosshairGroup:AddToggle(direction[2], { Text = direction[1], Default = true, })) BindViewmodelVisualOption(CrosshairGroup:AddLabel(direction[1] .. ' Color'):AddColorPicker(direction[3], { Default = Color3.new(1, 1, 1), Title = direction[1] .. ' Crosshair Color', })) end BindViewmodelVisualOption(CrosshairGroup:AddToggle('P1S26T7', { Text = 'Spread', Default = false, Tooltip = 'Pulses the crosshair gap in and out. Visual only - not tied to real weapon spread.', })) BindViewmodelVisualOption(CrosshairGroup:AddSlider('P1S26S8', { Text = 'Spread Min', Default = 4, Min = 0, Max = 100, Rounding = 0, Compact = true, })) BindViewmodelVisualOption(CrosshairGroup:AddSlider('P1S26S9', { Text = 'Spread Max', Default = 16, Min = 0, Max = 100, Rounding = 0, Compact = true, })) BindViewmodelVisualOption(CrosshairGroup:AddSlider('P1S26S10', { Text = 'Spread Speed', Default = 3, Min = 0, Max = 10, Rounding = 2, Compact = true, })) BindViewmodelVisualOption(CrosshairGroup:AddToggle('P1S26T8', { Text = 'Animated Rotation', Default = false, Tooltip = 'Spins the crosshair continuously, starting from the Rotation Angle.', })) BindViewmodelVisualOption(CrosshairGroup:AddSlider('P1S26S4', { Text = 'Rotation Angle', Default = 0, Min = 0, Max = 360, Rounding = 0, Compact = true, })) BindViewmodelVisualOption(CrosshairGroup:AddSlider('P1S26S5', { Text = 'Rotation Speed', Default = 90, Min = -360, Max = 360, Rounding = 0, Compact = true, })) BindViewmodelVisualOption(CrosshairGroup:AddInput('P1S26I1', { Default = '', Text = 'Image Asset / File', AllowEmpty = true, EmptyReset = '', Finished = true, })) BindViewmodelVisualOption(CrosshairGroup:AddSlider('P1S26S6', { Text = 'Image Size', Default = 32, Min = 4, Max = 256, Rounding = 0, Compact = true, })) BindViewmodelVisualOption(CrosshairGroup:AddSlider('P1S26S7', { Text = 'Image Transparency', Default = 0, Min = 0, Max = 1, Rounding = 2, Compact = true, })) BindViewmodelVisualOption(CrosshairGroup:AddToggle('P1S26T9', { Text = 'Follow Target', Default = false, Tooltip = 'Drags the crosshair toward the current Aimbot / Silent target.', })) BindViewmodelVisualOption(CrosshairGroup:AddDropdown('P1S26D2', { Values = { 'Crosshair', 'Crosshair + Text' }, Default = 'Crosshair', Multi = false, Text = 'Follow Mode', })) local CrosshairAnimationGroup = CrosshairTab:AddLeftGroupbox('Crosshair Animation') BindViewmodelVisualOption(CrosshairAnimationGroup:AddToggle('P1S26T10', { Text = 'Color Animation', Default = false, })) local CrosshairAnimationSettings = CrosshairAnimationGroup:AddDependencyBox() BindViewmodelVisualOption(CrosshairAnimationSettings:AddDropdown('P1S26D3', { Values = { 'Shimmer', 'Perimeter', 'PingPong' }, Default = 'Shimmer', Multi = false, Text = 'Mode', })) BindViewmodelVisualOption(CrosshairAnimationSettings:AddSlider('P1S26S12', { Text = 'Speed', Default = 1, Min = 0.1, Max = 5, Rounding = 1, Compact = true, })) BindViewmodelVisualOption(CrosshairAnimationSettings:AddLabel('Shimmer Fade'):AddColorPicker('P1S26C6', { Default = Color3.fromRGB(40, 40, 40), Title = 'Crosshair Shimmer Fade', })) BindViewmodelVisualOption(CrosshairAnimationSettings:AddLabel('Perimeter Start'):AddColorPicker('P1S26C7', { Default = Color3.fromRGB(255, 0, 0), Title = 'Crosshair Perimeter Start', })) BindViewmodelVisualOption(CrosshairAnimationSettings:AddLabel('Perimeter End'):AddColorPicker('P1S26C8', { Default = Color3.fromRGB(0, 0, 255), Title = 'Crosshair Perimeter End', })) BindViewmodelVisualOption(CrosshairAnimationSettings:AddLabel('PingPong Background'):AddColorPicker('P1S26C9', { Default = Color3.new(0, 0, 0), Title = 'Crosshair PingPong Background', })) BindViewmodelVisualOption(CrosshairAnimationSettings:AddLabel('PingPong Main'):AddColorPicker('P1S26C10', { Default = Color3.new(1, 1, 1), Title = 'Crosshair PingPong Main', })) BindViewmodelVisualOption(CrosshairAnimationSettings:AddSlider('P1S26S13', { Text = 'PingPong Rotation', Default = 0, Min = 0, Max = 360, Rounding = 0, Compact = true, })) CrosshairAnimationSettings:SetupDependencies({ { Toggles.P1S26T10, true } }) local CrosshairTextGroup = CrosshairTab:AddRightGroupbox('Crosshair Text') BindViewmodelVisualOption(CrosshairTextGroup:AddToggle('P1S26T11', { Text = 'Text', Default = false, Tooltip = 'Shows a custom label beneath the crosshair.', })) local CrosshairTextSettings = CrosshairTextGroup:AddDependencyBox() BindViewmodelVisualOption(CrosshairTextSettings:AddInput('P1S26I2', { Default = '', Text = 'Content', AllowEmpty = true, EmptyReset = '', Finished = true, })) BindViewmodelVisualOption(CrosshairTextSettings:AddDropdown('P1S26D4', { Values = { 'Inconsolata', 'Gotham', 'Source Sans', 'Roboto Mono' }, Default = 'Inconsolata', Multi = false, Text = 'Font', })) BindViewmodelVisualOption(CrosshairTextSettings:AddSlider('P1S26S14', { Text = 'Size', Default = 14, Min = 6, Max = 48, Rounding = 0, Compact = true, })) BindViewmodelVisualOption(CrosshairTextSettings:AddSlider('P1S26S15', { Text = 'Offset', Default = 24, Min = 0, Max = 100, Rounding = 0, Compact = true, })) BindViewmodelVisualOption(CrosshairTextSettings:AddLabel('Color'):AddColorPicker('P1S26C11', { Default = Color3.new(1, 1, 1), Title = 'Crosshair Text Color', })) BindViewmodelVisualOption(CrosshairTextSettings:AddToggle('P1S26T12', { Text = 'Outline', Default = false, })) BindViewmodelVisualOption(CrosshairTextSettings:AddLabel('Outline Color'):AddColorPicker('P1S26C12', { Default = Color3.new(0, 0, 0), Title = 'Crosshair Text Outline Color', })) BindViewmodelVisualOption(CrosshairTextSettings:AddSlider('P1S26S16', { Text = 'Outline Thickness', Default = 1, Min = 0, Max = 4, Rounding = 0, Compact = true, })) BindViewmodelVisualOption(CrosshairTextSettings:AddToggle('P1S26T13', { Text = 'Text Animation', Default = false, })) CrosshairTextSettings:SetupDependencies({ { Toggles.P1S26T11, true } }) local CrosshairTextAnimationSettings = CrosshairTextGroup:AddDependencyBox() BindViewmodelVisualOption(CrosshairTextAnimationSettings:AddDropdown('P1S26D5', { Values = { 'Shimmer', 'PingPong' }, Default = 'Shimmer', Multi = false, Text = 'Animation Mode', })) BindViewmodelVisualOption(CrosshairTextAnimationSettings:AddSlider('P1S26S17', { Text = 'Animation Speed', Default = 1, Min = 0.1, Max = 5, Rounding = 1, Compact = true, })) BindViewmodelVisualOption(CrosshairTextAnimationSettings:AddLabel('Shimmer Fade'):AddColorPicker('P1S26C13', { Default = Color3.fromRGB(40, 40, 40), Title = 'Text Shimmer Fade', })) BindViewmodelVisualOption(CrosshairTextAnimationSettings:AddLabel('PingPong Background'):AddColorPicker('P1S26C14', { Default = Color3.new(0, 0, 0), Title = 'Text PingPong Background', })) BindViewmodelVisualOption(CrosshairTextAnimationSettings:AddLabel('PingPong Main'):AddColorPicker('P1S26C15', { Default = Color3.new(1, 1, 1), Title = 'Text PingPong Main', })) BindViewmodelVisualOption(CrosshairTextAnimationSettings:AddSlider('P1S26S18', { Text = 'PingPong Rotation', Default = 0, Min = 0, Max = 360, Rounding = 0, Compact = true, })) CrosshairTextAnimationSettings:SetupDependencies({ { Toggles.P1S26T11, true }, { Toggles.P1S26T13, true }, }) local function BindVisualRemovalOption(control) if control and type(control.OnChanged) == 'function' then control:OnChanged( GuardRivalsCallback('NativeRemovals_OptionChanged', RivalsRuntimeBridge.NativeRemovals.RefreshAll) ) end return control end local OverlayRemovalsGroup = VisualsTab:AddRightGroupbox('Overlay & Effect Removals') BindVisualRemovalOption(OverlayRemovalsGroup:AddToggle('P1S27T1', { Text = 'No Flashbang', Default = false, })) BindVisualRemovalOption(OverlayRemovalsGroup:AddToggle('P1S27T2', { Text = 'No Burn Effect', Default = false, })) BindVisualRemovalOption(OverlayRemovalsGroup:AddToggle('P1S27T3', { Text = 'No ADS Vignette', Default = false, })) BindVisualRemovalOption(OverlayRemovalsGroup:AddToggle('P1S27T4', { Text = 'No Scope Overlay', Default = false, })) BindVisualRemovalOption(OverlayRemovalsGroup:AddToggle('P1S27T5', { Text = 'No Scope Reticle', Default = false, })) BindVisualRemovalOption(OverlayRemovalsGroup:AddToggle('P1S27T6', { Text = 'No Gun Tracers', Default = false, })) BindVisualRemovalOption(OverlayRemovalsGroup:AddToggle('P1S27T7', { Text = 'No Muzzle Flash', Default = false, })) BindVisualRemovalOption(OverlayRemovalsGroup:AddToggle('P1S27T8', { Text = 'No Hitmarker', Default = false, })) local function BindWorldVisualOption(control) if control and type(control.OnChanged) == 'function' then control:OnChanged( GuardRivalsCallback('WorldVisuals_OptionChanged', RivalsRuntimeBridge.WorldVisuals.RefreshAll) ) end return control end local WorldPresetsGroup = WorldTab:AddLeftGroupbox('Presets') WorldPresetsGroup:AddDropdown('P1S32D1', { Values = RivalsRuntimeBridge.WorldVisuals.WorldPresetOrder, Multi = false, Text = 'Theme', AllowNull = true, Searchable = true, Tooltip = "One-click world looks. Picking a theme sets the lighting, fog, bloom, colors, sun rays, atmosphere, and skybox below - tweak anything afterwards. Weather, Lightning and Ambience stay yours. Game Default restores the game's own look.", Callback = GuardRivalsCallback( 'WorldVisuals_ApplyPreset', function(value) RivalsRuntimeBridge.WorldVisuals.ApplyWorldPreset(value) end ), }) local P1S4 = WorldTab:AddLeftGroupbox('Lighting') BindWorldVisualOption(P1S4:AddToggle('P1S4T2', { Text = 'Ambient', Default = false })) local DepLightingAmbient = P1S4:AddDependencyBox() BindWorldVisualOption(DepLightingAmbient:AddLabel('Indoor Ambient'):AddColorPicker('P1S4C1', { Default = Color3.new(0.231373, 0.796078, 1), Title = 'Indoor Ambient', })) BindWorldVisualOption(DepLightingAmbient:AddLabel('Outdoor Ambient'):AddColorPicker('P1S4C2', { Default = Color3.new(0.003922, 0.4, 0.545098), Title = 'Outdoor Ambient', })) DepLightingAmbient:SetupDependencies({ { Toggles.P1S4T2, true } }) BindWorldVisualOption(P1S4:AddToggle('P1S4T3', { Text = 'Clock Time', Default = false })) local DepLightingClockTime = P1S4:AddDependencyBox() BindWorldVisualOption(DepLightingClockTime:AddSlider('P1S4S1', { Text = 'Time', Default = 12, Min = 0, Max = 24, Rounding = 2, Compact = true, })) DepLightingClockTime:SetupDependencies({ { Toggles.P1S4T3, true } }) BindWorldVisualOption(P1S4:AddToggle('P1S4T4', { Text = 'Brightness', Default = false })) local DepLightingBrightness = P1S4:AddDependencyBox() BindWorldVisualOption(DepLightingBrightness:AddSlider('P1S4S2', { Text = 'Brightness Value', Default = 2, Min = 0, Max = 10, Rounding = 2, Compact = true, })) DepLightingBrightness:SetupDependencies({ { Toggles.P1S4T4, true } }) BindWorldVisualOption(P1S4:AddToggle('P1S4T5', { Text = 'Color Shift', Default = false })) local DepLightingColorShift = P1S4:AddDependencyBox() BindWorldVisualOption(DepLightingColorShift:AddLabel('Color Shift Top'):AddColorPicker('P1S4C3', { Default = Color3.fromRGB(100, 160, 255), Title = 'Color Shift Top', })) BindWorldVisualOption(DepLightingColorShift:AddLabel('Color Shift Bottom'):AddColorPicker('P1S4C4', { Default = Color3.fromRGB(20, 40, 90), Title = 'Color Shift Bottom', })) DepLightingColorShift:SetupDependencies({ { Toggles.P1S4T5, true } }) BindWorldVisualOption(P1S4:AddToggle('P1S4T6', { Text = 'Environment Diffuse Scale', Default = false, Tooltip = 'How much ambient light the sky adds to surfaces.', })) local DepLightingDiffuse = P1S4:AddDependencyBox() BindWorldVisualOption(DepLightingDiffuse:AddSlider('P1S4S3', { Text = 'Diffuse Scale', Default = 0, Min = 0, Max = 1, Rounding = 2, Compact = true, })) DepLightingDiffuse:SetupDependencies({ { Toggles.P1S4T6, true } }) BindWorldVisualOption(P1S4:AddToggle('P1S4T7', { Text = 'Exposure Compensation', Default = false, Tooltip = 'Overall brightness boost or cut, like camera exposure. Positive = brighter.', })) local DepLightingExposure = P1S4:AddDependencyBox() BindWorldVisualOption(DepLightingExposure:AddSlider('P1S4S4', { Text = 'Exposure', Default = 0, Min = -5, Max = 5, Rounding = 2, Compact = true, })) DepLightingExposure:SetupDependencies({ { Toggles.P1S4T7, true } }) BindWorldVisualOption(P1S4:AddToggle('P1S4T8', { Text = 'Environment Specular Scale', Default = false, Tooltip = 'How much surfaces reflect the sky. Higher = shinier world.', })) local DepLightingSpecular = P1S4:AddDependencyBox() BindWorldVisualOption(DepLightingSpecular:AddSlider('P1S4S5', { Text = 'Specular Scale', Default = 0, Min = 0, Max = 1, Rounding = 2, Compact = true, })) DepLightingSpecular:SetupDependencies({ { Toggles.P1S4T8, true } }) local P1S5 = WorldTab:AddRightGroupbox('Shadows & Environment') BindWorldVisualOption(P1S5:AddToggle('P1S5T1', { Text = 'Fog', Default = false })) local DepFog = P1S5:AddDependencyBox() BindWorldVisualOption(DepFog:AddLabel('Fog Color'):AddColorPicker('P1S5C1', { Default = Color3.fromRGB(192, 192, 192), Title = 'Fog Color', })) BindWorldVisualOption(DepFog:AddSlider('P1S5S1', { Text = 'Fog Start', Default = 0, Min = 0, Max = 10000, Rounding = 0, Compact = true, })) BindWorldVisualOption(DepFog:AddSlider('P1S5S2', { Text = 'Fog End', Default = 10000, Min = 0, Max = 10000, Rounding = 0, Compact = true, })) DepFog:SetupDependencies({ { Toggles.P1S5T1, true } }) BindWorldVisualOption(P1S5:AddToggle('P1S5T2', { Text = 'Geographic Latitude', Default = false, Tooltip = "Moves the sun and moon's path across the sky, which shifts every shadow.", })) local DepLatitude = P1S5:AddDependencyBox() BindWorldVisualOption(DepLatitude:AddSlider('P1S5S3', { Text = 'Latitude', Default = 41.733, Min = -90, Max = 90, Rounding = 3, Compact = true, })) DepLatitude:SetupDependencies({ { Toggles.P1S5T2, true } }) BindWorldVisualOption(P1S5:AddToggle('P1S5T3', { Text = 'Disable Global Shadows', Default = false })) BindWorldVisualOption(P1S5:AddToggle('P1S5T4', { Text = 'Shadow Softness', Default = false })) local DepShadowSoftness = P1S5:AddDependencyBox() BindWorldVisualOption(DepShadowSoftness:AddSlider('P1S5S4', { Text = 'Softness', Default = 0.5, Min = 0, Max = 1, Rounding = 2, Compact = true, })) DepShadowSoftness:SetupDependencies({ { Toggles.P1S5T4, true } }) BindWorldVisualOption(P1S5:AddToggle('P1S5T5', { Text = 'Lighting Style', Default = false })) local DepLightingStyle = P1S5:AddDependencyBox() BindWorldVisualOption(DepLightingStyle:AddDropdown('P1S5D1', { Values = { 'Realistic', 'Soft' }, Default = 'Realistic', Multi = false, Text = 'Style', })) DepLightingStyle:SetupDependencies({ { Toggles.P1S5T5, true } }) BindWorldVisualOption(P1S5:AddToggle('P1S5T6', { Text = 'Shadow Color', Default = false })) local DepShadowColor = P1S5:AddDependencyBox() BindWorldVisualOption(DepShadowColor:AddLabel('Shadow Color Value'):AddColorPicker('P1S5C2', { Default = Color3.new(0.7, 0.7, 0.72), Title = 'Shadow Color', })) DepShadowColor:SetupDependencies({ { Toggles.P1S5T6, true } }) BindWorldVisualOption(P1S5:AddToggle('P1S5T7', { Text = 'Prioritize Lighting Quality', Default = false, Tooltip = 'Keeps lighting quality high even on low graphics settings. May cost FPS.', })) local P1S6 = WorldTab:AddLeftGroupbox('Bloom') BindWorldVisualOption(P1S6:AddToggle('P1S6T1', { Text = 'Bloom', Default = false })) local DepBloom = P1S6:AddDependencyBox() BindWorldVisualOption(DepBloom:AddSlider('P1S6S1', { Text = 'Intensity', Default = 0.4, Min = 0, Max = 1, Rounding = 2, Compact = true, })) BindWorldVisualOption(DepBloom:AddSlider('P1S6S2', { Text = 'Size', Default = 24, Min = 0, Max = 56, Rounding = 0, Compact = true, })) BindWorldVisualOption(DepBloom:AddSlider('P1S6S3', { Text = 'Threshold', Default = 0.95, Min = 0, Max = 5, Rounding = 2, Compact = true, })) DepBloom:SetupDependencies({ { Toggles.P1S6T1, true } }) local P1S7 = WorldTab:AddLeftGroupbox('Color Correction') BindWorldVisualOption(P1S7:AddToggle('P1S7T1', { Text = 'Color Correction', Default = false })) local DepColorCorrection = P1S7:AddDependencyBox() BindWorldVisualOption(DepColorCorrection:AddLabel('Tint Color'):AddColorPicker('P1S7C1', { Default = Color3.new(1, 1, 1), Title = 'Tint Color', })) BindWorldVisualOption(DepColorCorrection:AddSlider('P1S7S1', { Text = 'Saturation', Default = 0, Min = -1, Max = 1, Rounding = 2, Compact = true, })) BindWorldVisualOption(DepColorCorrection:AddSlider('P1S7S2', { Text = 'Brightness', Default = 0, Min = -1, Max = 1, Rounding = 2, Compact = true, })) BindWorldVisualOption(DepColorCorrection:AddSlider('P1S7S3', { Text = 'Contrast', Default = 0, Min = -1, Max = 1, Rounding = 2, Compact = true, })) DepColorCorrection:SetupDependencies({ { Toggles.P1S7T1, true } }) local P1S8 = WorldTab:AddRightGroupbox('Color Grading') BindWorldVisualOption(P1S8:AddToggle('P1S8T1', { Text = 'Color Grading', Default = false })) local DepColorGrading = P1S8:AddDependencyBox() BindWorldVisualOption(DepColorGrading:AddDropdown('P1S8D1', { Values = { 'Default', 'Retro' }, Default = 'Default', Multi = false, Text = 'Tonemapper Preset', })) DepColorGrading:SetupDependencies({ { Toggles.P1S8T1, true } }) local P1S10 = WorldTab:AddRightGroupbox('Sun Rays') BindWorldVisualOption(P1S10:AddToggle('P1S10T1', { Text = 'Sun Rays', Default = false })) local DepSunRays = P1S10:AddDependencyBox() BindWorldVisualOption(DepSunRays:AddSlider('P1S10S1', { Text = 'Intensity', Default = 0.25, Min = 0, Max = 1, Rounding = 2, Compact = true, })) BindWorldVisualOption(DepSunRays:AddSlider('P1S10S2', { Text = 'Spread', Default = 1, Min = 0, Max = 1, Rounding = 2, Compact = true, })) DepSunRays:SetupDependencies({ { Toggles.P1S10T1, true } }) local PX1 = WorldTab:AddRightGroupbox('Depth of Field') BindWorldVisualOption(PX1:AddToggle('PX1T1', { Text = 'Depth of Field', Default = false, Tooltip = 'Blurs everything nearer or farther than the focus band.', })) local DepDepthOfField = PX1:AddDependencyBox() BindWorldVisualOption(DepDepthOfField:AddSlider('PX1S1', { Text = 'Far Intensity', Default = 0.75, Min = 0, Max = 1, Rounding = 2, Compact = true, })) BindWorldVisualOption(DepDepthOfField:AddSlider('PX1S2', { Text = 'Focus Distance', Default = 25, Min = 0, Max = 500, Rounding = 0, Compact = true, })) BindWorldVisualOption(DepDepthOfField:AddSlider('PX1S3', { Text = 'In Focus Radius', Default = 30, Min = 0, Max = 500, Rounding = 0, Compact = true, })) BindWorldVisualOption(DepDepthOfField:AddSlider('PX1S4', { Text = 'Near Intensity', Default = 0.5, Min = 0, Max = 1, Rounding = 2, Compact = true, })) DepDepthOfField:SetupDependencies({ { Toggles.PX1T1, true } }) local P1S12 = WorldTab:AddLeftGroupbox('Skybox') BindWorldVisualOption(P1S12:AddToggle('P1S12T1', { Text = 'Skybox', Default = false })) P1S12:AddButton({ Text = 'Manage Custom Skyboxes', Func = GuardRivalsCallback( 'WorldVisuals_OpenCustomSkyboxesDialog', RivalsRuntimeBridge.WorldVisuals.OpenCustomSkyboxesDialog ), Tooltip = 'Add and remove saved six-face skyboxes in a focused dialog.', }) local DepSkybox = P1S12:AddDependencyBox() BindWorldVisualOption(DepSkybox:AddDropdown('P1S12D1', { Values = RivalsRuntimeBridge.WorldVisuals.ResolveSkyPresetNames(), Default = '', Multi = false, Text = 'Preset', Searchable = true, MaxVisibleDropdownItems = 12, })) DepSkybox:SetupDependencies({ { Toggles.P1S12T1, true } }) local P1S13 = WorldTab:AddRightGroupbox('Atmosphere') BindWorldVisualOption(P1S13:AddToggle('P1S13T1', { Text = 'Atmosphere', Default = false })) local DepAtmosphere = P1S13:AddDependencyBox() BindWorldVisualOption(DepAtmosphere:AddLabel('Color'):AddColorPicker('P1S13C1', { Default = Color3.new(0.7843, 0.6667, 0.4235), Title = 'Atmosphere Color', })) BindWorldVisualOption(DepAtmosphere:AddLabel('Decay'):AddColorPicker('P1S13C2', { Default = Color3.new(0.3608, 0.2353, 0.0549), Title = 'Atmosphere Decay', })) BindWorldVisualOption(DepAtmosphere:AddSlider('P1S13S1', { Text = 'Density', Default = 0.395, Min = 0, Max = 1, Rounding = 3, Compact = true, })) BindWorldVisualOption(DepAtmosphere:AddSlider('P1S13S2', { Text = 'Offset', Default = 0, Min = 0, Max = 1, Rounding = 2, Compact = true, })) BindWorldVisualOption(DepAtmosphere:AddSlider('P1S13S3', { Text = 'Glare', Default = 0, Min = 0, Max = 1, Rounding = 2, Compact = true, })) BindWorldVisualOption(DepAtmosphere:AddSlider('P1S13S4', { Text = 'Haze', Default = 0, Min = 0, Max = 1, Rounding = 2, Compact = true, })) DepAtmosphere:SetupDependencies({ { Toggles.P1S13T1, true } }) local P1S14 = WorldTab:AddRightGroupbox('Ambience') BindWorldVisualOption(P1S14:AddToggle('P1S14T1', { Text = 'Ambience', Default = false })) local DepAmbience = P1S14:AddDependencyBox() DepAmbience:AddDropdown('P1S14D1', { Values = RivalsRuntimeBridge.WorldVisuals.AmbiencePresetOrder, Multi = false, Text = 'Preset', AllowNull = true, Tooltip = 'Popular ambient loops. Picking one fills the sound box below - you can still paste your own ID or URL instead.', Callback = GuardRivalsCallback('WorldVisuals_AmbiencePreset', function(value) local source = RivalsRuntimeBridge.WorldVisuals.AmbiencePresets[value] if source and Options.P1S14I1 and Options.P1S14I1.Value ~= source then Options.P1S14I1:SetValue(source) end end), }) BindWorldVisualOption(DepAmbience:AddInput('P1S14I1', { Default = '', Text = 'Custom Sound (ID or URL)', AllowEmpty = true, EmptyReset = '', Finished = true, })) BindWorldVisualOption(DepAmbience:AddSlider('P1S14S1', { Text = 'Volume', Default = 1, Min = 0, Max = 1.5, Rounding = 2, Compact = true, })) DepAmbience:SetupDependencies({ { Toggles.P1S14T1, true } }) local P1S30 = VisualsTab:AddRightGroupbox('Stretched Resolution') BindWorldVisualOption(P1S30:AddToggle('P1S30T1', { Text = 'Stretched Resolution', Default = false, Tooltip = 'Stretches the screen like playing on a lower stretched resolution.', })) local StretchedResolutionSettings = P1S30:AddDependencyBox() BindWorldVisualOption(StretchedResolutionSettings:AddSlider('P1S30S1', { Text = 'X Ratio', Default = 1, Min = 0.01, Max = 1, Rounding = 2, Compact = true, })) BindWorldVisualOption(StretchedResolutionSettings:AddSlider('P1S30S2', { Text = 'Y Ratio', Default = 1, Min = 0.01, Max = 1, Rounding = 2, Compact = true, })) StretchedResolutionSettings:SetupDependencies({ { Toggles.P1S30T1, true } }) local P1S28 = WorldTab:AddLeftGroupbox('Weather') BindWorldVisualOption(P1S28:AddToggle('P1S28T1', { Text = 'Weather', Default = false, Tooltip = 'Snow, Rain, or Blizzard weather that follows your camera.', })) local WeatherSettings = P1S28:AddDependencyBox() BindWorldVisualOption(WeatherSettings:AddDropdown('P1S28D1', { Values = { 'Snow', 'Rain', 'Blizzard' }, Default = 'Snow', Multi = false, Text = 'Preset', })) BindWorldVisualOption(WeatherSettings:AddLabel('Color'):AddColorPicker('P1S28C1', { Default = Color3.fromRGB(255, 255, 255), Title = 'Weather Color', })) BindWorldVisualOption(WeatherSettings:AddSlider('P1S28S1', { Text = 'Intensity', Default = 1, Min = 0, Max = 2, Rounding = 2, Compact = true, })) BindWorldVisualOption(WeatherSettings:AddSlider('P1S28S2', { Text = 'Rate', Default = 1, Min = 0, Max = 3, Rounding = 2, Compact = true, })) BindWorldVisualOption(WeatherSettings:AddSlider('P1S28S3', { Text = 'Speed', Default = 1, Min = 0.5, Max = 3, Rounding = 2, Compact = true, })) BindWorldVisualOption(WeatherSettings:AddSlider('P1S28S4', { Text = 'Size', Default = 1, Min = 0.5, Max = 2, Rounding = 2, Compact = true, })) BindWorldVisualOption(WeatherSettings:AddSlider('P1S28S5', { Text = 'Glow', Default = 0.6, Min = 0, Max = 1, Rounding = 2, Compact = true, })) BindWorldVisualOption(WeatherSettings:AddSlider('P1S28S6', { Text = 'Spread', Default = 3, Min = 0, Max = 3, Rounding = 2, Compact = true, })) BindWorldVisualOption(WeatherSettings:AddSlider('P1S28S7', { Text = 'Height', Default = 45, Min = 10, Max = 100, Rounding = 0, Compact = true, })) BindWorldVisualOption(WeatherSettings:AddSlider('P1S28S8', { Text = 'Wind Strength', Default = 0, Min = 0, Max = 20, Rounding = 1, Compact = true, })) BindWorldVisualOption(WeatherSettings:AddSlider('P1S28S9', { Text = 'Wind Angle', Default = 0, Min = 0, Max = 360, Rounding = 0, Compact = true, })) WeatherSettings:SetupDependencies({ { Toggles.P1S28T1, true } }) local P1S29 = WorldTab:AddRightGroupbox('Lightning') BindWorldVisualOption(P1S29:AddToggle('P1S29T1', { Text = 'Lightning', Default = false, Tooltip = 'Strikes during Rain and Blizzard presets.', })) local LightningSettings = P1S29:AddDependencyBox() BindWorldVisualOption(LightningSettings:AddLabel('Bolt Color'):AddColorPicker('P1S29C1', { Default = Color3.fromRGB(214, 230, 255), Title = 'Lightning Color', })) BindWorldVisualOption(LightningSettings:AddSlider('P1S29S1', { Text = 'Interval', Default = 2, Min = 0.2, Max = 10, Rounding = 1, Compact = true, })) BindWorldVisualOption(LightningSettings:AddSlider('P1S29S2', { Text = 'Distance', Default = 80, Min = 10, Max = 200, Rounding = 0, Compact = true, })) BindWorldVisualOption(LightningSettings:AddSlider('P1S29S3', { Text = 'Height', Default = 220, Min = 50, Max = 400, Rounding = 0, Compact = true, })) BindWorldVisualOption(LightningSettings:AddSlider('P1S29S4', { Text = 'Thickness', Default = 4, Min = 0.5, Max = 12, Rounding = 1, Compact = true, })) BindWorldVisualOption(LightningSettings:AddSlider('P1S29S5', { Text = 'Jaggedness', Default = 7, Min = 0, Max = 20, Rounding = 1, Compact = true, })) BindWorldVisualOption(LightningSettings:AddSlider('P1S29S6', { Text = 'Branches', Default = 12, Min = 0, Max = 24, Rounding = 0, Compact = true, })) BindWorldVisualOption(LightningSettings:AddSlider('P1S29S7', { Text = 'Flash', Default = 8, Min = 0, Max = 20, Rounding = 1, Compact = true, })) BindWorldVisualOption(LightningSettings:AddToggle('P1S29T2', { Text = 'Sparks', Default = true })) BindWorldVisualOption(LightningSettings:AddLabel('Spark Color'):AddColorPicker('P1S29C2', { Default = Color3.fromRGB(238, 246, 255), Title = 'Spark Color', })) BindWorldVisualOption(LightningSettings:AddSlider('P1S29S8', { Text = 'Spark Count', Default = 18, Min = 0, Max = 40, Rounding = 0, Compact = true, })) BindWorldVisualOption(LightningSettings:AddSlider('P1S29S9', { Text = 'Spark Thickness', Default = 2.2, Min = 0.2, Max = 8, Rounding = 1, Compact = true, })) BindWorldVisualOption(LightningSettings:AddSlider('P1S29S10', { Text = 'Spark Distance', Default = 22, Min = 2, Max = 50, Rounding = 1, Compact = true, })) BindWorldVisualOption(LightningSettings:AddSlider('P1S29S11', { Text = 'Spark Speed', Default = 22, Min = 2, Max = 40, Rounding = 1, Compact = true, })) BindWorldVisualOption(LightningSettings:AddSlider('P1S29S12', { Text = 'Spark Jaggedness', Default = 4, Min = 0, Max = 12, Rounding = 1, Compact = true, Tooltip = 'Legacy setting kept for old configs; sparks are not affected by it.', })) BindWorldVisualOption(LightningSettings:AddToggle('P1S29T3', { Text = 'Explosion', Default = true })) BindWorldVisualOption(LightningSettings:AddLabel('Explosion Color'):AddColorPicker('P1S29C3', { Default = Color3.fromRGB(238, 200, 255), Title = 'Explosion Color', })) BindWorldVisualOption(LightningSettings:AddSlider('P1S29S13', { Text = 'Explosion Size', Default = 0.45, Min = 0, Max = 1, Rounding = 2, Compact = true, })) BindWorldVisualOption(LightningSettings:AddSlider('P1S29S14', { Text = 'Explosion Bolts', Default = 14, Min = 0, Max = 30, Rounding = 0, Compact = true, })) BindWorldVisualOption(LightningSettings:AddToggle('P1S29T4', { Text = 'Thunder Sound', Default = true })) BindWorldVisualOption(LightningSettings:AddSlider('P1S29S15', { Text = 'Thunder Volume', Default = 1, Min = 0, Max = 1.5, Rounding = 2, Compact = true, })) BindWorldVisualOption(LightningSettings:AddToggle('P1S29T5', { Text = 'Distance Delay', Default = true, Tooltip = 'Thunder arrives later for far-away strikes, like real lightning.', })) BindWorldVisualOption(LightningSettings:AddInput('P1S29I1', { Default = '', Text = 'Thunder Sound ID', AllowEmpty = true, EmptyReset = '', Finished = true, Tooltip = 'Leave empty to use the default thunder sound.', })) LightningSettings:SetupDependencies({ { Toggles.P1S29T1, true } }) -- ====================================================================== -- Texture Changer, Map Darkener, Anti-Lag and the watermark lookups (ZkxVader build, World tab) -- ====================================================================== do local TextureEngine = (function() -- Texture engine (ZkxVader build): asset swaps from a pack, map darkener, anti-lag. No menu code in here, the World tab part drives it. -- Every change is remembered per instance and put back when the feature is switched off or the hub unloads. Objects of the hub itself -- (names starting with ZkxHub), characters, view models, the camera and the terrain are never touched. -- Pack rules (asset id -> replacement url) come from the Minecraft pack (pastebin Cb9ExUkB), the Fleasion pack (fleasion-script) and the Determination pack -- (Magic Redux); the files are downloaded once into Engine.Folder under a hash of their url and handed to the game with getcustomasset. -- A rule whose replacement is '' blanks the asset; pack.Fonts = { { { family ids }, url of a ttf } } swaps UI fonts (needs the 'Fonts' kind). -- No Hands (Engine.NoHands) is independent of the packs: it hides the arms of the first person view model. local function clone(instance) if type(cloneref) == 'function' then return cloneref(instance) end return instance end local Players = clone(game:GetService('Players')) local Lighting = clone(game:GetService('Lighting')) local RunService = clone(game:GetService('RunService')) local Workspace = clone(workspace) local Engine = { Folder = 'ZkxHub/textures', Packs = {}, -- name -> { Rules = { { { id, ... }, url }, ... } }, filled by the World tab part OnStatus = function(_text) end, } -- caches only: a weak key table of Instances loses entries whenever the garbage collector runs (checked live: 11,224 -> 2,205 after one round), so every table that -- must remember an instance until it is restored is a plain table and is pruned of destroyed instances now and then local function weakKeys() return setmetatable({}, { __mode = 'k' }) end local function pruneDetached(map) for inst in pairs(map) do local ok, parent = pcall(function() return inst.Parent end) if not ok or parent == nil then map[inst] = nil end end end local function status(text) pcall(Engine.OnStatus, text) end -- ----------------------------------------------------------------------------------------------------------------------------------- helpers local Util = {} Engine.Util = Util -- the real file type from the first bytes: only these load in Roblox (webp, html error pages and the like are refused) function Util.sniff(body) if type(body) ~= 'string' or #body < 80 then return nil end local b1, b2, b3, b4 = string.byte(body, 1, 4) if b1 == 0x89 and b2 == 0x50 and b3 == 0x4E and b4 == 0x47 then return 'png' end if b1 == 0xFF and b2 == 0xD8 and b3 == 0xFF then return 'jpg' end if b1 == 0x4F and b2 == 0x67 and b3 == 0x67 and b4 == 0x53 then return 'ogg' end if b1 == 0x49 and b2 == 0x44 and b3 == 0x33 then return 'mp3' end if b1 == 0xFF and bit32.band(b2, 0xE0) == 0xE0 then return 'mp3' end if b1 == 0x52 and b2 == 0x49 and b3 == 0x46 and b4 == 0x46 and string.sub(body, 9, 12) == 'WAVE' then return 'wav' end if (b1 == 0x00 and b2 == 0x01 and b3 == 0x00 and b4 == 0x00) or string.sub(body, 1, 4) == 'true' or string.sub(body, 1, 4) == 'OTTO' then return 'ttf' end return nil end -- 32-bit FNV-1a (multiplication split so every step stays exact in a double); two seeds make the file name 16 hex digits local function fnv(text, seed) local hash = seed for index = 1, #text do hash = bit32.bxor(hash, string.byte(text, index)) hash = ((hash * 16777216) % 4294967296 + hash * 403) % 4294967296 end return hash end function Util.hashUrl(url) return string.format('%08x%08x', fnv(url, 2166136261), fnv(url, 5381)) end -- the asset id inside an Image / SoundId / Texture value: rbxassetid://123 or ...asset/?id=123 function Util.assetId(value) if type(value) ~= 'string' then return nil end return string.match(value, '^rbxassetid://(%d+)') or string.match(value, '[?&]id=(%d+)') end local function download(url) if type(request) == 'function' then local ok, response = pcall(request, { Url = url, Method = 'GET' }) if ok and type(response) == 'table' and response.StatusCode == 200 and type(response.Body) == 'string' then return response.Body end end local ok, body = pcall(function() return game:HttpGet(url) end) if ok and type(body) == 'string' then return body end return nil end local FILE_TYPES = { 'png', 'jpg', 'ogg', 'mp3', 'wav', 'ttf' } local function filesAvailable() return type(writefile) == 'function' and type(readfile) == 'function' and type(isfile) == 'function' and type(getcustomasset) == 'function' end local function ensureFolder() local path = '' for part in string.gmatch(Engine.Folder, '[^/]+') do path = path == '' and part or (path .. '/' .. part) pcall(function() if not isfolder(path) then makefolder(path) end end) end end -- a ttf is handed to the game as a font FAMILY: a small json next to the file lists the face, and getcustomasset of that json is what Font.new takes local function fontFamilyAsset(path) local okFile, fontAsset = pcall(getcustomasset, path) if not okFile or type(fontAsset) ~= 'string' or fontAsset == '' then return nil end -- written by hand: JSONEncode orders keys differently from run to run, which changes the content hash getcustomasset names the file by, and then a label that -- still carries the family of an earlier run could never be recognised as ours local jsonPath = path .. '.json' local encoded = '{"name":"ZkxHubPackFont","faces":[{"name":"Regular","weight":400,"style":"normal","assetId":"' .. string.gsub(fontAsset, '[\\"]', '\\%0') .. '"}]}' if not pcall(writefile, jsonPath, encoded) then return nil end local okFamily, family = pcall(getcustomasset, jsonPath) if okFamily and type(family) == 'string' and family ~= '' then return family end return nil end local function serve(path, kind) if kind == 'ttf' then return fontFamilyAsset(path) end local okAsset, asset = pcall(getcustomasset, path) if okAsset and type(asset) == 'string' and asset ~= '' then return asset end return nil end -- asset string for one pack url (cached file, or downloaded now); second value = it had to be downloaded function Util.assetFor(url) if string.sub(url, 1, 13) == 'rbxassetid://' then return url, false end if not filesAvailable() then return nil, false end local stem = Engine.Folder .. '/' .. Util.hashUrl(url) for _, kind in ipairs(FILE_TYPES) do local path = stem .. '.' .. kind if isfile(path) then local okRead, body = pcall(readfile, path) if okRead and Util.sniff(body) == kind then local asset = serve(path, kind) if asset then return asset, false end elseif type(delfile) == 'function' then pcall(delfile, path) -- a half written file from an earlier run end end end local body = download(url) local kind = Util.sniff(body) if not kind then return nil, true end local path = stem .. '.' .. kind if not pcall(writefile, path, body) then return nil, true end return serve(path, kind), true end -- run handler over list in ~4 ms slices; stops early when alive() turns false local function sliced(list, handler, alive) local clock = os.clock local started = clock() for index = 1, #list do if not alive() then return false end handler(list[index]) if clock() - started > 0.004 then task.wait() started = clock() end end return true end -- ------------------------------------------------------------------------------------------------------------ what counts as "the map" local PART_CLASSES = { Part = true, MeshPart = true, UnionOperation = true, WedgePart = true, CornerWedgePart = true, TrussPart = true, SpawnLocation = true, Seat = true, VehicleSeat = true, } local EXCLUDED_TOP = { Camera = true, Terrain = true, ViewModels = true, HurtEffect = true, ServerPing = true } local characterCache = weakKeys() local topCache = weakKeys() local function isCharacterModel(model) local known = characterCache[model] if known == nil then known = model:FindFirstChildOfClass('Humanoid') ~= nil or model:FindFirstChild('HumanoidRootPart') ~= nil or Players:GetPlayerFromCharacter(model) ~= nil characterCache[model] = known end return known end -- true for world content: not a character (at any depth), not a view model / camera / terrain, nothing the hub made (its objects are named ZkxHub...) -- (the world root is recognised by its class: a cloneref'd Workspace is never == to the instance you reach through .Parent) local function isMapObject(inst) local current, top = inst, inst while current and current.ClassName ~= 'Workspace' do if string.sub(current.Name, 1, 6) == 'ZkxHub' then return false end if current.ClassName == 'Model' and isCharacterModel(current) then return false end top = current current = current.Parent end if not current then return false end local allowed = topCache[top] if allowed == nil then allowed = not EXCLUDED_TOP[top.Name] topCache[top] = allowed end return allowed end Util.isMapObject = isMapObject -- ------------------------------------------------------------------------------------------------------------------------- texture packs local Texture = { Enabled = false, Pack = nil, Token = 0, IdMap = {}, FontMap = {}, -- family id -> font family asset (the json of a ttf) Kinds = { ['UI Images'] = true, ['Sounds'] = true, ['World Textures'] = true, ['Fonts'] = true }, Originals = {}, -- instance -> { prop -> the value before our swap } Replaced = {}, -- instance -> { prop -> what we put there }, so a swapped value is never swapped a second time Reverse = {}, -- our cached file value -> the original value it replaced (how a clone of a swapped template is recognised) Connections = {}, Stats = nil, } Engine.Texture = Texture local SKY_FACES = { 'SkyboxBk', 'SkyboxDn', 'SkyboxFt', 'SkyboxLf', 'SkyboxRt', 'SkyboxUp' } local TARGETS = { ImageLabel = { { Prop = 'Image', Kind = 'UI Images' } }, ImageButton = { { Prop = 'Image', Kind = 'UI Images' }, { Prop = 'HoverImage', Kind = 'UI Images' }, { Prop = 'PressedImage', Kind = 'UI Images' } }, Sound = { { Prop = 'SoundId', Kind = 'Sounds' } }, Decal = { { Prop = 'Texture', Kind = 'World Textures' } }, Texture = { { Prop = 'Texture', Kind = 'World Textures' } }, MeshPart = { { Prop = 'TextureID', Kind = 'World Textures' } }, Sky = {}, TextLabel = { { Prop = 'FontFace', Kind = 'Fonts', Font = true } }, TextButton = { { Prop = 'FontFace', Kind = 'Fonts', Font = true } }, TextBox = { { Prop = 'FontFace', Kind = 'Fonts', Font = true } }, } for _, face in ipairs(SKY_FACES) do table.insert(TARGETS.Sky, { Prop = face, Kind = 'World Textures' }) end local function textureRoots() local roots = { Workspace, game:GetService('ReplicatedStorage'), game:GetService('SoundService'), game:GetService('StarterGui'), Lighting } local player = Players.LocalPlayer if player then table.insert(roots, player) end return roots end -- one property of one instance: swap it when its asset id has a rule. Fonts compare the family (the swapped font keeps the label's weight and style). function Texture.SwapProp(inst, target) local prop = target.Prop local okRead, value = pcall(function() return inst[prop] end) if not okRead or value == nil then return end local current = value if target.Font then local okFamily, family = pcall(function() return value.Family end) if not okFamily then return end current = family end local ours = Texture.Replaced[inst] if ours and ours[prop] == current then return end -- still what we put there: never swap a swap (a skin id that is also a rule would chain) local id = Util.assetId(current) local replacement = id and (target.Font and Texture.FontMap[id] or Texture.IdMap[id]) if not replacement then -- the game builds live UI by cloning templates, and a clone of something we swapped arrives carrying our file: take it over, so it is put back too local origin = Texture.Reverse[current] if origin and not (ours and ours[prop] == current) then local restoreTo = origin if target.Font then local okFont, font = pcall(function() return Font.new(origin, value.Weight, value.Style) end) if not okFont then return end restoreTo = font end local saved = Texture.Originals[inst] if not saved then saved = {} Texture.Originals[inst] = saved end saved[prop] = restoreTo if not ours then ours = {} Texture.Replaced[inst] = ours end ours[prop] = current end return end if not target.Font and value == replacement then return end local newValue = replacement if target.Font then local okFont, font = pcall(function() return Font.new(replacement, value.Weight, value.Style) end) if not okFont then return end newValue = font end local saved = Texture.Originals[inst] if not saved then saved = {} Texture.Originals[inst] = saved end saved[prop] = value -- the newest original: the game may have set a new value since an earlier swap if pcall(function() inst[prop] = newValue end) then if not ours then ours = {} Texture.Replaced[inst] = ours end ours[prop] = replacement -- only values that are our own cached files can identify a clone (a plain asset id may be used by the game for something else), and a blank is no identity at all if string.sub(replacement, 1, 13) ~= 'rbxassetid://' and replacement ~= '' and Texture.Reverse[replacement] == nil then Texture.Reverse[replacement] = target.Font and current or value end end end function Texture.Swap(inst) local targets = TARGETS[inst.ClassName] if not targets then return end for _, target in ipairs(targets) do if Texture.Kinds[target.Kind] then Texture.SwapProp(inst, target) end end end -- puts the originals back, but only where the value is still the one we set: a newer value from the game is never overwritten with a stale one function Texture.Restore() for inst, props in pairs(Texture.Originals) do local ours = Texture.Replaced[inst] for prop, value in pairs(props) do pcall(function() local current = inst[prop] if prop == 'FontFace' then current = current.Family end if ours and ours[prop] ~= nil and ours[prop] == current then inst[prop] = value end end) end end table.clear(Texture.Originals) table.clear(Texture.Replaced) table.clear(Texture.Reverse) end local function disconnectAll(list) for _, connection in ipairs(list) do pcall(function() connection:Disconnect() end) end table.clear(list) end -- downloads (or finds cached) every file of a pack, a few at a time; returns idMap, fontMap + stats, or nil when the pack changed meanwhile function Texture.Prepare(name, token) local pack = Engine.Packs[name] if not pack then return nil, nil, { Missing = true } end ensureFolder() -- files to fetch: every distinct http url (an rbxassetid:// replacement and a blank rule need no file) local urls, seen = {}, {} local function want(url) if url ~= '' and not seen[url] and string.sub(url, 1, 13) ~= 'rbxassetid://' then seen[url] = true table.insert(urls, url) end end for _, rule in ipairs(pack.Rules) do want(rule[2]) end for _, rule in ipairs(pack.Fonts or {}) do want(rule[2]) end local assets, finished, fresh, failed = {}, 0, 0, 0 local nextIndex, running = 1, 0 local function worker() running += 1 while token == Texture.Token do local index = nextIndex nextIndex += 1 local url = urls[index] if not url then break end local ok, asset, downloaded = pcall(Util.assetFor, url) if ok and asset then assets[url] = asset else failed += 1 end if ok and downloaded then fresh += 1 end finished += 1 if finished % 4 == 0 or finished == #urls then status(string.format('%s pack: %d / %d files', name, finished, #urls)) end if finished % 6 == 0 then task.wait() end -- cached files never yield on their own: do not hold the frame end running -= 1 end for _ = 1, math.min(6, #urls) do task.spawn(worker) end while running > 0 and token == Texture.Token do task.wait(0.1) end if token ~= Texture.Token then return nil end local function assetOf(url) if url == '' then return '' end -- a blank rule: the asset is removed if string.sub(url, 1, 13) == 'rbxassetid://' then return url end return assets[url] end local idMap, fontMap, ids = {}, {}, 0 for _, rule in ipairs(pack.Rules) do local asset = assetOf(rule[2]) if asset ~= nil then for _, id in ipairs(rule[1]) do idMap[tostring(id)] = asset ids += 1 end end end for _, rule in ipairs(pack.Fonts or {}) do local family = assets[rule[2]] if family then for _, id in ipairs(rule[1]) do fontMap[tostring(id)] = family ids += 1 end end end return idMap, fontMap, { Files = #urls, Failed = failed, Fresh = fresh, Ids = ids, FilesOk = filesAvailable() } end function Texture.Scan(token) local list = {} for _, root in ipairs(textureRoots()) do local ok, found = pcall(function() return root:GetDescendants() end) if ok then table.move(found, 1, #found, #list + 1, list) end end sliced(list, function(inst) if TARGETS[inst.ClassName] then Texture.Swap(inst) end end, function() return token == Texture.Token end) end function Texture.Disable() Texture.Token += 1 Texture.Enabled = false disconnectAll(Texture.Connections) Texture.Restore() Texture.IdMap = {} Texture.FontMap = {} status('Off') end function Texture.Enable(name) Texture.Token += 1 local token = Texture.Token disconnectAll(Texture.Connections) Texture.Restore() Texture.IdMap = {} Texture.FontMap = {} Texture.Enabled = true Texture.Pack = name status(string.format('%s pack: starting', name)) task.spawn(function() local idMap, fontMap, stats = Texture.Prepare(name, token) if token ~= Texture.Token then return end if not idMap then status('Pack not found') return end Texture.IdMap, Texture.FontMap, Texture.Stats = idMap, fontMap, stats for _, root in ipairs(textureRoots()) do table.insert(Texture.Connections, root.DescendantAdded:Connect(function(inst) if TARGETS[inst.ClassName] then task.defer(Texture.Swap, inst) end end)) end Texture.Scan(token) if token ~= Texture.Token then return end if not stats.FilesOk then status(string.format('%s pack: this executor cannot save files, only built-in ids swap (%d)', name, stats.Ids)) elseif stats.Failed > 0 then status(string.format('%s pack ready: %d / %d files (%d failed)', name, stats.Files - stats.Failed, stats.Files, stats.Failed)) else status(string.format('%s pack ready: %d files', name, stats.Files)) end -- images the game sets after the object exists are only caught by looking again; sounds and decals arrive with their id local rounds = 0 while token == Texture.Token do task.wait(2.5) if token ~= Texture.Token then break end rounds += 1 if rounds % 4 == 0 then pruneDetached(Texture.Originals) pruneDetached(Texture.Replaced) end local player = Players.LocalPlayer local gui = player and player:FindFirstChildOfClass('PlayerGui') if gui and Texture.Kinds['UI Images'] then local ok, found = pcall(function() return gui:GetDescendants() end) if ok then sliced(found, function(inst) local class = inst.ClassName if class == 'ImageLabel' or class == 'ImageButton' then Texture.Swap(inst) end end, function() return token == Texture.Token end) end end end end) end -- quiet = only remember the choice (the caller is about to Enable); otherwise a running pack is re-applied for the new list function Texture.SetKinds(kinds, quiet) Texture.Kinds = { ['UI Images'] = kinds['UI Images'] == true, ['Sounds'] = kinds['Sounds'] == true, ['World Textures'] = kinds['World Textures'] == true, ['Fonts'] = kinds['Fonts'] == true } if not quiet and Texture.Enabled and next(Texture.IdMap) ~= nil then Texture.Restore() -- same session: put back what is no longer wanted, swap what is task.spawn(Texture.Scan, Texture.Token) end end -- throw away the downloaded files of one pack so the next Enable fetches them again function Texture.ClearCache(name) local pack = Engine.Packs[name] if not pack or not filesAvailable() or type(delfile) ~= 'function' then return 0 end local removed = 0 local function clear(rule) local url = rule[2] if url == '' or string.sub(url, 1, 13) == 'rbxassetid://' then return end local stem = Engine.Folder .. '/' .. Util.hashUrl(url) for _, kind in ipairs(FILE_TYPES) do local path = stem .. '.' .. kind if isfile(path) and pcall(delfile, path) then removed += 1 end if kind == 'ttf' and isfile(path .. '.json') then pcall(delfile, path .. '.json') end -- the font family file (not counted) end end for _, rule in ipairs(pack.Rules) do clear(rule) end for _, rule in ipairs(pack.Fonts or {}) do clear(rule) end return removed end -- -------------------------------------------------------------------------------------------------------------------------- map darkener local Darkener = { Enabled = false, Level = 6, Token = 0, Originals = {}, Connection = nil } Engine.Darkener = Darkener -- level 1 keeps 91 % of the brightness, level 10 keeps 10 % local function darkFactor() return 1 - (Darkener.Level / 10) * 0.9 end function Darkener.Paint(part) if not PART_CLASSES[part.ClassName] or not isMapObject(part) then return end local original = Darkener.Originals[part] if not original then original = part.Color Darkener.Originals[part] = original end local hue, saturation, value = original:ToHSV() part.Color = Color3.fromHSV(hue, saturation, value * darkFactor()) end function Darkener.Restore() for part, original in pairs(Darkener.Originals) do pcall(function() part.Color = original end) end table.clear(Darkener.Originals) end function Darkener.Enable(level) Darkener.Token += 1 local token = Darkener.Token Darkener.Level = level or Darkener.Level Darkener.Enabled = true if Darkener.Connection then Darkener.Connection:Disconnect() end Darkener.Connection = Workspace.DescendantAdded:Connect(function(inst) if PART_CLASSES[inst.ClassName] then task.defer(Darkener.Paint, inst) end end) task.spawn(function() local ok, found = pcall(function() return Workspace:GetDescendants() end) if ok then sliced(found, Darkener.Paint, function() return token == Darkener.Token end) end while token == Darkener.Token do -- parts of maps that are gone need not be remembered task.wait(20) if token == Darkener.Token then pruneDetached(Darkener.Originals) end end end) end function Darkener.SetLevel(level) Darkener.Level = level if not Darkener.Enabled then return end local token = Darkener.Token local parts = {} for part in pairs(Darkener.Originals) do table.insert(parts, part) end task.spawn(function() sliced(parts, function(part) local original = Darkener.Originals[part] if original and part.Parent then local hue, saturation, value = original:ToHSV() part.Color = Color3.fromHSV(hue, saturation, value * darkFactor()) end end, function() return token == Darkener.Token and Darkener.Enabled end) end) end function Darkener.Disable() Darkener.Token += 1 Darkener.Enabled = false if Darkener.Connection then Darkener.Connection:Disconnect() Darkener.Connection = nil end Darkener.Restore() end -- ----------------------------------------------------------------------------------------------------------------------------- anti-lag local AntiLag = { Enabled = false, Token = 0, Flags = { Materials = true, Textures = true, Shadows = true, Particles = true, Effects = true }, Originals = {}, Connections = {}, Quality = nil, } Engine.AntiLag = AntiLag local PARTICLE_CLASSES = { ParticleEmitter = true, Trail = true, Smoke = true, Fire = true, Sparkles = true } local EFFECT_CLASSES = { BloomEffect = true, BlurEffect = true, ColorCorrectionEffect = true, DepthOfFieldEffect = true, SunRaysEffect = true } local NONE = {} -- stored in place of nil (CustomPhysicalProperties is nil by default) local function remember(inst, prop) local saved = AntiLag.Originals[inst] if not saved then saved = {} AntiLag.Originals[inst] = saved end if saved[prop] == nil then local value = inst[prop] if value == nil then value = NONE end saved[prop] = value end end local function hubOwned(inst) return string.sub(inst.Name, 1, 6) == 'ZkxHub' end function AntiLag.Strip(inst) local class = inst.ClassName local flags = AntiLag.Flags if PART_CLASSES[class] then if not isMapObject(inst) then return end if flags.Materials and inst.Material ~= Enum.Material.SmoothPlastic then remember(inst, 'Material') remember(inst, 'CustomPhysicalProperties') -- friction / bounce stay what the material gave, only the look changes if inst.CustomPhysicalProperties == nil then inst.CustomPhysicalProperties = PhysicalProperties.new(inst.Material) end inst.Material = Enum.Material.SmoothPlastic end if flags.Shadows and inst.CastShadow then remember(inst, 'CastShadow') inst.CastShadow = false end elseif class == 'Decal' or class == 'Texture' then if flags.Textures and inst.Transparency < 1 and isMapObject(inst) then remember(inst, 'Transparency') inst.Transparency = 1 end elseif PARTICLE_CLASSES[class] then if flags.Particles and inst.Enabled and isMapObject(inst) then remember(inst, 'Enabled') inst.Enabled = false end elseif EFFECT_CLASSES[class] then if flags.Effects and inst.Enabled and not hubOwned(inst) then remember(inst, 'Enabled') inst.Enabled = false end end end function AntiLag.Restore() for inst, props in pairs(AntiLag.Originals) do for prop, value in pairs(props) do if rawequal(value, NONE) then value = nil end pcall(function() inst[prop] = value end) end end table.clear(AntiLag.Originals) end function AntiLag.SetQuality(on) local ok, rendering = pcall(function() return settings().Rendering end) if not ok or not rendering then return false end if on then if AntiLag.Quality == nil then AntiLag.Quality = rendering.QualityLevel end return pcall(function() rendering.QualityLevel = Enum.QualityLevel.Level01 end) end if AntiLag.Quality ~= nil then local previous = AntiLag.Quality AntiLag.Quality = nil return pcall(function() rendering.QualityLevel = previous end) end return true end function AntiLag.Scan() local token = AntiLag.Token local list = {} local ok, found = pcall(function() return Workspace:GetDescendants() end) if ok then table.move(found, 1, #found, 1, list) end local camera = Workspace.CurrentCamera for _, holder in ipairs({ Lighting, camera }) do if holder then for _, child in ipairs(holder:GetChildren()) do table.insert(list, child) end end end sliced(list, AntiLag.Strip, function() return token == AntiLag.Token and AntiLag.Enabled end) end function AntiLag.Enable(flags) AntiLag.Token += 1 AntiLag.Enabled = true if flags then AntiLag.Flags = flags end disconnectAll(AntiLag.Connections) local function watch(holder) if holder then table.insert(AntiLag.Connections, holder.DescendantAdded:Connect(function(inst) if PART_CLASSES[inst.ClassName] or PARTICLE_CLASSES[inst.ClassName] or EFFECT_CLASSES[inst.ClassName] or inst.ClassName == 'Decal' or inst.ClassName == 'Texture' then task.defer(AntiLag.Strip, inst) end end)) end end watch(Workspace) watch(Lighting) task.spawn(AntiLag.Scan) local token = AntiLag.Token task.spawn(function() while token == AntiLag.Token and AntiLag.Enabled do -- objects of maps that are gone need not be remembered task.wait(20) if token == AntiLag.Token then pruneDetached(AntiLag.Originals) end end end) end -- a changed option list: put everything back, then strip again with the new list function AntiLag.SetFlags(flags) AntiLag.Flags = flags if AntiLag.Enabled then AntiLag.Token += 1 AntiLag.Restore() task.spawn(AntiLag.Scan) end end function AntiLag.Disable() AntiLag.Token += 1 AntiLag.Enabled = false disconnectAll(AntiLag.Connections) AntiLag.Restore() end -- ------------------------------------------------------------------------------------------------------------------------------ no hands -- Hides the arms of the first person view model (Workspace.ViewModels: LeftArm / RightArm, their ShirtTexture decals and the wrapped meshes; gloves and sleeves by -- name). The game writes the arms' transparency every frame, so the hiding runs as the LAST render step. Weapons are never touched. local NoHands = { Enabled = false, Originals = {}, Tracked = {}, Hooked = weakKeys(), Connections = {}, BindName = 'ZkxHubNoHands' } Engine.NoHands = NoHands local ARM_NAMES = { LeftArm = true, RightArm = true, LeftArmWrapped = true, RightArmWrapped = true } local HAND_WORDS = { 'glove', 'sleeve', 'finger', 'wrist', 'palm', 'thumb' } local KEEP_WORDS = { 'handle', 'firearm', 'gun', 'barrel', 'scope', 'sight', 'mag', 'blade', 'knife', 'grip', 'trigger', 'muzzle', 'bullet', 'slide' } local function nameHas(name, words) for _, word in ipairs(words) do if string.find(name, word, 1, true) then return true end end return false end function NoHands.IsHandPart(inst) if not PART_CLASSES[inst.ClassName] or string.sub(inst.Name, 1, 6) == 'ZkxHub' then return false end if ARM_NAMES[inst.Name] then return true end local name = string.lower(inst.Name) if nameHas(name, KEEP_WORDS) then return false end return nameHas(name, HAND_WORDS) end local function trackHand(inst) if NoHands.Tracked[inst] then return end NoHands.Tracked[inst] = true NoHands.Originals[inst] = { Transparency = inst.Transparency, LocalTransparencyModifier = inst.LocalTransparencyModifier } end function NoHands.Consider(inst) if not NoHands.Enabled then return end local class = inst.ClassName if PART_CLASSES[class] then if NoHands.IsHandPart(inst) then trackHand(inst) for _, child in ipairs(inst:GetChildren()) do if child.ClassName == 'Decal' or child.ClassName == 'Texture' then trackHand(child) end end end elseif class == 'Decal' or class == 'Texture' then local parent = inst.Parent if parent and NoHands.Tracked[parent] then trackHand(inst) end end end function NoHands.Step() for inst in pairs(NoHands.Tracked) do if inst.Parent == nil then NoHands.Tracked[inst] = nil NoHands.Originals[inst] = nil else pcall(function() inst.Transparency = 1 inst.LocalTransparencyModifier = 1 end) end end end local function hookViewModels(folder) if NoHands.Hooked[folder] then return end NoHands.Hooked[folder] = true table.insert(NoHands.Connections, folder.DescendantAdded:Connect(function(inst) task.defer(NoHands.Consider, inst) end)) for _, inst in ipairs(folder:GetDescendants()) do NoHands.Consider(inst) end end function NoHands.Enable() if NoHands.Enabled then return end NoHands.Enabled = true local existing = Workspace:FindFirstChild('ViewModels') if existing then hookViewModels(existing) end table.insert(NoHands.Connections, Workspace.ChildAdded:Connect(function(child) if child.Name == 'ViewModels' and NoHands.Enabled then hookViewModels(child) end end)) pcall(function() RunService:UnbindFromRenderStep(NoHands.BindName) end) RunService:BindToRenderStep(NoHands.BindName, Enum.RenderPriority.Last.Value, NoHands.Step) end function NoHands.Disable() if not NoHands.Enabled then return end NoHands.Enabled = false pcall(function() RunService:UnbindFromRenderStep(NoHands.BindName) end) disconnectAll(NoHands.Connections) for inst, original in pairs(NoHands.Originals) do pcall(function() inst.Transparency = original.Transparency inst.LocalTransparencyModifier = original.LocalTransparencyModifier end) end table.clear(NoHands.Originals) table.clear(NoHands.Tracked) table.clear(NoHands.Hooked) end -- --------------------------------------------------------------------------------------------------------------------------------- unload function Engine.Destroy() Texture.Disable() Darkener.Disable() AntiLag.Disable() AntiLag.SetQuality(false) NoHands.Disable() end return Engine end)() TextureEngine.Packs = (function() -- Texture packs: rules only (asset id -> replacement url). Generated by tools\make_texture_packs.py, do not edit by hand. -- Minecraft: the rule table of pastebin.com/raw/Cb9ExUkB (files: 66buncer/Rivals-Pack-Assets + InventivetalentDev/minecraft-assets) -- Fleasion: the rule table of fleasion-script (files: prism658/Resources-fleasion) -- Fleasion: dropped mine.webp (webp) for ids 133917828562858, 121503061771505 return { Minecraft = { Rules = { { { 7658055825 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/item_slot_3_blue_hollow.png' }, { { 17138490999 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/random/levelup.ogg' }, { { 103035811146294 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/random/click.ogg' }, { { 16810041280 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/mob/villager/yes1.ogg' }, { { 16810321565 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/mob/villager/no1.ogg' }, { { 16770456156, 16492958314 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/mob/enderdragon/wings1.ogg' }, { { 16537337310, 16537449730 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/random/orb.ogg' }, { { 17826470563 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/note/pling.ogg' }, { { 15109829804 }, 'https://66mods-assets.pages.dev/repos/66buncer/Rivals-Pack-Assets/headshot_silverfish_kill.ogg' }, { { 18221725850, 18239670056, 18221726246 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/records/chirp.ogg' }, { { 18239670367 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/mob/zombie/say1.ogg' }, { { 17026600996 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/random/anvil_land.ogg' }, { { 16530229616 }, 'https://66mods-assets.pages.dev/repos/66buncer/Rivals-Pack-Assets/kill1_zombie_hurt.ogg' }, { { 16530229541 }, 'https://66mods-assets.pages.dev/repos/66buncer/Rivals-Pack-Assets/kill2_ghast_death.ogg' }, { { 16530229695 }, 'https://66mods-assets.pages.dev/repos/66buncer/Rivals-Pack-Assets/kill3_wither_death.ogg' }, { { 99115398611290 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/note/pling.ogg' }, { { 8483887957 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/mob/chicken/say1.ogg' }, { { 6384899588 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/music/menu/menu1.ogg' }, { { 70643163489676 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/random/anvil_land.ogg' }, { { 91547731028928 }, 'https://66mods-assets.pages.dev/repos/66buncer/Rivals-Pack-Assets/demote_anvil_break.ogg' }, { { 138975587469438 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/random/levelup.ogg' }, { { 17697682466, 17733314783 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/music/menu/menu2.ogg' }, { { 177266782 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/random/wood_click.ogg' }, { { 18525513345 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/note/bd.ogg' }, { { 13110130082 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/random/successful_hit.ogg' }, { { 17467242617 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/mob/enderdragon/growl1.ogg' }, { { 14812965418 }, 'https://66mods-assets.pages.dev/repos/66buncer/Rivals-Pack-Assets/molotov_idle_fire.ogg' }, { { 14812827622 }, 'https://66mods-assets.pages.dev/repos/66buncer/Rivals-Pack-Assets/molotov_ignite.ogg' }, { { 13455969017 }, 'https://66mods-assets.pages.dev/repos/66buncer/Rivals-Pack-Assets/rpg_explosion.ogg' }, { { 16812389263 }, 'https://66mods-assets.pages.dev/repos/66buncer/Rivals-Pack-Assets/molotov_extinguish_fizz.ogg' }, { { 114274252176516 }, 'https://66mods-assets.pages.dev/repos/66buncer/Rivals-Pack-Assets/portal_ambience.ogg' }, { { 17259538274 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/note/pling.ogg' }, { { 17769583566 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/random/pop.ogg' }, { { 18221897857 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/mob/wither/spawn.ogg' }, { { 106551800007995 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/random/bow.ogg' }, { { 114467311328776 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/mob/endermen/idle1.ogg' }, { { 13160401062 }, 'https://66mods-assets.pages.dev/repos/InventivetalentDev/minecraft-assets/assets/minecraft/sounds/entity/player/attack/strong1.ogg' }, { { 85163949920258 }, 'https://66mods-assets.pages.dev/repos/66buncer/Rivals-Pack-Assets/jumppad_place_slime.ogg' }, { { 17835965985 }, 'https://66mods-assets.pages.dev/repos/66buncer/Rivals-Pack-Assets/jumppad_bounce_slime.ogg' }, { { 82715240396507 }, 'https://66mods-assets.pages.dev/repos/66buncer/Rivals-Pack-Assets/crossbow_shoot1.ogg' }, { { 112269321366473 }, 'https://66mods-assets.pages.dev/repos/66buncer/Rivals-Pack-Assets/crossbow_loading_start.ogg' }, { { 76155503538875 }, 'https://66mods-assets.pages.dev/repos/66buncer/Rivals-Pack-Assets/crossbow_loading_end.ogg' }, { { 13270206222, 13270206087 }, 'https://66mods-assets.pages.dev/repos/66buncer/Rivals-Pack-Assets/sniper_candidate_enderdragon_hit.ogg' }, { { 13220167337 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/item_slot_1_grey.png' }, { { 13188242420 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/item_slot_2_red.png' }, { { 13220167472 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/item_slot_3_blue.png' }, { { 13188153054 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/item_slot_4_green.png' }, { { 13188242287 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/item_slot_5_gold.png' }, { { 17803962335 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/welcome.png' }, { { 81461991645938 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/lvl_eye_new.png' }, { { 99135525400251 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/ender_eye.png' }, { { 133793956251748 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/arch.png' }, { { 116941545385923 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/nem.png' }, { { 8276600102 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/heart_hp.png' }, { { 126928245743331 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/killfeed_headshot_skull.png' }, { { 17838290166, 17619444538, 108220438104376 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/icon_x.png' }, { { 17838317115 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/icon_plus.png' }, { { 17562685319 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/icon_back_arrow.png' }, { { 113844621753627 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/icon_next_arrow.png' }, { { 17229230506 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/icon_lock.png' }, { { 18175187129, 17495953455 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/icon_key.png' }, { { 14641612286, 17619445191 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/icon_gear_settings.png' }, { { 17175095665 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/icon_trophy.png' }, { { 16802957270 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/bone.png' }, { { 133917828562858, 121503061771505 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/egg.png' }, { { 108088064658436 }, 'https://raw.githubusercontent.com/66buncer/Rivals-Pack-Assets/main/arenamine.png' }, }, }, Fleasion = { Rules = { { { 7658055825 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/grods.png' }, { { 17803962335 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/attachment.png' }, { { 13854780213 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/logo.png' }, { { 13854780042 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/heartleft.png' }, { { 81461991645938 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/lvl.png' }, { { 16802957270 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/heartbroken.png' }, { { 99115398611290 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/icbtw.png' }, { { 17175092502, 17094014569 }, 'rbxassetid://72106446453934' }, { { 106623367501544, 131795064007344, 73543520622815 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/f.png' }, { { 80716950169934 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/d-.png' }, { { 136100661820261 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/d.png' }, { { 107898816876115 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/d+.png' }, { { 134520747948636 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/c-.png' }, { { 114166096331502 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/c.png' }, { { 90039594400813 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/c+.png' }, { { 133903971285645 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/b-.png' }, { { 82834564754747 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/b.png' }, { { 73345783863790 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/b+.png' }, { { 113997689031026 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/a-.png' }, { { 88059506918419 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/a.png' }, { { 112183171942172 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/a+.png' }, { { 104871954739030 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/s.png' }, { { 109012386782238 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/ss.png' }, { { 127982903682334 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/u.png' }, { { 133793956251748 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/top200xrank.png' }, { { 116941545385923 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/nemx.png' }, { { 17138490999 }, 'rbxassetid://1721952882' }, { { 103035811146294 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/allclear.base.ogg' }, { { 16810041280 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/allclear.base.ogg' }, { { 16810321565 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/allclear.base.ogg' }, { { 16770456156, 16492958314 }, 'rbxassetid://2428506580' }, { { 16537337310, 16537449730 }, 'rbxassetid://70643163489676' }, { { 17826470563 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/theeyellow.ogg' }, { { 18221725850, 18239670056, 18221726246 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/winnerwinner.ogg' }, { { 17026600996 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/showscore.base.ogg' }, { { 16530229616 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/kill1.ogg' }, { { 16530229541 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/kill2.ogg' }, { { 16530229695 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/kill3.ogg' }, { { 8483887957 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/clearspin.base.ogg' }, { { 6384899588 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/lego-build.mp3' }, { { 16737738420 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/Sliding.mp3' }, { { 70643163489676 }, 'rbxassetid://17405655409' }, { { 91547731028928 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/studio-audience-awwww-sound-fx.mp3' }, { { 138975587469438 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/final-fantasy-vii-victory-fanfare-1.mp3' }, { { 17697682466, 17733314783 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/kuchu-toshi.mp3' }, { { 177266782 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/menuclick.base.ogg' }, { { 18525513345 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/achievement_3.base.ogg' }, { { 13110130082 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/target.base.ogg' }, { { 17467242617 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/bluer.ogg' }, { { 17016581922 }, 'https://raw.githubusercontent.com/prism658/Resources-fleasion/main/elimin.mp3' }, }, }, } end)() TextureEngine.Packs.Determination = (function() -- Determination pack: rules only (asset id -> replacement). Generated by tools\make_determination_pack.py, do not edit by hand. -- Source: the rule table of `Determination Texture Pack.txt` (Magic Redux / DETERMINATION; files: prism658/undertale-txtpack). -- '' blanks the asset (ambience removed); Fonts = pixel fonts that replace the game's UI font families. -- Font family 12187365977 was added to the pixel font rule: it is the family the Rivals UI uses (3,523 of 3,528 text objects, checked 2026-11-02 in the live -- client); the pack's own 11 ids no longer appear anywhere, so without it the font would never change. -- Syne Mono font: dropped SyneMono-Regular.ttf (HTTP Error 404: Not Found) for ids 127338559130032, 16294586406, 75156669118637, 93477262732302 -- Bronze 3: ids 106623367501544, 131795064007344 belong to a later rule -- Bronze 3: dropped f.png (HTTP Error 404: Not Found) for ids 73543520622815 return { Rules = { { { 6384899588 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/AUDIO_APPEARANCE.wav' }, { { 8483887957 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/snd_weaponpull.wav' }, { { 16537337310, 16537449730 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/snd_break1.wav' }, { { 17016581922 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/break2.ogg' }, { { 16810321565 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/snd_closet_impact.ogg' }, { { 16770456156 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/mus_sfx_a_lithit.wav' }, { { 177266782 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/snd_select.wav' }, { { 16492958314 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/snd_scytheburst.wav' }, { { 103035811146294 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/snd_b.wav' }, { { 16810041280 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/mus_sfx_abreak.wav' }, { { 16530229695, 16530229541, 16530229616 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/mus_sfx_gigapunch.wav' }, { { 17138490999 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/heal.ogg' }, { { 18525513345 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/snd_dimbox.wav' }, { { 13110130082 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/snd_credit_s.wav' }, { { 17153811469 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/snd_breakc.wav' }, { { 17826470563 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/Finale.ogg' }, { { 17026600996 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/snd_heavydamage.wav' }, { { 17467242617 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/sdfinal.ogg' }, { { 18221725850, 18239670056, 18221726246 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/snd_dumbvictory.wav' }, { { 17697682466, 17733314783 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/Hotel.mp3' }, { { 7658055825 }, 'rbxassetid://6372755229' }, { { 17813065011, 17813065464, 12099785239 }, '' }, { { 2108482005, 14147881792, 135908632589654, 84214501374682, 10196550937, 12261809766, 2108545280, 10196550667, 14147882149, 103020541883227, 89972436184102, 12261813110, 14147883091, 2108482395, 10196550128, 14147882405, 2108482542, 10196549902, 14147881297, 72960281658487, 92138082970751, 2108482676, 10196567794, 12261813678, 14147882761, 10196550367, 2108482231 }, 'rbxassetid://7658055825' }, { { 133917828562858, 121503061771505 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/58a20c59c8dd3432c6fa8221.png' }, { { 133793956251748 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/Archnemisis_heart_1.png' }, { { 116941545385923 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/Nemisis_heart_1.png' }, { { 80716950169934 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/Silver_1_heart.png' }, { { 136100661820261 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/silver_2_heart.png' }, { { 107898816876115 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/silver_3_heart.png' }, { { 134520747948636 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/Gold_1_heart.png' }, { { 114166096331502 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/Gold_2_heart.png' }, { { 90039594400813 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/Gold_3_heart.png' }, { { 133903971285645 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/Plat_1_heart.png' }, { { 82834564754747 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/Plat_2_heart.png' }, { { 73345783863790 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/Plat_3_heart.png' }, { { 113997689031026 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/Diam_1_heart.png' }, { { 88059506918419 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/Diam_2_heart.png' }, { { 112183171942172 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/Diam_3_heart.png' }, { { 104871954739030 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/Onyx_1_heart.png' }, { { 109012386782238 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/Onyx_2_heart.png' }, { { 127982903682334 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/Onyx_3_heart.png' }, { { 106623367501544 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/bronze_1_heart_fixed_2_times.png' }, { { 131795064007344 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/bronze_2_heart.png' }, { { 17175092502, 17094014569 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/streak.png' }, { { 111599878354131 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/unranked_heart.png' }, { { 13854780042 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/heartleft.png' }, { { 13854780213 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/heartright.png' }, { { 17803962335 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/undlogo.png' }, { { 16802957270 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/heartbroken.png' }, { { 81461991645938 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/lvl.png' }, { { 13188153054, 13188242420, 13220167472, 13220167337 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/lvl.png' }, }, Fonts = { { { 12187323909, 12187320363, 12187354260, 12187342816, 12187280273, 12187303601, 12187262242, 12187288714, 12187341500, 12187271237, 12187341020, 12187365977 }, 'https://raw.githubusercontent.com/prism658/undertale-txtpack/main/PixelOperator-Bold.ttf' }, }, } end)() -- World tab: Texture Changer (left), Map Darkener and Anti-Lag (right). The engine above does the work; this part is only menu and glue. RivalsRuntimeBridge.TextureEngine = TextureEngine local TX_KINDS = { 'UI Images', 'Sounds', 'World Textures', 'Fonts' } local TX_STRIP = { 'Smooth Materials', 'Map Textures', 'Part Shadows', 'Particles & Trails', 'Post Effects' } local function pickedValues(id) local option = Options[id] return option and option.Value or {} end -- Texture Changer ------------------------------------------------------------------------------------------------------ local textureStatus local function syncTexture() if Toggles.TXEnabled and Toggles.TXEnabled.Value == true then TextureEngine.Texture.SetKinds(pickedValues('TXKinds'), true) TextureEngine.Texture.Enable((Options.TXPack and Options.TXPack.Value) or 'Minecraft') else TextureEngine.Texture.Disable() end end local function syncNoHands() if Toggles.TXNoHands and Toggles.TXNoHands.Value == true then TextureEngine.NoHands.Enable() else TextureEngine.NoHands.Disable() end end TextureEngine.OnStatus = function(text) if textureStatus then textureStatus:SetText(text) end end local TextureGroup = WorldTab:AddLeftGroupbox('Texture Changer') TextureGroup:AddToggle('TXEnabled', { Text = 'Texture Changer', Default = false, Tooltip = 'Swaps the Rivals icons, sounds, fonts and map texture for a texture pack. A pack downloads once (into ZkxHub/textures) and loads from disk after that. Off puts the original assets back.', Callback = GuardRivalsCallback('TextureChanger_Toggle', syncTexture), }) local TextureBox = TextureGroup:AddDependencyBox() TextureBox:AddDropdown('TXPack', { Values = { 'Minecraft', 'Fleasion', 'Determination' }, Default = 'Minecraft', Multi = false, AllowNull = false, Text = 'Pack', Tooltip = 'Minecraft: block icons, Minecraft sounds and the hollow slot frame. Fleasion: custom rank icons, new sounds and a custom map grid. Determination: Undertale hearts for the ranks, retro sounds, a pixel font and a new sky, with the ambience removed (about 4 MB, downloaded once). Only one pack runs at a time.', Callback = GuardRivalsCallback('TextureChanger_Pack', function() if Toggles.TXEnabled and Toggles.TXEnabled.Value == true then syncTexture() end end), }) TextureBox:AddDropdown('TXKinds', { Values = TX_KINDS, Default = TX_KINDS, Multi = true, AllowNull = true, Text = 'Replace', Tooltip = 'UI Images: icons and menu pictures. Sounds: hit, kill and menu sounds. World Textures: decals, mesh textures such as the map grid, and the sky. Fonts: the pixel font of the Determination pack (no other pack has one).', Callback = GuardRivalsCallback('TextureChanger_Kinds', function(value) TextureEngine.Texture.SetKinds(value or {}) end), }) textureStatus = TextureBox:AddLabel('Pack: off', true) TextureBox:AddButton({ Text = 'Redownload Pack', DoubleClick = true, Tooltip = 'Deletes the downloaded files of the selected pack and fetches them again. Use it when an icon or a sound looks broken.', Func = GuardRivalsCallback('TextureChanger_Redownload', function() local name = (Options.TXPack and Options.TXPack.Value) or 'Minecraft' TextureEngine.Texture.Disable() local removed = TextureEngine.Texture.ClearCache(name) Library:Notify({ Title = 'Texture Changer', Description = string.format('Removed %d cached file(s) of the %s pack.', removed, name), Time = 4 }) if Toggles.TXEnabled and Toggles.TXEnabled.Value == true then syncTexture() end end), }) TextureBox:SetupDependencies({ { Toggles.TXEnabled, true } }) -- the No Hands switch itself sits in Client Effects > Model Chams (build_vader.py patch_client_effects, id TXNoHands); the engine is here. That tab is built -- first, so a switch restored as on is applied now, whichever of the two ran first. RivalsRuntimeBridge.SyncNoHands = syncNoHands syncNoHands() -- Map Darkener ----------------------------------------------------------------------------------------------------------- local function syncDarkener() if Toggles.TXDarkener and Toggles.TXDarkener.Value == true then TextureEngine.Darkener.Enable((Options.TXDarkLevel and Options.TXDarkLevel.Value) or 6) else TextureEngine.Darkener.Disable() end end local DarkGroup = WorldTab:AddRightGroupbox('Map Darkener') DarkGroup:AddToggle('TXDarkener', { Text = 'Map Darkener', Default = false, Tooltip = 'Darkens the map pieces so players and your weapon stand out. Only the map changes: characters, view models and the hub are left alone. Off puts every colour back.', Callback = GuardRivalsCallback('MapDarkener_Toggle', syncDarkener), }) local DarkBox = DarkGroup:AddDependencyBox() DarkBox:AddSlider('TXDarkLevel', { Text = 'Darkness', Default = 6, Min = 1, Max = 10, Rounding = 0, Compact = true, Tooltip = '1 = a little darker, 10 = nearly black.', Callback = GuardRivalsCallback('MapDarkener_Level', function(value) TextureEngine.Darkener.SetLevel(value) end), }) DarkBox:SetupDependencies({ { Toggles.TXDarkener, true } }) -- Anti-Lag --------------------------------------------------------------------------------------------------------------- local function lagFlags() local picked = pickedValues('TXLagStrip') return { Materials = picked['Smooth Materials'] == true, Textures = picked['Map Textures'] == true, Shadows = picked['Part Shadows'] == true, Particles = picked['Particles & Trails'] == true, Effects = picked['Post Effects'] == true, } end local function syncLag() local on = Toggles.TXAntiLag ~= nil and Toggles.TXAntiLag.Value == true if on then TextureEngine.AntiLag.Enable(lagFlags()) else TextureEngine.AntiLag.Disable() end TextureEngine.AntiLag.SetQuality(on and Toggles.TXLagQuality ~= nil and Toggles.TXLagQuality.Value == true) end local LagGroup = WorldTab:AddRightGroupbox('Anti-Lag') LagGroup:AddToggle('TXAntiLag', { Text = 'Anti-Lag', Default = false, Tooltip = 'Makes the map cheaper to draw. Players, your weapon and the hub are not touched and everything is put back when it is switched off. Visual only: the game still plays the same.', Callback = GuardRivalsCallback('AntiLag_Toggle', syncLag), }) local LagBox = LagGroup:AddDependencyBox() LagBox:AddDropdown('TXLagStrip', { Values = TX_STRIP, Default = TX_STRIP, Multi = true, AllowNull = true, Text = 'Strip', Tooltip = 'Smooth Materials: flat plastic look (friction stays). Map Textures: decals and stains. Part Shadows: parts stop casting shadows. Particles & Trails: dust, smoke and fire. Post Effects: bloom, blur, colour correction, depth of field, sun rays.', Callback = GuardRivalsCallback('AntiLag_Strip', function() if Toggles.TXAntiLag and Toggles.TXAntiLag.Value == true then TextureEngine.AntiLag.SetFlags(lagFlags()) end end), }) LagBox:AddToggle('TXLagQuality', { Text = 'Lowest Graphics Quality', Default = false, Tooltip = 'Also sets the Roblox graphics level to 1 while Anti-Lag is on and restores your level afterwards.', Callback = GuardRivalsCallback('AntiLag_Quality', function() TextureEngine.AntiLag.SetQuality(Toggles.TXAntiLag ~= nil and Toggles.TXAntiLag.Value == true and Toggles.TXLagQuality.Value == true) end), }) LagBox:SetupDependencies({ { Toggles.TXAntiLag, true } }) Library:OnUnload(GuardRivalsCallback('TextureChanger_Unload', function() TextureEngine.Destroy() end)) -- World tab: Rivals Settings (right column). The game's own options (SettingsLibrary.Info) set through PlayerDataController:SetSetting at identity 2 -- (executor threads cannot require the game's modules). Local only: nothing is sent to the server by the hub, and switching it off puts every option back. -- Checked 2026-11-02 in the live client: the game reacts (Lighting.GlobalShadows follows 'Shadows Disabled'), and the restore is exact. -- (The FFlags section that used to sit beside it was removed on request: Potassium's setfflag does not change the engine's flags.) do -- his own export of the game's options (the file named "Rivals FFlags") shows these ones matter for clarity / performance; hotkeys, crosshair and volumes stay yours local RIVALS_PRESET = { { Name = 'Camera FOV 120', Setting = 'Camera FOV', Value = 120 }, { Name = 'No Camera Shake', Setting = 'Camera Shake', Value = false }, { Name = 'No FOV Effects', Setting = 'Camera FOV Effects', Value = false }, { Name = 'Hide Muzzle Flashes (Aiming)', Setting = 'Aiming Hides Muzzle Flashes', Value = true }, { Name = 'Shadows Disabled', Setting = 'Shadows Disabled', Value = true }, } local presetNames = {} for _, entry in ipairs(RIVALS_PRESET) do table.insert(presetNames, entry.Name) end local SettingsGroup = WorldTab:AddRightGroupbox('Rivals Settings') local settingsStatus local saved = {} -- setting name -> value it had before the preset local Settings = { Saved = saved } RivalsRuntimeBridge.SettingsPreset = Settings local function atGameIdentity(fn) local getId, setId = getthreadidentity, setthreadidentity or setidentity local old = getId and getId() or nil if setId then pcall(setId, 2) end local ok, a, b = pcall(fn) if setId and old then pcall(setId, old) end return ok, a, b end local function controller() local ok, module = pcall(function() return require(game:GetService('Players').LocalPlayer.PlayerScripts.Controllers.PlayerDataController) end) return ok and module or nil end function Settings.Get(name) local data = controller() if not data then return nil, false end local ok, value = atGameIdentity(function() return data:GetSetting(name) end) return value, ok end function Settings.Set(name, value) local data = controller() if not data then return false end return (atGameIdentity(function() data:SetSetting(name, value) end)) end function Settings.Restore() local count = 0 for name, value in pairs(saved) do if Settings.Set(name, value) then count += 1 end end table.clear(saved) return count end -- apply exactly the picked entries, put back the ones that are no longer picked function Settings.Sync(on, picked) if not on then return Settings.Restore() end local wanted = {} for _, entry in ipairs(RIVALS_PRESET) do if picked[entry.Name] == true then wanted[entry.Setting] = entry.Value end end for name, value in pairs(saved) do if wanted[name] == nil and Settings.Set(name, value) then saved[name] = nil end end local done = 0 for name, value in pairs(wanted) do local current, ok = Settings.Get(name) if ok then if saved[name] == nil then saved[name] = current end if current == value or Settings.Set(name, value) then done += 1 end end end return done end local function syncSettings() local on = Toggles.RSPreset ~= nil and Toggles.RSPreset.Value == true local picked = Options.RSEntries and Options.RSEntries.Value or {} local count = Settings.Sync(on, picked) if settingsStatus then settingsStatus:SetText(on and string.format('%d setting(s) applied', count) or 'Off: your own settings') end end SettingsGroup:AddToggle('RSPreset', { Text = 'Rivals Settings Preset', Default = false, Tooltip = 'Sets the picked game options for this session through the game\'s own settings (nothing is sent to the server by the hub). Off puts every option back to what it was.', Callback = GuardRivalsCallback('RivalsSettings_Toggle', syncSettings), }) local SettingsBox = SettingsGroup:AddDependencyBox() SettingsBox:AddDropdown('RSEntries', { Values = presetNames, Default = presetNames, Multi = true, AllowNull = true, Text = 'Options', Tooltip = 'Camera FOV 120 is the game maximum. Shadows Disabled: turning it off again needs a game restart (the game says so).', Callback = GuardRivalsCallback('RivalsSettings_Entries', function() if Toggles.RSPreset and Toggles.RSPreset.Value == true then syncSettings() end end), }) settingsStatus = SettingsBox:AddLabel('Off: your own settings', true) SettingsBox:SetupDependencies({ { Toggles.RSPreset, true } }) Library:OnUnload(GuardRivalsCallback('RivalsSettings_Unload', function() Settings.Restore() end)) end end do -- Watermark: the Rivals lookups for the Map and Server details (the library only knows the generic ones) if Library.SetWatermarkProvider then local Players = cloneref(game:GetService('Players')) local World = cloneref(workspace) local function text(value) if type(value) == 'string' and value ~= '' then return value end return nil end -- the name of the map of the match you are in; the lobby and the shooting range have none local function resolveMap() local player = Players.LocalPlayer local scripts = player and player:FindFirstChild('PlayerScripts') local controllers = scripts and scripts:FindFirstChild('Controllers') local module = controllers and controllers:FindFirstChild('DuelController') local controller = module and RivalsRuntimeBridge.RequireGameModule(module, 'DuelController') local duel = type(controller) == 'table' and controller.CurrentObject or nil if duel then if type(duel.GetMapName) == 'function' then local ok, name = pcall(duel.GetMapName, duel) if ok and text(name) then return name end end if type(duel.Get) == 'function' then for _, key in ipairs({ 'MapName', 'Map' }) do local ok, name = pcall(duel.Get, duel, key) if ok and text(name) then return name end end end return text(duel.MapName) or text(duel.Map) or 'Match' end for _, attribute in ipairs({ 'MapName', 'MatchMap' }) do local name = text(World:GetAttribute(attribute)) if name then return name end end return 'Lobby' end -- the region of the server: the game keeps it as a workspace attribute and shows it in the top bar local function resolveServer() local region = text(World:GetAttribute('ServerRegion')) if region then return region end local player = Players.LocalPlayer local node = player and player:FindFirstChildOfClass('PlayerGui') for _, name in ipairs({ 'InsetGui', 'MainFrame', 'Container', 'RightButtons', 'Details', 'Container', 'ServerRegion', 'Title' }) do node = node and node:FindFirstChild(name) end return node and text(node.Text) or nil end Library:SetWatermarkProvider('Map', resolveMap, 2) Library:SetWatermarkProvider('Server', resolveServer, 5) end end RivalsRuntimeBridge.Extras.BuildVisualsUi(CrosshairTab) end -- ====================================================================== -- Menu: ragebot (ZHub) -- ====================================================================== BuildRivalsRagebotUi = function(Window) local ZHubRage = RequireSharedModule('ZHub_ragebot') local ZHub = { ProfileDefaults = {}, Pushers = {}, LastActive = nil, Started = false, Rage = ZHubRage } RivalsRuntimeBridge.ZHub = ZHub -- Every control registers a push function (UI value -> ragebot setting) and a default for the profile files. local function Register(id, kind, default, push) ZHub.ProfileDefaults[#ZHub.ProfileDefaults + 1] = { idx = id, type = kind, value = default } ZHub.Pushers[id] = push end local function AddZHubToggle(box, id, text, default, tooltip, path) local function push() local obj = Toggles[id] if obj then ZHubRage.Set(path, obj.Value == true) end end Register(id, 'Toggle', default, push) return box:AddToggle(id, { Text = text, Default = default, Tooltip = tooltip, Callback = GuardRivalsCallback('ZHubRagebot_' .. id, push), }) end local function AddZHubSlider(box, id, text, default, min, max, rounding, tooltip, path, suffix) local function push() local obj = Options[id] if obj and type(obj.Value) == 'number' then ZHubRage.Set(path, obj.Value) end end Register(id, 'Slider', default, push) return box:AddSlider(id, { Text = text, Default = default, Min = min, Max = max, Rounding = rounding, Suffix = suffix or '', Compact = true, Tooltip = tooltip, Callback = GuardRivalsCallback('ZHubRagebot_' .. id, push), }) end local function AddZHubDropdown(box, id, text, values, default, tooltip, path, map) local function push() local obj = Options[id] if obj and obj.Value ~= nil then ZHubRage.Set(path, map and map[obj.Value] or obj.Value) end end Register(id, 'Dropdown', default, push) return box:AddDropdown(id, { Values = values, Default = default, Multi = false, Text = text, Tooltip = tooltip, Callback = GuardRivalsCallback('ZHubRagebot_' .. id, push), }) end -- Min/Max pair (the original menu has a range slider): two sliders pushed as one sorted range. local function AddZHubRange(box, idMin, idMax, text, defaultMin, defaultMax, min, max, rounding, tooltip, path) local function push() local lo, hi = Options[idMin], Options[idMax] if lo and hi and type(lo.Value) == 'number' and type(hi.Value) == 'number' then ZHubRage.Set({ path[1], path[2], path[3], path[4], 'Min' }, math.min(lo.Value, hi.Value)) ZHubRage.Set({ path[1], path[2], path[3], path[4], 'Max' }, math.max(lo.Value, hi.Value)) end end Register(idMin, 'Slider', defaultMin, push) Register(idMax, 'Slider', defaultMax, push) local callback = GuardRivalsCallback('ZHubRagebot_' .. idMin, push) box:AddSlider(idMin, { Text = text .. ' (min)', Default = defaultMin, Min = min, Max = max, Rounding = rounding, Compact = true, Tooltip = tooltip, Callback = callback, }) box:AddSlider(idMax, { Text = text .. ' (max)', Default = defaultMax, Min = min, Max = max, Rounding = rounding, Compact = true, Tooltip = tooltip, Callback = callback, }) end local MODE_MAP = { ['Off'] = 'Off', ['Random'] = 'Random', ['Translocate'] = 'Translocate', ['Projectile Breaker'] = 'ProjectileBreaker', } local ON_EMPTY_MAP = { ['Reload'] = 'Reload', ['Swap'] = 'Swap', ['Swap or Reload'] = 'SwapOrReload', } local PRIORITY_MAP = { ['Primary > Secondary > Melee'] = { 'Primary', 'Secondary', 'Melee' }, ['Primary > Melee > Secondary'] = { 'Primary', 'Melee', 'Secondary' }, ['Secondary > Primary > Melee'] = { 'Secondary', 'Primary', 'Melee' }, ['Secondary > Melee > Primary'] = { 'Secondary', 'Melee', 'Primary' }, ['Melee > Primary > Secondary'] = { 'Melee', 'Primary', 'Secondary' }, ['Melee > Secondary > Primary'] = { 'Melee', 'Secondary', 'Primary' }, } local PRIORITY_VALUES = { 'Primary > Secondary > Melee', 'Primary > Melee > Secondary', 'Secondary > Primary > Melee', 'Secondary > Melee > Primary', 'Melee > Primary > Secondary', 'Melee > Secondary > Primary', } -- Enabled = toggle AND key state (Always / Toggle / Hold come from the key picker next to the toggle). -- The ragebot hooks the game only once it is first switched on. local function ComputeActive() local toggle = Toggles.KR_Enabled if not (toggle and toggle.Value == true) then return false end local picker = Options.KR_EnabledK if picker and type(picker.GetState) == 'function' then return picker:GetState() == true end return true end local function SyncEnabled(force) local active = ComputeActive() if active and not ZHub.Started then ZHub.Started = true ZHubRage.Start() end if force or active ~= ZHub.LastActive then ZHub.LastActive = active ZHubRage.SetEnabled(active) end end -- The hub's own Cheater Detector feeds Prioritize Hackers (the ragebot also tags by its own rules). local function FeedHackers() local tags = ZHubRage.Tags local detector = RivalsRuntimeBridge.CheaterDetector if not (tags and detector and detector.Flagged) then return end for player in pairs(detector.Flagged) do if player.Parent ~= nil then tags:Add(player, 'Hacker') end end end function ZHub.PushAll() for _, push in pairs(ZHub.Pushers) do pcall(push) end end RivalsRuntimeBridge.ZHubRagebotActive = function() return ZHub.LastActive == true end RivalsRuntimeBridge.ZHubRagebotSync = function() SyncEnabled(false) FeedHackers() end RivalsRuntimeBridge.ZHubRagebotDestroy = function() ZHub.LastActive = false pcall(ZHubRage.Unload) end -- its own icon (Combat already has the crosshair): skull when the icon registry has it, the built-in axe otherwise local RagebotIcon = (Library.GetCustomIcon and Library:GetCustomIcon('skull')) and 'skull' or 'axe' local Tab = Window:AddTab('Ragebot', RagebotIcon) ZHub.Tab = Tab local Activation = Tab:AddLeftGroupbox('Ragebot') local EnableToggle = Activation:AddToggle('KR_Enabled', { Text = 'Enable Ragebot', Default = false, Tooltip = 'Master switch. The key next to it sets how it turns on: Always, Toggle or Hold. The ragebot only starts hooking the game the first time it is switched on.', Callback = GuardRivalsCallback('ZHubRagebot_Enabled', function() SyncEnabled(false) end), }) EnableToggle:AddKeyPicker('KR_EnabledK', { Default = 'Unknown', Mode = 'Always', Text = 'Ragebot', NoUI = false }) Register('KR_Enabled', 'Toggle', false, function() SyncEnabled(true) end) ZHub.ProfileDefaults[#ZHub.ProfileDefaults + 1] = { idx = 'KR_EnabledK', type = 'KeyPicker', mode = 'Always', key = 'Unknown', modifiers = {} } AddZHubToggle( Activation, 'KR_Hackers', 'Prioritize Hackers', false, 'Targets players flagged as hackers first. Flags come from the ragebot (camera pitch past a full turn, or over 200 studs/s for a quarter second) and from the Cheater Detector when it is on.', { 'Ragebot', 'PrioritizeHackers' } ) AddZHubSlider( Activation, 'KR_Stability', 'Stability', 0.15, 0, 1.5, 3, 'How many seconds before an enemy is expected back inside the 4,194,304 stud limit that shots are allowed. Higher fires earlier.', { 'Ragebot', 'Stability' }, ' s' ) AddZHubSlider( Activation, 'KR_ShootFrames', 'Shoot Frames', 1, 1, 5, 0, 'How many frames the fire lock stays open after a shot is allowed.', { 'Ragebot', 'ShootFrames' } ) Activation:AddLabel('Only one ragebot runs at a time. The original teleport ragebot is in the right column; switching one on turns the other one\'s features off.', true) local StatusLabel = Activation:AddLabel('Status: not started', true) task.spawn(function() while not Library.Unloaded do pcall(function() StatusLabel:SetText('Status: ' .. tostring(ZHubRage.Status)) end) task.wait(0.5) end end) local Weapons = Tab:AddLeftGroupbox('Weapon Strategy') AddZHubToggle( Weapons, 'KR_WPrimary', 'Primary Enabled', true, 'Allow the primary slot.', { 'Ragebot', 'Weapons', 'Enabled', 'Primary' } ) AddZHubToggle( Weapons, 'KR_WSecondary', 'Secondary Enabled', true, 'Allow the secondary slot.', { 'Ragebot', 'Weapons', 'Enabled', 'Secondary' } ) AddZHubToggle( Weapons, 'KR_WMelee', 'Melee Enabled', true, 'Allow the melee slot.', { 'Ragebot', 'Weapons', 'Enabled', 'Melee' } ) do local function push() local obj = Options.KR_Priority if obj and PRIORITY_MAP[obj.Value] then ZHubRage.Set({ 'Ragebot', 'Weapons', 'Priority' }, table.clone(PRIORITY_MAP[obj.Value])) end end Register('KR_Priority', 'Dropdown', PRIORITY_VALUES[1], push) Weapons:AddDropdown('KR_Priority', { Values = PRIORITY_VALUES, Default = PRIORITY_VALUES[1], Multi = false, Text = 'Weapon Priority', Tooltip = 'Preferred slot first.', Callback = GuardRivalsCallback('ZHubRagebot_Priority', push), }) end AddZHubDropdown( Weapons, 'KR_OnEmpty', 'On Empty', { 'Reload', 'Swap', 'Swap or Reload' }, 'Swap or Reload', 'Reload: reload the empty gun in place. Swap: switch to another weapon, never reload. Swap or Reload: swap first, reload only when nothing else is usable.', { 'Ragebot', 'Weapons', 'OnEmpty' }, ON_EMPTY_MAP ) local Evasion = Tab:AddLeftGroupbox('Evasion') AddZHubDropdown( Evasion, 'KR_Mode', 'Evasion Mode', { 'Off', 'Random', 'Translocate', 'Projectile Breaker' }, 'Random', 'Where your server-side position is parked. Translocate parks you and never shoots.', { 'Ragebot', 'Evasion', 'Mode' }, MODE_MAP ) local RandomBox = Evasion:AddDependencyBox() AddZHubToggle( RandomBox, 'KR_RandAnchor', 'Character Origin', false, 'Centre the ring on your character instead of the origin.', { 'Ragebot', 'Evasion', 'Random', 'AnchorFromCharacter' } ) AddZHubSlider( RandomBox, 'KR_RandRadius', 'Base Radius', 100, 5, 100000000, 0, 'Ring radius. Right-click the slider to type a value.', { 'Ragebot', 'Evasion', 'Random', 'BaseRadius' } ) AddZHubSlider( RandomBox, 'KR_RandFactor', 'Random Factor', 0.5, 0, 1, 1, 'The radius is picked between Base and Base x (1 + factor).', { 'Ragebot', 'Evasion', 'Random', 'RadiusRandomFactor' } ) RandomBox:SetupDependencies({ { Options.KR_Mode, 'Random' } }) local TranslocateBox = Evasion:AddDependencyBox() AddZHubSlider( TranslocateBox, 'KR_TransOffset', 'Offset', -5, -5, 5, 1, 'Vertical offset from the bottom of the out-of-bounds volume.', { 'Ragebot', 'Evasion', 'Translocate', 'Offset' } ) TranslocateBox:SetupDependencies({ { Options.KR_Mode, 'Translocate' } }) local BreakerBox = Evasion:AddDependencyBox() local PB = { 'Ragebot', 'Evasion', 'ProjectileBreaker' } AddZHubRange( BreakerBox, 'KR_PBFwdMin', 'KR_PBFwdMax', 'Forward Depth', 0, 4, 0, 10, 1, 'Range of the forward offset into the surface.', { PB[1], PB[2], PB[3], 'DepthForward' } ) AddZHubSlider( BreakerBox, 'KR_PBFwdFreq', 'Forward Frequency', 5, 0, 20, 1, 'Speed of the forward offset sweep.', { PB[1], PB[2], PB[3], 'DepthForwardFrequency' } ) AddZHubRange( BreakerBox, 'KR_PBUpMin', 'KR_PBUpMax', 'Upward Depth', 0, 5.5, 0, 10, 1, 'Range of the downward offset into the surface.', { PB[1], PB[2], PB[3], 'DepthUp' } ) AddZHubSlider( BreakerBox, 'KR_PBUpFreq', 'Upward Frequency', 5, 0, 20, 1, 'Speed of the vertical offset sweep.', { PB[1], PB[2], PB[3], 'DepthUpFrequency' } ) AddZHubSlider( BreakerBox, 'KR_PBInterval', 'Reposition Interval', 0.3, 0.05, 2, 2, 'How often a new surface is picked.', { PB[1], PB[2], PB[3], 'RepositionInterval' }, ' s' ) AddZHubToggle( BreakerBox, 'KR_PBFallAnchor', 'Fallback Character Origin', false, 'Fallback ring centred on your character instead of the origin.', { PB[1], PB[2], PB[3], 'FallbackAnchorFromCharacter' } ) AddZHubSlider( BreakerBox, 'KR_PBFallRadius', 'Fallback Radius', 100, 5, 100000000, 0, 'Ring radius used when no surface is available. Right-click to type a value.', { PB[1], PB[2], PB[3], 'FallbackBaseRadius' } ) AddZHubSlider( BreakerBox, 'KR_PBFallFactor', 'Fallback Random Factor', 0.5, 0, 1, 1, 'Radius spread of the fallback ring.', { PB[1], PB[2], PB[3], 'FallbackRadiusRandomFactor' } ) BreakerBox:SetupDependencies({ { Options.KR_Mode, 'Projectile Breaker' } }) end -- ====================================================================== -- Menu: classic ragebot -- ====================================================================== RivalsRuntimeBridge.BuildClassicRagebotUi = function(Window) -- The original teleport ragebot, kept next to the new one in the same Ragebot tab: the new one fills the left column, this one the right. -- Its four setting groups are the tabs of one box (the old 'Sections' dropdown is gone). -- Same control ids as before (P8S4T1, P8S4T1K, RB*), so configs saved with the old ragebot still load. local Tab = RivalsRuntimeBridge.ZHub and RivalsRuntimeBridge.ZHub.Tab if not Tab then Tab = Window:AddTab('Old Ragebot', 'axe') end local Activation = Tab:AddRightGroupbox('Old Ragebot') local RageToggle = Activation:AddToggle('P8S4T1', { Text = 'Enable Old Ragebot', Default = false, Tooltip = 'The original ragebot: teleports above the chosen enemy and shoots from there. Hides in the void while reloading or swapping.', }) RageToggle:AddKeyPicker('P8S4T1K', { Default = 'Unknown', Mode = 'Toggle', SyncToggleState = true, Text = 'Old Rage', }) Activation:AddLabel('Only one ragebot runs at a time. Switching one on turns the other one\'s features off. Old doesnt mean not working.', true) -- two tabs per box: the tab buttons share the width of one column, four of them overlapped (the hub font is wide) local Aiming = Tab:AddRightTabbox('Classic Targeting') local Targeting = Aiming:AddTab('Targeting') Targeting:AddDropdown( 'RBTM', { Text = 'Target Mode', Values = { 'Closest', 'Lowest Health' }, Default = 'Closest', Multi = false } ) Targeting:AddToggle('RBAP', { Text = 'Priority Targets', Default = true, Tooltip = 'Prefer the targets ticked below.' }) Targeting:AddToggle( 'RBPV', { Text = 'Prioritize Void Players', Default = true, Tooltip = 'Players hiding far out of bounds.' } ) Targeting:AddToggle( 'RBPA', { Text = 'Prioritize Armed Players', Default = true, Tooltip = 'Players currently holding a weapon.' } ) local Movement = Aiming:AddTab('Movement') Movement:AddSlider( 'RBOY', { Text = 'Height Above Target', Default = 1, Min = -5, Max = 50, Rounding = 1, Compact = true } ) Movement:AddToggle( 'RBHV', { Text = 'Hover In Place', Default = true, Tooltip = 'Freezes you at the spot above the target between steps.' } ) Movement:AddToggle('RBNC', { Text = 'Noclip', Default = true }) Movement:AddToggle('RBAA', { Text = 'Anti-Aim Spin', Default = false, Tooltip = 'Wobbles your facing while hovering.' }) local Gear = Tab:AddRightTabbox('Classic Weapons') local Weapons = Gear:AddTab('Weapons') Weapons:AddToggle( 'RBFS', { Text = 'Fast Shoot', Default = true, Tooltip = 'Zero cooldowns, recoil and spread, max projectile speed. Restored when rage turns off.', } ) Weapons:AddToggle('RBNS', { Text = 'No Spread Packets', Default = true }) Weapons:AddSlider('RBSA', { Text = 'Shots Per Tick', Default = 1, Min = 1, Max = 10, Rounding = 0, Compact = true }) Weapons:AddSlider( 'RBTD', { Text = 'Fire Delay', Default = 0.04, Min = 0.01, Max = 1, Rounding = 2, Suffix = 's', Compact = true } ) Weapons:AddDropdown( 'RBPW', { Text = 'Preferred Weapon', Values = { 'primary', 'secondary', 'melee' }, Default = 'primary', Multi = false } ) Weapons:AddToggle('RBAE', { Text = 'Auto Equip Preferred', Default = true }) Weapons:AddToggle( 'RBAS', { Text = 'Auto Swap On Empty', Default = true, Tooltip = 'Swaps weapons (then reloads) when the magazine is empty.' } ) local Void = Gear:AddTab('Void') Void:AddToggle('RBVR', { Text = 'Void While Reloading', Default = true }) Void:AddToggle( 'RBVK', { Text = 'Void When No Enemies', Default = true, Tooltip = 'Hides far away once everyone is dead.' } ) Void:AddSlider( 'RBSV', { Text = 'Shotgun Reload Void', Default = 5, Min = 5, Max = 30, Rounding = 0, Suffix = 's', Compact = true } ) Void:AddSlider('RBVI', { Text = 'Jumps Per Frame', Default = 500, Min = 1, Max = 500, Rounding = 0, Compact = true }) Void:AddSlider('RBVN', { Text = 'Min Distance', Default = 1, Min = 1, Max = 500000, Rounding = 0, Compact = true }) Void:AddSlider('RBVD', { Text = 'Max Distance', Default = 50000, Min = 1, Max = 500000, Rounding = 0, Compact = true }) -- the two ragebots would fight over the character, so switching one on switches the other off, and so are all of the other one's features. -- 'new' = the ZHub ragebot, 'old' = this one. A parked feature remembers its value: switching its ragebot back on restores it. local FEATURES = { old = { { id = 'RBAP', off = false, default = true }, { id = 'RBPV', off = false, default = true }, { id = 'RBPA', off = false, default = true }, { id = 'RBHV', off = false, default = true }, { id = 'RBNC', off = false, default = true }, { id = 'RBAA', off = false, default = false }, { id = 'RBFS', off = false, default = true }, { id = 'RBNS', off = false, default = true }, { id = 'RBAE', off = false, default = true }, { id = 'RBAS', off = false, default = true }, { id = 'RBVR', off = false, default = true }, { id = 'RBVK', off = false, default = true }, }, new = { { id = 'KR_Hackers', off = false, default = false }, { id = 'KR_WPrimary', off = false, default = true }, { id = 'KR_WSecondary', off = false, default = true }, { id = 'KR_WMelee', off = false, default = true }, { id = 'KR_Mode', off = 'Off', default = 'Random' }, { id = 'KR_RandAnchor', off = false, default = false }, { id = 'KR_PBFallAnchor', off = false, default = false }, }, } local Exclusive = { Parked = { old = {}, new = {} } } RivalsRuntimeBridge.RagebotExclusive = Exclusive local function Control(id) return Toggles[id] or Options[id] end local function SetFeature(id, value) local control = Control(id) if control and control.Value ~= value then control:SetValue(value) end end local function ParkFeatures(group) local parked = Exclusive.Parked[group] for _, feature in ipairs(FEATURES[group]) do local control = Control(feature.id) if control and control.Value ~= feature.off then parked[feature.id] = control.Value control:SetValue(feature.off) end end end local function RestoreFeatures(group) local parked = Exclusive.Parked[group] if next(parked) ~= nil then for id, value in pairs(parked) do SetFeature(id, value) end table.clear(parked) return end -- nothing remembered (a saved config kept the set parked): a set that is entirely off would leave its ragebot crippled, so it gets its defaults for _, feature in ipairs(FEATURES[group]) do local control = Control(feature.id) if control and control.Value ~= feature.off then return end end for _, feature in ipairs(FEATURES[group]) do SetFeature(feature.id, feature.default) end end -- group = the ragebot that just went on function Exclusive.Apply(group) ParkFeatures(group == 'new' and 'old' or 'new') RestoreFeatures(group) end -- after a config load or import the loaded values are the truth: forget what was parked, then enforce the rule again function Exclusive.Reconcile() table.clear(Exclusive.Parked.old) table.clear(Exclusive.Parked.new) local ZHub, classic = Toggles.KR_Enabled, Toggles.P8S4T1 if ZHub and ZHub.Value == true then if classic and classic.Value == true then classic:SetValue(false) end Exclusive.Apply('new') elseif classic and classic.Value == true then Exclusive.Apply('old') end end local function SwitchOff(toggle) if toggle and toggle.Value == true then toggle:SetValue(false) end end RageToggle:OnChanged(GuardRivalsCallback('ClassicRagebot_Exclusive', function() if RageToggle.Value == true then SwitchOff(Toggles.KR_Enabled) Exclusive.Apply('old') end end)) local ZHubToggle = Toggles.KR_Enabled if ZHubToggle then local previous = ZHubToggle.Changed ZHubToggle:OnChanged(GuardRivalsCallback('ClassicRagebot_ExclusiveZHub', function(...) if previous then previous(...) end if ZHubToggle.Value == true then SwitchOff(Toggles.P8S4T1) Exclusive.Apply('new') end end)) end end -- ====================================================================== -- Menu: cosmetics -- ====================================================================== BuildRivalsCosmeticsUi = function(MiscTab) local P5S4 = MiscTab:AddLeftGroupbox('Unlock All') P5S4:AddToggle('P5S1T2', { Text = 'Unlock All Cosmetics', Default = false, Tooltip = "Unlocks skins, wraps, charms, finishers, and emotes in the game's Customize menus.", Callback = GuardRivalsCallback('Cosmetics_Enabled_Changed', function(enabled) if enabled then pcall(RivalsRuntimeBridge.ApplyRivalsCosmetics) pcall(RivalsRuntimeBridge.RefreshNativeRivalsCosmetics) else pcall(RivalsRuntimeBridge.RestoreRivalsCosmetics) end end), }) P5S4:AddToggle('P5S1T4', { Text = 'Unlock All Weapons', Default = false, Tooltip = 'Shows every weapon from ItemLibrary in the picker and loadout UI. Server-side ownership checks may still reject picking weapons you do not own.', Callback = GuardRivalsCallback('Cosmetics_WeaponUnlockChanged', function() if RivalsRuntimeBridge.RebuildRivalsWeaponUnlock then pcall(RivalsRuntimeBridge.RebuildRivalsWeaponUnlock) end end), }) local RankCharmModule = RequireSharedModule('rank_charm_spoofer') local RankCharm = { Module = RankCharmModule, ProfileDefaults = {} } RivalsRuntimeBridge.RankCharm = RankCharm local KEEP_CHARM = 'Keep equipped' local rankCatalog = RankCharmModule.GetCatalog() local rankNames = rankCatalog and rankCatalog.RankNames or { 'Unranked', 'Bronze 1', 'Bronze 2', 'Bronze 3', 'Silver 1', 'Silver 2', 'Silver 3', 'Gold 1', 'Gold 2', 'Gold 3', 'Platinum 1', 'Platinum 2', 'Platinum 3', 'Diamond 1', 'Diamond 2', 'Diamond 3', 'Onyx 1', 'Onyx 2', 'Onyx 3', 'Nemesis', 'Archnemesis', } local charmLabels = { KEEP_CHARM } for _, label in ipairs(rankCatalog and rankCatalog.SeasonLabels or { 'Season 0', 'Season 1', 'Season 2', 'Season 3' }) do charmLabels[#charmLabels + 1] = label end local boardRankName = 'Archnemesis' for _, rankName in ipairs(rankNames) do local rank = rankCatalog and rankCatalog.Ranks[rankName] if rank and rank.BoardCap ~= nil then boardRankName = rankName end end local function RegisterRankCharmDefault(id, kind, value) local entry = { idx = id, type = kind } if kind == 'Input' then entry.text = value else entry.value = value end RankCharm.ProfileDefaults[#RankCharm.ProfileDefaults + 1] = entry end function RankCharm.PushAll() local toggle, charm, rank, board, every = Toggles.KC_Enabled, Options.KC_Charm, Options.KC_Rank, Options.KC_Board, Toggles.KC_All RankCharmModule.Configure({ Enabled = toggle ~= nil and toggle.Value == true, CharmLabel = charm and charm.Value or KEEP_CHARM, Rank = rank and rank.Value or boardRankName, Leaderboard = board and tonumber(board.Value) or 1, ForceAll = every ~= nil and every.Value == true, }) end local onRankCharmChanged = GuardRivalsCallback('RankCharm_Changed', function() RankCharm.PushAll() end) local RC = MiscTab:AddLeftGroupbox('Ranked Charm Spoofer') RegisterRankCharmDefault('KC_Enabled', 'Toggle', false) RC:AddToggle('KC_Enabled', { Text = 'Ranked Charm Spoofer', Default = false, Tooltip = 'Shows the rank model you pick on your ranked (Season) charm. Only your own screen changes; other players still see your real charm.', Callback = onRankCharmChanged, }) RegisterRankCharmDefault('KC_Charm', 'Dropdown', KEEP_CHARM) RC:AddDropdown('KC_Charm', { Values = charmLabels, Default = KEEP_CHARM, Multi = false, Text = 'Charm', Tooltip = "Keep equipped: only Season charms you already have on a weapon change rank. Pick a season to swap whatever charm is on your weapons for that season's charm.", Callback = onRankCharmChanged, }) RegisterRankCharmDefault('KC_Rank', 'Dropdown', boardRankName) RC:AddDropdown('KC_Rank', { Values = rankNames, Default = boardRankName, Multi = false, Text = 'Rank', Tooltip = 'The rank model shown on the charm.', Callback = onRankCharmChanged, }) local RankBoardBox = RC:AddDependencyBox() RegisterRankCharmDefault('KC_Board', 'Input', '1') RankBoardBox:AddInput('KC_Board', { Default = '1', Numeric = true, Finished = true, AllowEmpty = true, EmptyReset = '1', Text = 'Leaderboard Rank', Placeholder = '1 - 200', Tooltip = 'The "#" number printed on the top rank charm (1 to 200).', Callback = onRankCharmChanged, }) RankBoardBox:SetupDependencies({ { Options.KC_Rank, boardRankName } }) RegisterRankCharmDefault('KC_All', 'Toggle', false) RC:AddToggle('KC_All', { Text = 'Show on every weapon', Default = false, Tooltip = 'Also puts the charm on weapons that have none equipped. Applies when the weapon spawns.', Callback = onRankCharmChanged, }) RC:AddButton('Apply Now', function() RankCharm.PushAll() RankCharmModule.Refresh() end) local RankCharmStatus = RC:AddLabel('Status: starting', true) task.spawn(function() while not Library.Unloaded do pcall(function() RankCharmStatus:SetText('Status: ' .. tostring(RankCharmModule.Status)) end) task.wait(0.5) end end) task.spawn(GuardRivalsCallback('RankCharm_Init', function() for _ = 1, 60 do if RankCharmModule.Init() then break end task.wait(1) end RankCharm.PushAll() end)) Library:OnUnload(GuardRivalsCallback('RankCharm_Unload', function() pcall(RankCharmModule.Unload) end)) RivalsCosmeticsState.CosmeticsUiLoaded = true end -- ====================================================================== -- Menu: Automation tab -- ====================================================================== BuildRivalsPickupUi = function(Window) local automationTab = Window:AddTab('Automation', 'zap') local P3S1 = automationTab:AddLeftGroupbox('Game Modes') P3S1:AddToggle('P8S1T1', { Text = 'Auto Heal', Default = true, Tooltip = 'Automatically grabs health packs when you are low.', }) P3S1:AddToggle('P8S1T2', { Text = 'Auto Ammo Pickup', Default = true, Tooltip = 'Automatically grabs ammo packs when your magazine is empty.', }) P3S1:AddToggle('P8S4T2', { Text = 'Auto Respawn', Default = false, Tooltip = 'Respawns you automatically when the death prompt appears.', }) Toggles.P8S1T1:OnChanged(GuardRivalsCallback('AutoHeal_ToggleChanged', function(value) if value and UpdateRivalsPickupFeatures then UpdateRivalsPickupFeatures() end end)) Toggles.P8S1T2:OnChanged(GuardRivalsCallback('AutoAmmo_ToggleChanged', function(value) if value and UpdateRivalsPickupFeatures then UpdateRivalsPickupFeatures() end end)) local P3S8 = automationTab:AddLeftGroupbox('Subspace Tripmines') P3S8:AddToggle('P8S8T1', { Text = 'Auto Trigger', Default = false, Tooltip = 'Automatically sets off enemy tripmines.', }) P3S8:AddToggle('P8S8T2', { Text = 'Team Trigger', Default = false, Tooltip = 'Also triggers your own and allied tripmines.', }) local P3S5 = automationTab:AddRightGroupbox('Auto Loadout & Ban') P3S5:AddToggle('P8S5T1', { Text = 'Auto Loadout', Default = false, Tooltip = 'Fills your pick screen once per open using the selected slot fallbacks.', }) P3S5:AddToggle('P8S5T3', { Text = 'Per Map', Default = false, Tooltip = 'Keeps a separate set of loadouts for each selected map.', }) local DepAutoLoadoutMap = P3S5:AddDependencyBox() DepAutoLoadoutMap:AddDropdown('P8S5D7', { Values = RivalsRuntimeBridge.ResolveAutoLoadoutMapValues(), Default = 1, Multi = false, Text = 'Map', Searchable = true, MaxVisibleDropdownItems = 12, }) DepAutoLoadoutMap:SetupDependencies({ { Toggles.P8S5T3, true } }) P3S5:AddToggle('P8S5T4', { Text = 'Multiple Loadouts', Default = false, Tooltip = 'Uses all your loadouts (loadout 1 first) as item-by-item fallbacks when a weapon is unavailable.', }) local AutoLoadoutKeyLabels = {} local function ApplyAutoLoadoutCount(rawCount) local count = RivalsRuntimeBridge.SetAutoLoadoutCount(rawCount) local priorityOption = Options.P8S5D8 if priorityOption then local values = {} for index, priority in ipairs(RivalsRuntimeBridge.ResolveAutoLoadoutPriorities()) do values[index] = priority end priorityOption:SetValues(values) if not table.find(values, priorityOption.Value) then priorityOption:SetValue('1') end end for index, keyLabel in ipairs(AutoLoadoutKeyLabels) do keyLabel:SetVisible(index <= count) end return count end P3S5:AddSlider('P8S5N', { Text = 'Loadouts', Default = 3, Min = 1, Max = 10, Rounding = 0, Compact = true, Tooltip = 'How many loadouts exist (1 to 10). Add Loadout below does this for you.', }) local AddLoadoutButton = P3S5:AddButton({ Text = 'Add Loadout', Tooltip = 'Makes the next loadout (up to 10), turns Multiple Loadouts on and selects the new loadout so you can pick its weapons straight away.', Func = GuardRivalsCallback('AutoLoadout_Add', function() local current = math.floor( tonumber(Options.P8S5N and Options.P8S5N.Value) or #RivalsRuntimeBridge.ResolveAutoLoadoutPriorities() ) if current >= 10 then Library:Notify({ Title = 'Auto Loadout', Description = '10 loadouts is the maximum.', Time = 3 }) return end local nextCount = current + 1 if Toggles.P8S5T4 and Toggles.P8S5T4.Value ~= true then Toggles.P8S5T4:SetValue(true) end Options.P8S5N:SetValue(nextCount) Options.P8S5D8:SetValue(tostring(nextCount)) end), }) AddLoadoutButton:AddButton({ Text = 'Remove Last', Tooltip = 'Removes the last loadout. Its weapons stay in the config and come back if you add it again.', Func = GuardRivalsCallback('AutoLoadout_Remove', function() local current = math.floor( tonumber(Options.P8S5N and Options.P8S5N.Value) or #RivalsRuntimeBridge.ResolveAutoLoadoutPriorities() ) if current <= 1 then Library:Notify({ Title = 'Auto Loadout', Description = 'You need at least one loadout.', Time = 3 }) return end Options.P8S5N:SetValue(current - 1) end), }) P3S5:AddDropdown('P8S5D8', { Values = RivalsRuntimeBridge.ResolveAutoLoadoutPriorities(), Default = '1', Multi = false, Text = 'Loadout', Tooltip = 'The loadout that gets equipped (and edited). With Multiple Loadouts on, this is the loadout being edited while all your loadouts act as fallbacks.', }) local LoadoutBanner = nil local LoadoutNoticePending = false local function LoadoutBannerText() local selected = Options.P8S5D8 and Options.P8S5D8.Value or '1' return 'Loadout ' .. tostring(selected) .. ' / ' .. tostring(#RivalsRuntimeBridge.ResolveAutoLoadoutPriorities()) end local function RefreshLoadoutBanner() if Library.Unloaded then return end local wanted = Toggles.P8S5T6 and Toggles.P8S5T6.Value == true if wanted and not LoadoutBanner then local ok, banner = pcall(function() return Library:AddDraggableLabel(LoadoutBannerText()) end) if ok and banner then LoadoutBanner = banner banner.Label.TextSize = 18 banner.Label.AnchorPoint = Vector2.new(0.5, 0) banner.Label.Position = UDim2.new(0.5, 0, 0, 10) end elseif not wanted and LoadoutBanner then pcall(function() LoadoutBanner:Destroy() end) LoadoutBanner = nil end if LoadoutBanner then LoadoutBanner:SetText(LoadoutBannerText()) end end local function ShowLoadoutNotice() if Toggles.P8S5T7 and Toggles.P8S5T7.Value == true then Library:Notify({ Title = 'Loadout ' .. tostring(Options.P8S5D8.Value), Description = 'Switched to ' .. LoadoutBannerText():lower(), Time = 2, }) end end -- One key for every loadout: each press moves to the next one (1, 2, 3 ... then back to 1) local function CycleLoadout() local list = RivalsRuntimeBridge.ResolveAutoLoadoutPriorities() local option = Options.P8S5D8 if not option or #list == 0 then return end local nextValue = list[(table.find(list, option.Value) or 0) % #list + 1] LoadoutNoticePending = true if nextValue ~= option.Value then option:SetValue(nextValue) end if LoadoutNoticePending then LoadoutNoticePending = false RefreshLoadoutBanner() ShowLoadoutNotice() end end P3S5:AddLabel('Next Loadout Key'):AddKeyPicker('P8S5D8KC', { Text = 'Next Loadout', Default = 'Unknown', Mode = 'Press', Tooltip = 'One key for every loadout: each press moves to the next loadout (1, 2, 3 ... then back to 1).', Callback = GuardRivalsCallback('AutoLoadout_KeyCycle', CycleLoadout), }) P3S5:AddToggle('P8S5T6', { Text = 'Loadout Banner', Default = true, Tooltip = 'Keeps the current loadout number on screen. Drag it anywhere while the menu is open.', Callback = GuardRivalsCallback('AutoLoadout_Banner', RefreshLoadoutBanner), }) P3S5:AddToggle('P8S5T7', { Text = 'Loadout Notification', Default = true, Tooltip = 'Shows a short notification with the loadout number each time the key switches it.', }) RivalsRuntimeBridge.AutoWeaponMenu.BuildMenu(P3S5) local DepAutoLoadout = P3S5:AddDependencyBox() DepAutoLoadout:AddDropdown('P8S5D1', { Values = RivalsRuntimeBridge.ResolveAutoLoadoutWeaponValues('Primary'), Default = RivalsRuntimeBridge.ResolveAutoLoadoutDefaultWeapon('Primary'), Multi = false, Text = 'Primary', Tooltip = 'Primary weapon for the selected map and loadout priority.', }) DepAutoLoadout:AddDropdown('P8S5D2', { Values = RivalsRuntimeBridge.ResolveAutoLoadoutWeaponValues('Secondary'), Default = RivalsRuntimeBridge.ResolveAutoLoadoutDefaultWeapon('Secondary'), Multi = false, Text = 'Secondary', Tooltip = 'Secondary weapon for the selected map and loadout priority.', }) DepAutoLoadout:AddDropdown('P8S5D3', { Values = RivalsRuntimeBridge.ResolveAutoLoadoutWeaponValues('Melee'), Default = RivalsRuntimeBridge.ResolveAutoLoadoutDefaultWeapon('Melee'), Multi = false, Text = 'Melee', Tooltip = 'Melee weapon for the selected map and loadout priority.', }) DepAutoLoadout:AddDropdown('P8S5D4', { Values = RivalsRuntimeBridge.ResolveAutoLoadoutWeaponValues('Utility'), Default = RivalsRuntimeBridge.ResolveAutoLoadoutDefaultWeapon('Utility'), Multi = false, Text = 'Utility', Tooltip = 'Utility weapon for the selected map and loadout priority.', }) DepAutoLoadout:SetupDependencies({ { Toggles.P8S5T1, true } }) RivalsRuntimeBridge.RefreshAutoLoadoutDropdownValues() Toggles.P8S5T1:OnChanged(GuardRivalsCallback('AutoLoadout_ToggleChanged', function(value) if value then RivalsRuntimeBridge.QueueAutoLoadoutSubmission() else RivalsRuntimeBridge.ResetAutoLoadoutState() end end)) for _, optionId in ipairs({ 'P8S5D1', 'P8S5D2', 'P8S5D3', 'P8S5D4' }) do local option = Options[optionId] if option and type(option.OnChanged) == 'function' then option:OnChanged(GuardRivalsCallback(string.format('AutoLoadout_%s_Changed', optionId), function() if RivalsAutoLoadoutState.RefreshingDropdownValues > 0 then return end -- a config load sets the options one by one in any order; the profiles are restored from its metadata afterwards if RivalsRuntimeBridge.IsConfigLoading() then return end RivalsRuntimeBridge.SyncAutoLoadoutSelection(optionId) RivalsRuntimeBridge.QueueAutoLoadoutSubmission() end)) end end local function RefreshAutoLoadoutProfile() if RivalsAutoLoadoutState.RefreshingDropdownValues > 0 then return end RivalsRuntimeBridge.RefreshAutoLoadoutDropdownValues() if RivalsRuntimeBridge.IsConfigLoading() then return end RivalsRuntimeBridge.QueueAutoLoadoutSubmission() end Toggles.P8S5T3:OnChanged(GuardRivalsCallback('AutoLoadout_PerMapChanged', RefreshAutoLoadoutProfile)) Toggles.P8S5T4:OnChanged(GuardRivalsCallback('AutoLoadout_MultipleChanged', RefreshAutoLoadoutProfile)) Options.P8S5D7:OnChanged(GuardRivalsCallback('AutoLoadout_MapChanged', RefreshAutoLoadoutProfile)) Options.P8S5D8:OnChanged(GuardRivalsCallback('AutoLoadout_PriorityChanged', function() RefreshAutoLoadoutProfile() RefreshLoadoutBanner() if not RivalsRuntimeBridge.IsConfigLoading() then RivalsRuntimeBridge.AutoWeaponMenu.OnLoadoutSwitched() end if LoadoutNoticePending then LoadoutNoticePending = false ShowLoadoutNotice() end end)) Options.P8S5N:OnChanged(GuardRivalsCallback('AutoLoadout_CountChanged', function(value) ApplyAutoLoadoutCount(value) RefreshAutoLoadoutProfile() RefreshLoadoutBanner() end)) ApplyAutoLoadoutCount(Options.P8S5N.Value) task.defer(RefreshLoadoutBanner) P3S5:AddToggle('P8S5T2', { Text = 'Auto Ban Weapons', Default = false, Tooltip = 'In ranked weapon-ban votes, submits the first available selected weapon for each ban round.', }) local DepAutoBan = P3S5:AddDependencyBox() DepAutoBan:AddDropdown('P8S5D5', { Values = RivalsRuntimeBridge.ResolveAutoBanWeaponValues(), Default = {}, Multi = true, Text = 'First Ban', Searchable = true, MaxVisibleDropdownItems = 12, }) DepAutoBan:AddDropdown('P8S5D6', { Values = RivalsRuntimeBridge.ResolveAutoBanWeaponValues(), Default = {}, Multi = true, Text = 'Second Ban', Searchable = true, MaxVisibleDropdownItems = 12, }) DepAutoBan:SetupDependencies({ { Toggles.P8S5T2, true } }) Toggles.P8S5T2:OnChanged(GuardRivalsCallback('AutoBan_ToggleChanged', function(value) RivalsRuntimeBridge.RegisterAutoBanSignals() if value then RivalsRuntimeBridge.TryAutoBanVote() end end)) for _, optionId in ipairs({ 'P8S5D5', 'P8S5D6' }) do Options[optionId]:OnChanged( GuardRivalsCallback( string.format('AutoBan_%s_Changed', optionId), function() RivalsRuntimeBridge.TryAutoBanVote() end ) ) end local P3S7 = automationTab:AddRightGroupbox('Auto Vote') P3S7:AddToggle('P8S5T5', { Text = 'Auto Vote Maps', Default = false, Tooltip = 'Votes for the first selected map offered by the live duel vote.', }) local DepAutoVote = P3S7:AddDependencyBox() DepAutoVote:AddDropdown('P8S5D9', { Values = RivalsRuntimeBridge.ResolveAutoVoteMapValues(), Default = {}, Multi = true, Text = 'Maps', Searchable = true, MaxVisibleDropdownItems = 12, }) DepAutoVote:SetupDependencies({ { Toggles.P8S5T5, true } }) Toggles.P8S5T5:OnChanged(GuardRivalsCallback('AutoVote_ToggleChanged', function() RivalsRuntimeBridge.RegisterAutoBanSignals() RivalsRuntimeBridge.TryAutoBanVote() end)) Options.P8S5D9:OnChanged( GuardRivalsCallback('AutoVote_MapsChanged', function() RivalsRuntimeBridge.TryAutoBanVote() end) ) local P3S6 = automationTab:AddRightGroupbox('Auto Queue') P3S6:AddDropdown('P8S6D1', { Values = RivalsRuntimeBridge.ResolveAutoQueueModeValues(), Default = '1v1', Multi = false, Text = 'Mode', Tooltip = 'Game mode to auto-queue into, including released matchmaking, rotating, and arcade modes.', }) P3S6:AddToggle('P8S6T1', { Text = 'Auto Queue', Default = false, Tooltip = 'Joins the selected mode and re-queues automatically after each match.', }) Toggles.P8S6T1:OnChanged( GuardRivalsCallback('AutoQueue_ToggleChanged', function(value) RivalsRuntimeBridge.OnAutoQueueToggled(value) end) ) Options.P8S6D1:OnChanged( GuardRivalsCallback('AutoQueue_ModeChanged', function() RivalsRuntimeBridge.OnAutoQueueModeChanged() end) ) return automationTab end -- ====================================================================== -- Battle pass claiming (used by Misc > Rewards > Claim All) -- ====================================================================== -- The game's own pass page fires ClaimBattlePassReward(tier, track) for a reward when tier <= PassLevel, track <= MaxPassTrackNum and -- RewardsClaimed[tostring(tier)][tostring(track)] is empty. FireServer never says whether the server took it, so every claim here is -- checked against the player data (the server answer lands there) and sent once more if it did not show up. do local Pass = {} RivalsRuntimeBridge.BattlePassClaim = Pass function Pass.IsClaimed(battlePass, tier, track) local claimed = type(battlePass) == 'table' and battlePass.RewardsClaimed or nil if type(claimed) ~= 'table' then return false end local row = claimed[tostring(tier)] or claimed[tier] if type(row) ~= 'table' then return false end return (row[tostring(track)] or row[track]) and true or false end -- nil + reason when there is nothing to read: 'season' (library not loaded), 'inactive' (no pass this season), 'progress' (no pass data yet) function Pass.GetState(pdc, seasonLibrary) if type(seasonLibrary) ~= 'table' or type(seasonLibrary.CurrentSeason) ~= 'table' then return nil, 'season' end local season = seasonLibrary.CurrentSeason if not season.BattlePassActive then return nil, 'inactive' end local ok, seasons = pcall(function() return pdc:Get('Seasons') end) local seasonData = ok and type(seasons) == 'table' and season.Name and seasons[season.Name] or nil local battlePass = type(seasonData) == 'table' and seasonData.BattlePass or nil if type(battlePass) ~= 'table' then return nil, 'progress' end return { Season = season, BattlePass = battlePass, PassLevel = tonumber(battlePass.PassLevel) or 0, MaxTrack = tonumber(battlePass.MaxPassTrackNum) or 0, } end -- rewards that can be claimed right now, and how many more are unlocked by level but sit on a track that is not owned function Pass.Pending(state) local season = state.Season local catalog = type(season.BattlePassRewards) == 'table' and season.BattlePassRewards or {} local tiers = math.min(state.PassLevel, tonumber(season.NumBattlePassTiers) or state.PassLevel) local tracks = tonumber(season.NumBattlePassTracks) or math.max(state.MaxTrack, 1) local pending, locked = {}, 0 for tier = 1, tiers do local row = catalog[tier] if type(row) == 'table' then for track = 1, tracks do if row[track] ~= nil and not Pass.IsClaimed(state.BattlePass, tier, track) then if track <= state.MaxTrack then pending[#pending + 1] = { Tier = tier, Track = track } else locked = locked + 1 end end end end end return pending, locked end -- options: Delay (s between rewards), Cancelled() -> bool, Progress(done, total, result) function Pass.ClaimAll(pdc, seasonLibrary, remote, options) options = options or {} local delay = tonumber(options.Delay) or 0.2 local result = { Claimed = 0, Failed = 0, Locked = 0, Total = 0 } local state = Pass.GetState(pdc, seasonLibrary) if not state or not remote then return result end local pending, locked = Pass.Pending(state) result.Locked, result.Total = locked, #pending for index, entry in ipairs(pending) do if options.Cancelled and options.Cancelled() then break end local accepted = false for attempt = 1, 2 do if not pcall(remote.FireServer, remote, entry.Tier, entry.Track) then break end local deadline = os.clock() + (attempt == 1 and 1.2 or 2) repeat task.wait(0.1) local fresh = Pass.GetState(pdc, seasonLibrary) if fresh and Pass.IsClaimed(fresh.BattlePass, entry.Tier, entry.Track) then accepted = true end until accepted or os.clock() >= deadline if accepted then break end end if accepted then result.Claimed = result.Claimed + 1 else result.Failed = result.Failed + 1 end if options.Progress then pcall(options.Progress, index, #pending, result) end task.wait(delay) end return result end end -- ====================================================================== -- Menu: Movement and Misc tabs -- ====================================================================== BuildRivalsMiscUi = function(Window) BuildRivalsEventUi = function(EventTab) RivalsRuntimeBridge.TowerKit.Ui.Build(EventTab, { State = EventZombieState, TestFire = EventZombieTestDamage, DumpStructure = EventZombieDumpStructure, DumpEnum = EventZombieDumpEnum, }) end local MovementTab = Window:AddTab('Movement', 'move') local EventTabIcon = (Library.GetCustomIcon and Library:GetCustomIcon('ghost')) and 'ghost' or 'bug' local EventTab = Window:AddTab('Event', EventTabIcon) BuildRivalsEventUi(EventTab) local miscTab = Window:AddTab('Misc', 'package') local RIVALS_REWARD_CLAIM_DELAY = 0.2 local RIVALS_REWARD_SETTLE_DELAY = 0.6 local RIVALS_REWARD_EMPTY_CONFIRMATIONS = 3 local function ClaimAllRivalsBundle() local pdc = ResolvePlayerDataController() if not pdc or not IsPlayerDataControllerReady() then Library:Notify({ Title = 'RIVALS', Description = 'Player data not ready yet, try again in a moment.', Time = 3 }) return end local DAILY_REWARDS = { { remote = 'ClaimLikeReward', flag = 'ClaimedLikeReward' }, { remote = 'ClaimFavoriteReward', flag = 'ClaimedFavoriteReward' }, { remote = 'ClaimNotificationsReward', flag = 'ClaimedNotificationsReward' }, } local dailyClaimed, dailySkippedAlready = 0, 0 for _, info in ipairs(DAILY_REWARDS) do if pdc:Get(info.flag) then dailySkippedAlready = dailySkippedAlready + 1 else local r = ResolveRivalsDataRemote(info.remote, 'RemoteEvent') if r then local ok = pcall(r.FireServer, r) if ok then dailyClaimed = dailyClaimed + 1 task.wait(RIVALS_REWARD_CLAIM_DELAY) end end end end local passClaimed, passFailed, passLocked = 0, 0, 0 local passNotActive, passNoProgress = false, false do local passRemote = ResolveRivalsDataRemote('ClaimBattlePassReward', 'RemoteEvent') local modulesFolder = ReplicatedStorage:FindFirstChild('Modules') local seasonLibraryModule = modulesFolder and modulesFolder:FindFirstChild('SeasonLibrary') local seasonLibrary = seasonLibraryModule and RivalsRuntimeBridge.RequireGameModule(seasonLibraryModule, 'SeasonLibrary') or nil if passRemote and seasonLibrary then local pass = RivalsRuntimeBridge.BattlePassClaim local state, why = pass.GetState(pdc, seasonLibrary) if not state then passNotActive = why == 'inactive' passNoProgress = why == 'progress' else local waiting = #pass.Pending(state) if waiting > 0 then Library:Notify({ Title = 'RIVALS', Description = string.format('Claiming %d battle pass reward%s...', waiting, waiting == 1 and '' or 's'), Time = 3, }) end local result = pass.ClaimAll(pdc, seasonLibrary, passRemote, { Delay = RIVALS_REWARD_CLAIM_DELAY, Cancelled = function() return Library.Unloaded end, }) passClaimed, passFailed, passLocked = result.Claimed, result.Failed, result.Locked end end end local capsuleClaimed = 0 local capsuleSkippedDuel = false local duelState = FighterDataCache.LocalDuel if duelState and (duelState.IsInDuel or duelState.IsInShootingRange) then capsuleSkippedDuel = true else local capsuleRemote = ResolveRivalsDataRemote('UseUnclaimedReward', 'RemoteFunction') if capsuleRemote then local maxQty = 99 local modulesFolder = ReplicatedStorage:FindFirstChild('Modules') local constantsModule = modulesFolder and modulesFolder:FindFirstChild('CONSTANTS') if constantsModule then local okC, constants = pcall(require, constantsModule) if okC and type(constants) == 'table' and type(constants.MAX_BACKPACK_USE_QUANTITY) == 'number' then maxQty = constants.MAX_BACKPACK_USE_QUANTITY end end local emptyStreak = 0 local failureStreak = 0 for _ = 1, 400 do local rewards = pdc:Get('UnclaimedRewards') if type(rewards) ~= 'table' or rewards[1] == nil then emptyStreak = emptyStreak + 1 if emptyStreak >= RIVALS_REWARD_EMPTY_CONFIRMATIONS then break end task.wait(RIVALS_REWARD_SETTLE_DELAY) else emptyStreak = 0 local entry = rewards[1] local startQty = type(entry) == 'table' and tonumber(entry.Quantity) or 1 if not startQty or startQty < 1 then startQty = 1 end local useQty = math.min(startQty, maxQty) local ok, result = pcall(capsuleRemote.InvokeServer, capsuleRemote, 1, useQty) if ok and result == 'Success' then capsuleClaimed = capsuleClaimed + 1 failureStreak = 0 else failureStreak = failureStreak + 1 if failureStreak >= 5 then break end end task.wait(RIVALS_REWARD_CLAIM_DELAY) end end end end local parts = {} if dailyClaimed > 0 then table.insert(parts, string.format('%d daily', dailyClaimed)) end if capsuleClaimed > 0 then table.insert(parts, string.format('%d capsule%s', capsuleClaimed, capsuleClaimed == 1 and '' or 's')) end if passClaimed > 0 then table.insert(parts, string.format('%d pass tier%s', passClaimed, passClaimed == 1 and '' or 's')) end local desc if #parts > 0 then desc = 'Claimed ' .. table.concat(parts, ', ') .. '.' if capsuleSkippedDuel then desc = desc .. ' Capsules skipped (in duel/range).' end elseif capsuleSkippedDuel then desc = 'Capsules skipped (in duel/range). Nothing else to claim.' elseif dailySkippedAlready == 3 and not passNotActive and not passNoProgress then desc = 'Already up to date - nothing to claim.' elseif dailySkippedAlready == 3 then desc = 'Daily rewards already claimed - nothing else to claim right now.' else desc = 'Nothing to claim.' end if passFailed > 0 then desc = desc .. string.format(' %d pass reward%s not accepted by the server, press Claim All again.', passFailed, passFailed == 1 and ' was' or 's were') end if passLocked > 0 then desc = desc .. string.format(' %d premium pass reward%s need the paid track.', passLocked, passLocked == 1 and '' or 's') end Library:Notify({ Title = 'RIVALS', Description = desc, Time = 4 }) end local playerSpooferLabels = { Winstreak = 'Win Streak', Level = 'Level', RankedElo = 'Ranked ELO', NametagStatus = 'Nametag Status', Influencer = 'Influencer', RobloxEmployee = 'Roblox Employee', NosniyTeam = "Nosniy's Team", } local playerSpooferScopes = { { suffix = 'You', title = 'Player Spoofer (You)', side = 'Left' }, { suffix = 'Oth', title = 'Player Spoofer (Others)', side = 'Right' }, } for _, scope in ipairs(playerSpooferScopes) do local spooferGroup = scope.side == 'Left' and miscTab:AddLeftGroupbox(scope.title) or miscTab:AddRightGroupbox(scope.title) for _, field in ipairs(RivalsRuntimeBridge.PlayerSpoofer.AttributeFields) do local toggleId = 'PSpoof' .. scope.suffix .. field.key .. 'T' local toggle = spooferGroup:AddToggle(toggleId, { Text = playerSpooferLabels[field.key], Default = false, }) toggle:OnChanged(GuardRivalsCallback('PlayerSpoofer_Toggle', RivalsRuntimeBridge.PlayerSpoofer.RefreshAll)) if field.control ~= 'const' then local valueId = 'PSpoof' .. scope.suffix .. field.key .. 'V' local default = scope.suffix == 'You' and field.youDefault or field.othDefault local valueBox = spooferGroup:AddDependencyBox() valueBox:AddInput(valueId, { Text = 'Value', Default = default, AllowEmpty = true, EmptyReset = '', }) Options[valueId]:OnChanged( GuardRivalsCallback('PlayerSpoofer_Value', RivalsRuntimeBridge.PlayerSpoofer.RefreshAll) ) valueBox:SetupDependencies({ { Toggles[toggleId], true } }) end end end local P10S1 = miscTab:AddLeftGroupbox('Rewards') P10S1:AddButton({ Text = 'Claim All', Func = GuardRivalsCallback('Rewards_ClaimAll', ClaimAllRivalsBundle), Tooltip = 'Opens all capsules (charms etc) and claims daily + pass rewards.', }) local P10S3 = MovementTab:AddLeftGroupbox('Movement') local function BindMovementControl(control) if control and type(control.OnChanged) == 'function' then control:OnChanged(GuardRivalsCallback('Movement_OptionChanged', RivalsRuntimeBridge.Movement.RefreshAll)) end return control end local WalkSpeedToggle = BindMovementControl(P10S3:AddToggle('P10S3T1', { Text = 'WalkSpeed', Default = false, })) WalkSpeedToggle:AddKeyPicker('P10S3T1K', { Default = 'Unknown', Mode = 'Always', Text = 'WalkSpeed', NoUI = false, }) local WalkSpeedSettings = P10S3:AddDependencyBox() BindMovementControl(WalkSpeedSettings:AddSlider('P10S3S1', { Text = 'WalkSpeed Multiplier', Default = 2, Min = 1, Max = 10, Rounding = 2, Compact = true, })) WalkSpeedSettings:SetupDependencies({ { Toggles.P10S3T1, true } }) local SlidingToggle = BindMovementControl(P10S3:AddToggle('P10S3T2', { Text = 'Sliding Speed', Default = false, })) SlidingToggle:AddKeyPicker('P10S3T2K', { Default = 'Unknown', Mode = 'Always', Text = 'Sliding Speed', NoUI = false, }) local SlidingSettings = P10S3:AddDependencyBox() BindMovementControl(SlidingSettings:AddSlider('P10S3S2', { Text = 'Sliding Multiplier', Default = 10, Min = 1, Max = 10, Rounding = 2, Compact = true, })) SlidingSettings:SetupDependencies({ { Toggles.P10S3T2, true } }) BindMovementControl(P10S3:AddToggle('P10S3T3', { Text = 'Jump Power', Default = false, })) local JumpPowerSettings = P10S3:AddDependencyBox() BindMovementControl(JumpPowerSettings:AddSlider('P10S3S3', { Text = 'Jump Power Multiplier', Default = 2, Min = 0.1, Max = 10, Rounding = 2, Compact = true, })) JumpPowerSettings:SetupDependencies({ { Toggles.P10S3T3, true } }) local FlightToggle = BindMovementControl(P10S3:AddToggle('P10S3T4', { Text = 'Flight', Default = false, Tooltip = 'Camera-relative WASD flight; Space rises and Left Control / Shift descends.', })) FlightToggle:AddKeyPicker('P10S3T4K', { Default = 'Unknown', Mode = 'Always', Text = 'Flight', NoUI = false, }) local FlightSettings = P10S3:AddDependencyBox() BindMovementControl(FlightSettings:AddSlider('P10S3S4', { Text = 'Flight Speed', Default = 100, Min = 25, Max = 500, Rounding = 0, Compact = true, })) FlightSettings:SetupDependencies({ { Toggles.P10S3T4, true } }) local NoclipToggle = BindMovementControl(P10S3:AddToggle('P10S3T5', { Text = 'Noclip', Default = false, })) NoclipToggle:AddKeyPicker('P10S3T5K', { Default = 'Unknown', Mode = 'Always', Text = 'Noclip', NoUI = false, }) local AutoStrafeToggle = BindMovementControl(P10S3:AddToggle('P10S3T6', { Text = 'Auto Strafe', Default = false, Tooltip = 'Applies camera-relative movement while Space is held.', })) AutoStrafeToggle:AddKeyPicker('P10S3T6K', { Default = 'Unknown', Mode = 'Always', Text = 'Auto Strafe', NoUI = false, }) local AutoStrafeSettings = P10S3:AddDependencyBox() BindMovementControl(AutoStrafeSettings:AddSlider('P10S3S5', { Text = 'Auto Strafe Speed', Default = 50, Min = 1, Max = 200, Rounding = 0, Compact = true, })) AutoStrafeSettings:SetupDependencies({ { Toggles.P10S3T6, true } }) P10S3:AddToggle('P4S2T5', { Text = 'Infinite Double Jumps', Default = false, Tooltip = 'Gives you unlimited double jumps.', Callback = RivalsModsState.EnsureHooks, }) local LongJumpToggle = BindMovementControl(P10S3:AddToggle('P10S3T7', { Text = 'Long Jump', Default = false, Tooltip = 'Launches a long jump when you press the keybind.', })) LongJumpToggle:AddKeyPicker('P10S3T7K', { Default = 'Unknown', Mode = 'Hold', Text = 'Long Jump', NoUI = false, }) local LongJumpSettings = P10S3:AddDependencyBox() BindMovementControl(LongJumpSettings:AddDropdown('P10S3D1', { Values = { 'Under Feet', 'Look Direction' }, Default = 'Under Feet', Multi = false, Text = 'Mode', })) BindMovementControl(LongJumpSettings:AddSlider('P10S3S6', { Text = 'Force', Default = 60, Min = 0, Max = 300, Rounding = 0, Compact = true, })) BindMovementControl(LongJumpSettings:AddSlider('P10S3S7', { Text = 'Upward Velocity', Default = 20, Min = 0, Max = 200, Rounding = 0, Compact = true, })) BindMovementControl(LongJumpSettings:AddSlider('P10S3S8', { Text = 'Behind Offset', Default = 8, Min = 0, Max = 8, Rounding = 1, Compact = true, })) LongJumpSettings:SetupDependencies({ { Toggles.P10S3T7, true } }) local P10S6 = MovementTab:AddLeftGroupbox('Animation Player') P10S6:AddToggle('P10S6T1', { Text = 'Animation Player', Default = false, Tooltip = 'Loops a custom Roblox animation on your character.', }) P10S6:AddInput('P10S6I1', { Text = 'Custom Animation ID', Default = '', Placeholder = 'rbxassetid://123456789', AllowEmpty = true, EmptyReset = '', Finished = true, Tooltip = 'Accepts an asset ID or any string containing one.', }) P10S6:AddSlider('P10S6S1', { Text = 'Speed', Default = 1, Min = 0, Max = 5, Rounding = 2, Compact = true, }) P10S6:AddSlider('P10S6S2', { Text = 'Start', Default = 0, Min = 0, Max = 100, Rounding = 0, Compact = true, Suffix = '%', }) P10S6:AddSlider('P10S6S3', { Text = 'End', Default = 100, Min = 0, Max = 100, Rounding = 0, Compact = true, Suffix = '%', }) Toggles.P10S6T1:OnChanged( GuardRivalsCallback('AnimationPlayer_Enabled', RivalsRuntimeBridge.AnimationPlayer.RefreshAll) ) Options.P10S6I1:OnChanged( GuardRivalsCallback('AnimationPlayer_CustomId', RivalsRuntimeBridge.AnimationPlayer.RefreshAll) ) Options.P10S6S1:OnChanged(GuardRivalsCallback('AnimationPlayer_Speed', RivalsRuntimeBridge.AnimationPlayer.AdjustSpeed)) local P10S4 = MovementTab:AddRightGroupbox('Movement Recorder') P10S4:AddToggle('P10S4T1', { Text = 'Movement Recorder', Default = false, Tooltip = 'Records and replays movement, camera, equipment, item input, quick attacks, jumps, crouches, and slides per map.', }) P10S4:AddToggle('P10S4T2', { Text = 'Hide UI', Default = false }) P10S4:AddToggle('P10S4T3', { Text = 'Hide Notifications', Default = false }) P10S4:AddDivider() P10S4:AddSlider('P10S4S1', { Text = 'Open Distance', Default = 3, Min = 1, Max = 25, Rounding = 1, Compact = true, }) P10S4:AddSlider('P10S4S2', { Text = 'Render Distance', Default = 50, Min = 10, Max = 250, Rounding = 0, Compact = true, }) P10S4:AddDivider() P10S4:AddSlider('P10S4S3', { Text = 'Look Align Speed', Default = 180, Min = 45, Max = 720, Rounding = 0, Compact = true, Tooltip = 'How fast the camera turns to the recording starting look angle.', }) P10S4:AddSlider('P10S4S4', { Text = 'Look Smoothness', Default = 35, Min = 0, Max = 100, Rounding = 0, Compact = true, }) P10S4:AddSlider('P10S4S5', { Text = 'Align Snap Distance', Default = 0.2, Min = 0.1, Max = 5, Rounding = 1, Compact = true, Tooltip = 'Once this close to the start, position is considered aligned.', }) P10S4:AddDivider() P10S4:AddToggle('P10S4T4', { Text = 'Record', Default = false }) Toggles.P10S4T4:AddKeyPicker('P10S4T4K', { Default = 'Unknown', Mode = 'Toggle', Text = 'Record', NoUI = false, SyncToggleState = true, }) P10S4:AddToggle('P10S4T5', { Text = 'Replay', Default = false }) Toggles.P10S4T5:AddKeyPicker('P10S4T5K', { Default = 'Unknown', Mode = 'Toggle', Text = 'Replay', NoUI = false, SyncToggleState = true, }) P10S4:AddDivider() P10S4:AddInput('P10S4I1', { Text = 'Recording Name', Default = '', Placeholder = 'my recording', AllowEmpty = true, EmptyReset = '', Finished = true, }) P10S4:AddButton({ Text = 'Save Recording', Func = GuardRivalsCallback( 'MovementRecorder_Save', function() RivalsRuntimeBridge.MovementRecorder.SaveLastRecording(Options.P10S4I1 and Options.P10S4I1.Value) end ), }) local P10S5 = MovementTab:AddRightGroupbox('Recordings') RivalsRuntimeBridge.MovementRecorder.LoadPersistence() P10S5:AddDropdown('P10S4D1', { Values = RivalsRuntimeBridge.MovementRecorder.GetMapNames(), Default = nil, Multi = false, Text = 'Map', }) P10S5:AddDropdown('P10S4D2', { Values = {}, Default = nil, Multi = false, Text = 'Config', }) P10S5:AddButton({ Text = 'Delete Recording', DoubleClick = true, Func = GuardRivalsCallback( 'MovementRecorder_Delete', function() RivalsRuntimeBridge.MovementRecorder.DeleteRecording(Options.P10S4D2 and Options.P10S4D2.Value) end ), }) P10S5:AddButton({ Text = 'Refresh Recordings', Func = GuardRivalsCallback('MovementRecorder_Refresh', function() RivalsRuntimeBridge.MovementRecorder.PersistenceLoaded = false RivalsRuntimeBridge.MovementRecorder.LoadPersistence() RivalsRuntimeBridge.MovementRecorder.RefreshRecordingOptions() end), }) Toggles.P10S4T1:OnChanged( GuardRivalsCallback('MovementRecorder_Enabled', RivalsRuntimeBridge.MovementRecorder.OnEnabledToggled) ) Toggles.P10S4T4:OnChanged( GuardRivalsCallback('MovementRecorder_Record', RivalsRuntimeBridge.MovementRecorder.OnRecordToggled) ) Toggles.P10S4T5:OnChanged( GuardRivalsCallback('MovementRecorder_Replay', RivalsRuntimeBridge.MovementRecorder.OnReplayToggled) ) Options.P10S4D1:OnChanged( GuardRivalsCallback('MovementRecorder_Map', RivalsRuntimeBridge.MovementRecorder.RefreshRecordingOptions) ) RivalsRuntimeBridge.MovementRecorder.RefreshRecordingOptions() local PX10 = miscTab:AddRightGroupbox('Cheater Detector') PX10:AddToggle('PX10T1', { Text = 'Cheater Detector', Default = false, Tooltip = 'Flags players who keep moving faster than any legitimate movement allows.', }) local DepCheaterDetector = PX10:AddDependencyBox() DepCheaterDetector:AddToggle('PX10T2', { Text = 'Notify', Default = true, Tooltip = 'Shows a notification the first time a player gets flagged.', }) DepCheaterDetector:AddToggle('PX10T3', { Text = 'Mark in ESP', Default = true, Tooltip = 'Adds [CHEATER?] after the name of flagged players in the ESP.', }) DepCheaterDetector:AddSlider('PX10S1', { Text = 'Speed Threshold', Default = 120, Min = 60, Max = 300, Rounding = 0, Suffix = ' studs/s', Compact = true, Tooltip = 'Sustained horizontal speed above this flags a player. Lower catches more but risks false flags.', }) DepCheaterDetector:SetupDependencies({ { Toggles.PX10T1, true } }) local P10S7 = miscTab:AddRightGroupbox('Device Spoof') P10S7:AddToggle('P10S7T1', { Text = 'Device Spoof', Default = false, Tooltip = 'Reports your input device to the server as the chosen platform.', }) P10S7:AddDropdown('P10S7D1', { Values = { 'Desktop', 'Mobile', 'Console', 'VR' }, Default = 'VR', Text = 'Spoof Type', Tooltip = 'Which device type you appear to be playing on.', }) Toggles.P10S7T1:OnChanged(GuardRivalsCallback('DeviceSpoof_Toggle', RivalsRuntimeBridge.DeviceSpoof.RefreshAll)) Options.P10S7D1:OnChanged(GuardRivalsCallback('DeviceSpoof_SpoofType', RivalsRuntimeBridge.DeviceSpoof.RefreshAll)) local P10S2 = miscTab:AddRightGroupbox('Staff Detector') P10S2:AddToggle('P10S2T1', { Text = 'Staff Detector', Default = false, Tooltip = 'Detects RIVALS moderators and players who are friends with a moderator.', }) P10S2:AddDropdown('P10S2D1', { Values = { 'Notify', 'Kick', 'To Lobby' }, Default = {}, Multi = true, Text = 'Moderator Actions', Tooltip = 'Actions for direct RIVALS staff. Kick takes precedence over To Lobby when both are selected.', }) P10S2:AddDropdown('P10S2D2', { Values = { 'Notify', 'Kick', 'To Lobby' }, Default = {}, Multi = true, Text = 'Mod Friend Actions', Tooltip = 'Actions for players whose friends list contains RIVALS staff. Kick takes precedence over To Lobby.', }) P10S2:AddLabel('Text Color'):AddColorPicker('P10S2C1', { Default = Color3.fromRGB(250, 170, 70), Title = 'Staff Detector Text Color', }) -- ====================================================================== -- Staff detector -- ====================================================================== local StaffDetector = { Notifications = {}, Connections = {}, RoleCache = {}, FriendCache = {}, Detections = {}, ActionCache = {}, ClassificationInFlight = {}, RetryGeneration = {}, StaffUserIds = {}, GroupId = 3461453, MinimumRank = 100, LobbyPlaceId = 17625359962, RoleCacheLifetime = 300, FriendCacheLifetime = 300, StaffSetLifetime = 900, FailureRetryDelay = 15, MaxStaffRoles = 32, MaxGroupPagesPerRole = 10, MaxFriendPages = 5, StaffSetExpiresAt = 0, StaffSetReady = false, StaffSetComplete = false, StaffSetLoading = false, EscapeCooldownUntil = 0, Enabled = false, Unloaded = false, Generation = 0, HttpService = game:GetService('HttpService'), TeleportService = game:GetService('TeleportService'), } function StaffDetector.IsStaff(player) if not player or player == LP then return false end local cached = StaffDetector.RoleCache[player.UserId] if cached and cached.ExpiresAt > os.clock() then return cached.Value end local ok, rank = pcall(function() return player:GetRankInGroup(StaffDetector.GroupId) end) if not ok or type(rank) ~= 'number' then return nil end local isStaff = rank >= StaffDetector.MinimumRank StaffDetector.RoleCache[player.UserId] = { Value = isStaff, ExpiresAt = os.clock() + StaffDetector.RoleCacheLifetime, } if isStaff then StaffDetector.StaffUserIds[player.UserId] = true end return isStaff end function StaffDetector.FetchJson(url) local requestOk, response = pcall(game.HttpGet, game, url) if not requestOk or type(response) ~= 'string' then return nil end local decodeOk, decoded = pcall(StaffDetector.HttpService.JSONDecode, StaffDetector.HttpService, response) return decodeOk and type(decoded) == 'table' and decoded or nil end function StaffDetector.EnsureStaffUserIds() local now = os.clock() if StaffDetector.StaffSetReady and StaffDetector.StaffSetExpiresAt > now then return true end if StaffDetector.StaffSetLoading then local waitUntil = now + 5 repeat task.wait(0.05) until not StaffDetector.StaffSetLoading or os.clock() >= waitUntil or StaffDetector.Unloaded return StaffDetector.StaffSetReady end StaffDetector.StaffSetLoading = true local roles = StaffDetector.FetchJson(string.format('https://groups.roblox.com/v1/groups/%d/roles', StaffDetector.GroupId)) local nextStaffUserIds = {} local complete = roles ~= nil and type(roles.roles) == 'table' local staffRoleCount = 0 if complete then for _, role in ipairs(roles.roles) do if type(role) == 'table' and type(role.rank) == 'number' and role.rank >= StaffDetector.MinimumRank and tonumber(role.id) then staffRoleCount = staffRoleCount + 1 if staffRoleCount > StaffDetector.MaxStaffRoles then complete = false break end local cursor = nil local pageCount = 0 repeat pageCount = pageCount + 1 local url = string.format( 'https://groups.roblox.com/v1/groups/%d/roles/%d/users?limit=100&sortOrder=Asc', StaffDetector.GroupId, tonumber(role.id) ) if cursor and cursor ~= '' then url = url .. '&cursor=' .. StaffDetector.HttpService:UrlEncode(cursor) end local page = StaffDetector.FetchJson(url) if not page or type(page.data) ~= 'table' then complete = false break end for _, member in ipairs(page.data) do local userId = type(member) == 'table' and tonumber(member.userId or member.id) or nil if userId then nextStaffUserIds[userId] = true end end cursor = page.nextPageCursor until not cursor or cursor == '' or pageCount >= StaffDetector.MaxGroupPagesPerRole if cursor and cursor ~= '' then complete = false end end end end if complete then StaffDetector.StaffUserIds = nextStaffUserIds StaffDetector.StaffSetReady = true StaffDetector.StaffSetComplete = true else for userId in pairs(nextStaffUserIds) do StaffDetector.StaffUserIds[userId] = true end StaffDetector.StaffSetReady = StaffDetector.StaffSetReady or next(StaffDetector.StaffUserIds) ~= nil StaffDetector.StaffSetComplete = false end StaffDetector.StaffSetExpiresAt = os.clock() + (complete and StaffDetector.StaffSetLifetime or StaffDetector.FailureRetryDelay) StaffDetector.StaffSetLoading = false return StaffDetector.StaffSetReady end function StaffDetector.IsModFriend(player) if not player or player == LP then return false end local cached = StaffDetector.FriendCache[player.UserId] if cached and cached.ExpiresAt > os.clock() then return cached.Value, cached.StaffFriendName end if not StaffDetector.EnsureStaffUserIds() then return nil end local friendsOk, friendPages = pcall(Players.GetFriendsAsync, Players, player.UserId) if not friendsOk or not friendPages then return nil end local matchedName = nil local pageCount = 0 local complete = true repeat pageCount = pageCount + 1 local pageOk, friends = pcall(friendPages.GetCurrentPage, friendPages) if not pageOk or type(friends) ~= 'table' then complete = false break end for _, friend in ipairs(friends) do local friendUserId = type(friend) == 'table' and tonumber(friend.Id or friend.UserId or friend.id) or nil if friendUserId and StaffDetector.StaffUserIds[friendUserId] then matchedName = friend.Username or friend.DisplayName or tostring(friendUserId) break end end if matchedName or friendPages.IsFinished then break end if pageCount >= StaffDetector.MaxFriendPages then complete = false break end local advanceOk = pcall(friendPages.AdvanceToNextPageAsync, friendPages) if not advanceOk then complete = false break end until false if not matchedName and (not complete or not StaffDetector.StaffSetComplete) then return nil end local isModFriend = matchedName ~= nil StaffDetector.FriendCache[player.UserId] = { Value = isModFriend, StaffFriendName = matchedName, ExpiresAt = os.clock() + StaffDetector.FriendCacheLifetime, } return isModFriend, matchedName end function StaffDetector.IsActionSelected(kind, action) local option = Options[kind == 'Moderator' and 'P10S2D1' or 'P10S2D2'] return option and type(option.Value) == 'table' and option.Value[action] == true end function StaffDetector.DestroyNotification(kind) local notification = StaffDetector.Notifications[kind] if not notification then return end pcall(function() if type(notification.Destroy) == 'function' then notification:Destroy() elseif type(notification.Remove) == 'function' then notification:Remove() end end) StaffDetector.Notifications[kind] = nil end function StaffDetector.UpdateNotifications() for _, kind in ipairs({ 'Moderator', 'ModFriend' }) do local names = {} if StaffDetector.Enabled and StaffDetector.IsActionSelected(kind, 'Notify') then for _, detection in pairs(StaffDetector.Detections) do if detection.Kind == kind and detection.Player and detection.Player.Parent == Players then local name = detection.Player.DisplayName or detection.Player.Name if kind == 'ModFriend' and detection.StaffFriendName then name = string.format('%s (friend of %s)', name, detection.StaffFriendName) end table.insert(names, name) end end end table.sort(names) if #names == 0 then StaffDetector.DestroyNotification(kind) else local description = (kind == 'Moderator' and 'In server: ' or 'In server: ') .. table.concat(names, ', ') local notification = StaffDetector.Notifications[kind] if notification then local changed = pcall(function() notification:ChangeDescription(description) end) if not changed then StaffDetector.DestroyNotification(kind) end end if not StaffDetector.Notifications[kind] then local ok, created = pcall( function() return Library:Notify({ Title = kind == 'Moderator' and 'Moderator Detected' or 'Mod Friend Detected', Description = description, Persist = true, TitleColor = Options.P10S2C1.Value, DescriptionColor = Options.P10S2C1.Value, }) end ) if ok and created then StaffDetector.Notifications[kind] = created end end end end end function StaffDetector.RunActions(detection) if not StaffDetector.Enabled or not detection or not detection.Player then return end local kind = detection.Kind local actionPrefix = string.format('%s:%d:', kind, detection.Player.UserId) if StaffDetector.IsActionSelected(kind, 'Kick') then local actionKey = actionPrefix .. 'Kick' if not StaffDetector.ActionCache[actionKey] then StaffDetector.ActionCache[actionKey] = true pcall( LP.Kick, LP, string.format( 'Staff Detector: %s detected (%s).', detection.Player.Name, kind == 'Moderator' and 'moderator' or 'moderator friend' ) ) end return end if StaffDetector.IsActionSelected(kind, 'To Lobby') and game.PlaceId ~= StaffDetector.LobbyPlaceId then local actionKey = actionPrefix .. 'To Lobby' if not StaffDetector.ActionCache[actionKey] and os.clock() >= StaffDetector.EscapeCooldownUntil then StaffDetector.EscapeCooldownUntil = os.clock() + 5 local ok = pcall( StaffDetector.TeleportService.Teleport, StaffDetector.TeleportService, StaffDetector.LobbyPlaceId, LP ) if ok then StaffDetector.ActionCache[actionKey] = true end end end end function StaffDetector.QueueClassification(player, delaySeconds) if not StaffDetector.Enabled or not player or player == LP then return end local userId = player.UserId local retryGeneration = (StaffDetector.RetryGeneration[userId] or 0) + 1 StaffDetector.RetryGeneration[userId] = retryGeneration local detectorGeneration = StaffDetector.Generation task.delay(delaySeconds or 0, function() if not StaffDetector.Enabled or StaffDetector.Unloaded or detectorGeneration ~= StaffDetector.Generation or retryGeneration ~= StaffDetector.RetryGeneration[userId] or player.Parent ~= Players or StaffDetector.ClassificationInFlight[userId] then return end StaffDetector.ClassificationInFlight[userId] = true local isStaff = StaffDetector.IsStaff(player) local detection = nil local unavailable = isStaff == nil if isStaff == true then detection = { Player = player, Kind = 'Moderator' } elseif isStaff == false then local isModFriend, staffFriendName = StaffDetector.IsModFriend(player) unavailable = isModFriend == nil if isModFriend == true then detection = { Player = player, Kind = 'ModFriend', StaffFriendName = staffFriendName, } end end StaffDetector.ClassificationInFlight[userId] = nil if not StaffDetector.Enabled or StaffDetector.Unloaded or detectorGeneration ~= StaffDetector.Generation or retryGeneration ~= StaffDetector.RetryGeneration[userId] or player.Parent ~= Players then return end StaffDetector.Detections[userId] = detection StaffDetector.UpdateNotifications() if detection then StaffDetector.RunActions(detection) elseif unavailable then StaffDetector.QueueClassification(player, StaffDetector.FailureRetryDelay) end end) end function StaffDetector.Refresh() if not StaffDetector.Enabled then StaffDetector.UpdateNotifications() return end local activeUserIds = {} for _, player in ipairs(Players:GetPlayers()) do if player ~= LP then activeUserIds[player.UserId] = true StaffDetector.QueueClassification(player, 0) end end for userId in pairs(StaffDetector.Detections) do if not activeUserIds[userId] then StaffDetector.Detections[userId] = nil end end StaffDetector.UpdateNotifications() end function StaffDetector.DisconnectPlayers() for key, connection in pairs(StaffDetector.Connections) do pcall(function() connection:Disconnect() end) StaffDetector.Connections[key] = nil end end function StaffDetector.Start() if StaffDetector.Enabled or StaffDetector.Unloaded then return end StaffDetector.Enabled = true StaffDetector.Generation = StaffDetector.Generation + 1 StaffDetector.Connections.PlayerAdded = Players.PlayerAdded:Connect( GuardRivalsCallback( 'StaffDetector_PlayerAdded', function(player) StaffDetector.QueueClassification(player, 0) end ) ) StaffDetector.Connections.PlayerRemoving = Players.PlayerRemoving:Connect(GuardRivalsCallback('StaffDetector_PlayerRemoving', function(player) local userId = player.UserId StaffDetector.RoleCache[userId] = nil StaffDetector.FriendCache[userId] = nil StaffDetector.Detections[userId] = nil StaffDetector.ClassificationInFlight[userId] = nil StaffDetector.RetryGeneration[userId] = nil for actionKey in pairs(StaffDetector.ActionCache) do if actionKey:find(':' .. tostring(userId) .. ':', 1, true) then StaffDetector.ActionCache[actionKey] = nil end end StaffDetector.UpdateNotifications() end)) StaffDetector.Refresh() end function StaffDetector.Destroy(unloading) StaffDetector.Enabled = false StaffDetector.Generation = StaffDetector.Generation + 1 StaffDetector.DisconnectPlayers() for _, kind in ipairs({ 'Moderator', 'ModFriend' }) do StaffDetector.DestroyNotification(kind) end table.clear(StaffDetector.Detections) table.clear(StaffDetector.ActionCache) table.clear(StaffDetector.ClassificationInFlight) table.clear(StaffDetector.RetryGeneration) if unloading then StaffDetector.Unloaded = true table.clear(StaffDetector.RoleCache) table.clear(StaffDetector.FriendCache) table.clear(StaffDetector.StaffUserIds) end end function StaffDetector.ActionsChanged() StaffDetector.UpdateNotifications() if StaffDetector.Enabled then for _, detection in pairs(StaffDetector.Detections) do StaffDetector.RunActions(detection) end end end function StaffDetector.ColorChanged() for _, kind in ipairs({ 'Moderator', 'ModFriend' }) do StaffDetector.DestroyNotification(kind) end StaffDetector.UpdateNotifications() end Toggles.P10S2T1:OnChanged(GuardRivalsCallback('StaffDetector_ToggleChanged', function(value) if value then StaffDetector.Start() else StaffDetector.Destroy() end end)) Options.P10S2D1:OnChanged(GuardRivalsCallback('StaffDetector_ModeratorActionsChanged', StaffDetector.ActionsChanged)) Options.P10S2D2:OnChanged(GuardRivalsCallback('StaffDetector_ModFriendActionsChanged', StaffDetector.ActionsChanged)) Options.P10S2C1:OnChanged(GuardRivalsCallback('StaffDetector_ColorChanged', StaffDetector.ColorChanged)) Library:OnUnload(GuardRivalsCallback('StaffDetector_Unload', function() StaffDetector.Destroy(true) end)) RivalsRuntimeBridge.Extras.BuildMiscUi(miscTab) return miscTab end -- ====================================================================== -- Serverhop tab (Trello #25) -- ====================================================================== -- Lists the public servers of the place you are in (Roblox's own server list: players, fps, ping), filtered by player count, -- and joins one: the one you picked, the lowest ping or the emptiest. A join that fails goes on to the next server; when Roblox -- limits the list what was loaded stays on screen instead of an empty list. -- -- Roblox hands the list out sorted by player count (Asc = emptiest first, Desc = fullest first), 100 servers a page, and answers -- 429 after only a few requests a minute. So the list is read from BOTH ends and each end remembers how far it got (its -- frontier). A player count that lies between the two frontiers is searched for from the nearer end, one page at a time, and the -- search stops the moment a server with that count shows up. A refresh that Roblox refuses keeps the servers already loaded. do local HttpService = game:GetService('HttpService') local TeleportService = game:GetService('TeleportService') local function freshFrontier() return { Asc = { Count = nil, Cursor = nil, Started = false, Done = false }, Desc = { Count = nil, Cursor = nil, Started = false, Done = false }, } end local Serverhop = { Pool = {}, -- id -> server, everything loaded so far Servers = {}, -- the pool as a list Frontier = freshFrontier(), LoadedAt = 0, LimitedUntil = 0, Labels = {}, -- list text -> server FetchedAt = 0, Busy = false, Joining = false, LastMore = 'Desc', } RivalsRuntimeBridge.Serverhop = Serverhop local MIN_FETCH_GAP = 3 local PAGE_GAP = 2 -- between two pages of one search local SEARCH_STEPS = 8 local LIMIT_PAUSE = 25 local MAX_PLAYERS = 40 local JOIN_WAIT = 8 local MAX_ATTEMPTS = 5 local function toggleOn(id) return Toggles ~= nil and Toggles[id] ~= nil and Toggles[id].Value == true end local function optionValue(id, fallback) local option = Options and Options[id] if option == nil or option.Value == nil then return fallback end return option.Value end local function notify(text, seconds) if Library and Library.Notify then pcall(function() Library:Notify({ Title = 'Serverhop', Description = text, Time = seconds or 4 }) end) end end local function currentPlaceId() return game.PlaceId end local function chosenCount() return tonumber(optionValue('ZSHD1', 'Any')) end -- ---------------------------------------------------------------- one page from Roblox local function httpGet(url) local requestFunction = (syn and syn.request) or request or http_request or (http and http.request) if type(requestFunction) == 'function' then local ok, response = pcall(requestFunction, { Url = url, Method = 'GET' }) if ok and type(response) == 'table' then return response.StatusCode, response.Body end return nil, tostring(response) end local ok, body = pcall(game.HttpGet, game, url) if ok then return 200, body end return nil, tostring(body) end -- returns the servers of one page and the cursor of the next one (nil at the end); on failure nil, nil and the reason function Serverhop.FetchPage(placeId, direction, cursor) local url = string.format( 'https://games.roblox.com/v1/games/%d/servers/Public?sortOrder=%s&limit=100%s', placeId, direction, cursor and ('&cursor=' .. cursor) or '' ) local status, body = httpGet(url) if status == 429 then Serverhop.LimitedUntil = os.clock() + LIMIT_PAUSE return nil, nil, 'Roblox is limiting the server list' end if status ~= 200 then return nil, nil, 'the server list did not load (' .. tostring(status) .. ')' end local ok, decoded = pcall(HttpService.JSONDecode, HttpService, body) if not ok or type(decoded) ~= 'table' or type(decoded.data) ~= 'table' then return nil, nil, 'the server list answer was not readable' end local servers = {} for _, entry in ipairs(decoded.data) do if type(entry) == 'table' and type(entry.id) == 'string' then servers[#servers + 1] = { id = entry.id, playing = tonumber(entry.playing) or 0, max = tonumber(entry.maxPlayers) or 0, ping = tonumber(entry.ping), fps = tonumber(entry.fps), } end end return servers, type(decoded.nextPageCursor) == 'string' and decoded.nextPageCursor or nil end -- ---------------------------------------------------------------- the pool and its two frontiers function Serverhop.Sync() local list = {} for _, server in pairs(Serverhop.Pool) do list[#list + 1] = server end Serverhop.Servers = list Serverhop.LoadedAt = os.clock() end function Serverhop.Absorb(direction, servers, nextCursor) local frontier = Serverhop.Frontier[direction] frontier.Started = true frontier.Cursor = nextCursor frontier.Done = nextCursor == nil for _, server in ipairs(servers) do Serverhop.Pool[server.id] = server end local last = servers[#servers] if last then if direction == 'Asc' then frontier.Count = math.max(frontier.Count or 0, last.playing) else frontier.Count = math.min(frontier.Count or math.huge, last.playing) end end Serverhop.Sync() end function Serverhop.HasCount(count) for _, server in pairs(Serverhop.Pool) do if server.playing == count then return true end end return false end -- true when a page boundary has already gone past this player count, so the pool holds every server that has it function Serverhop.Covers(count) local asc, desc = Serverhop.Frontier.Asc, Serverhop.Frontier.Desc if asc.Started and (asc.Done or (asc.Count ~= nil and count < asc.Count)) then return true end if desc.Started and (desc.Done or (desc.Count ~= nil and count > desc.Count)) then return true end return false end -- the end that has the shorter way left to a player count (nil when both ends are read to the finish) function Serverhop.NextDirection(count) local asc, desc = Serverhop.Frontier.Asc, Serverhop.Frontier.Desc if asc.Done and desc.Done then return nil end local ascGap = asc.Done and math.huge or math.abs(count - (asc.Count or 0)) local descGap = desc.Done and math.huge or math.abs((desc.Count or MAX_PLAYERS) - count) return ascGap <= descGap and 'Asc' or 'Desc' end -- reads the next page of one end function Serverhop.Step(direction) local frontier = Serverhop.Frontier[direction] local servers, nextCursor, reason = Serverhop.FetchPage(currentPlaceId(), direction, frontier.Cursor) if not servers then return false, reason end Serverhop.Absorb(direction, servers, nextCursor) return true end -- pages towards a player count until one shows up, a boundary passes it, Roblox says wait or the budget is gone function Serverhop.Search(count, steps) for _ = 1, steps or SEARCH_STEPS do if Serverhop.HasCount(count) then return true end if Serverhop.Covers(count) then return false end -- a page boundary went past it: there is none if os.clock() < Serverhop.LimitedUntil then return false, 'Roblox is limiting the server list' end local direction = Serverhop.NextDirection(count) if not direction then return false, 'that is the whole list' end local ok, reason = Serverhop.Step(direction) if not ok then return false, reason end if Serverhop.Rebuild then Serverhop.Rebuild() end task.wait(PAGE_GAP) end return Serverhop.HasCount(count) end -- servers worth showing: not this one, not full, and the chosen player count when one is chosen function Serverhop.Filter(servers, count, currentJobId) local out = {} for _, server in ipairs(servers) do local isOther = server.id ~= currentJobId local hasRoom = server.max <= 0 or server.playing < server.max local countOk = count == nil or server.playing == count if isOther and hasRoom and countOk then out[#out + 1] = server end end return out end local function pingOf(server) return server.ping or math.huge end -- display order: lowest ping first, then fewest players function Serverhop.SortByPing(servers) local sorted = table.clone(servers) table.sort(sorted, function(a, b) if pingOf(a) ~= pingOf(b) then return pingOf(a) < pingOf(b) end if a.playing ~= b.playing then return a.playing < b.playing end return a.id < b.id end) return sorted end function Serverhop.SortByPlayers(servers) local sorted = table.clone(servers) table.sort(sorted, function(a, b) if a.playing ~= b.playing then return a.playing < b.playing end if pingOf(a) ~= pingOf(b) then return pingOf(a) < pingOf(b) end return a.id < b.id end) return sorted end function Serverhop.Label(index, server) local color = Library and Library.Palette and Library.Palette.accent or nil local hex = typeof(color) == 'Color3' and color:ToHex() or '7d55ff' local fps = server.fps and string.format('%d', server.fps) or '?' local ping = server.ping and string.format('%d', server.ping) or '?' return string.format( 'Server #%d %d/%d fps: %s ping: %s', index, hex, server.playing, server.max, hex, fps, hex, ping ) end -- ---------------------------------------------------------------- joining -- tries the candidates in order; a teleport that fails (or never leaves) moves on to the next one function Serverhop.JoinFrom(candidates) if Serverhop.Joining then notify('Already joining a server.') return false end if #candidates == 0 then notify('No server to join. Refresh the list or change the player count.') return false end Serverhop.Joining = true task.spawn(function() local failed = false local connection = TeleportService.TeleportInitFailed:Connect(function() failed = true end) for attempt = 1, math.min(#candidates, MAX_ATTEMPTS) do local server = candidates[attempt] failed = false notify(string.format('Joining server %d of %d (%d players, ping %s).', attempt, math.min(#candidates, MAX_ATTEMPTS), server.playing, server.ping and tostring(math.floor(server.ping)) or '?'), 3) local ok = pcall(TeleportService.TeleportToPlaceInstance, TeleportService, currentPlaceId(), server.id, LP) if not ok then failed = true end local waited = 0 while not failed and waited < JOIN_WAIT do task.wait(0.25) waited = waited + 0.25 end if not failed then break end -- still here after the wait means the teleport is under way if attempt < math.min(#candidates, MAX_ATTEMPTS) then notify('That server did not accept you. Trying the next one.', 3) end end connection:Disconnect() Serverhop.Joining = false end) return true end -- ---------------------------------------------------------------- what the tab shows function Serverhop.Visible() return Serverhop.SortByPing(Serverhop.Filter(Serverhop.Servers, chosenCount(), game.JobId)) end function Serverhop.StatusText(shown) local asc, desc = Serverhop.Frontier.Asc, Serverhop.Frontier.Desc local loaded = #Serverhop.Servers local text = string.format('%d shown, %d loaded', shown, loaded) if loaded > 0 then local from = asc.Started and tostring(asc.Count or '?') or '-' local to = desc.Started and tostring(desc.Count or '?') or '-' text = text .. string.format(' (up to %s players from the bottom, down to %s from the top)', from, to) end local count = chosenCount() if shown == 0 and count then if os.clock() < Serverhop.LimitedUntil then text = string.format('No server with %d players loaded yet. Roblox is limiting the list: try again in %ds.', count, math.ceil(Serverhop.LimitedUntil - os.clock())) elseif Serverhop.Covers(count) then text = string.format('No server has %d players right now.', count) else text = string.format('No server with %d players loaded yet. Press Load More.', count) end end return text end function Serverhop.Rebuild() local list = Options and Options.ZSHL1 if not list then return end local visible = Serverhop.Visible() local labels = {} Serverhop.Labels = {} for index, server in ipairs(visible) do local label = Serverhop.Label(index, server) labels[#labels + 1] = label Serverhop.Labels[label] = server end list:SetValues(labels) if Serverhop.StatusLabel then Serverhop.StatusLabel:SetText(Serverhop.StatusText(#labels)) end end -- a fresh start: the first page of each end; if Roblox refuses both, what was loaded before stays function Serverhop.Refresh(manual) if Serverhop.Busy then return end if os.clock() < Serverhop.LimitedUntil then if manual then notify(string.format('Roblox is limiting the server list. Try again in %ds.', math.ceil(Serverhop.LimitedUntil - os.clock())), 3) end return end if os.clock() - Serverhop.FetchedAt < MIN_FETCH_GAP then if manual then notify('Wait a moment before refreshing again.', 2) end return end Serverhop.Busy = true Serverhop.FetchedAt = os.clock() task.spawn(function() local kept = { Pool = Serverhop.Pool, Servers = Serverhop.Servers, Frontier = Serverhop.Frontier, LoadedAt = Serverhop.LoadedAt } Serverhop.Pool, Serverhop.Frontier = {}, freshFrontier() local okAsc, reasonAsc = Serverhop.Step('Asc') task.wait(PAGE_GAP) local okDesc, reasonDesc = Serverhop.Step('Desc') if not okAsc and not okDesc then Serverhop.Pool, Serverhop.Servers, Serverhop.Frontier, Serverhop.LoadedAt = kept.Pool, kept.Servers, kept.Frontier, kept.LoadedAt if #kept.Servers > 0 then notify('Showing the last list: ' .. tostring(reasonAsc or reasonDesc) .. '.', 4) else notify((tostring(reasonAsc or reasonDesc):gsub('^%l', string.upper)) .. '.', 4) end else local count = chosenCount() if count then Serverhop.Search(count) end end Serverhop.Busy = false if not Library.Unloaded then Serverhop.Rebuild() end end) end -- one more page, from the end that is nearer to the chosen player count (or the two ends in turn) function Serverhop.LoadMore() if Serverhop.Busy then return end if os.clock() < Serverhop.LimitedUntil then notify(string.format('Roblox is limiting the server list. Try again in %ds.', math.ceil(Serverhop.LimitedUntil - os.clock())), 3) return end local count = chosenCount() local direction = count and Serverhop.NextDirection(count) if not direction then direction = Serverhop.LastMore == 'Desc' and 'Asc' or 'Desc' if Serverhop.Frontier[direction].Done then direction = direction == 'Asc' and 'Desc' or 'Asc' end end if Serverhop.Frontier[direction].Done then notify('The whole server list is loaded.', 3) return end Serverhop.LastMore = direction Serverhop.Busy = true task.spawn(function() local ok, reason = Serverhop.Step(direction) if not ok then notify(tostring(reason):gsub('^%l', string.upper) .. '.', 4) end Serverhop.Busy = false if not Library.Unloaded then Serverhop.Rebuild() end end) end -- the player count was changed: show what is loaded, then look for it if it is not there yet function Serverhop.CountChanged() Serverhop.Rebuild() local count = chosenCount() if not count or Serverhop.Busy or Serverhop.HasCount(count) or Serverhop.Covers(count) then return end if #Serverhop.Servers == 0 then return end Serverhop.Busy = true task.spawn(function() local found, reason = Serverhop.Search(count) if not found and reason then notify(tostring(reason):gsub('^%l', string.upper) .. '.', 4) end Serverhop.Busy = false if not Library.Unloaded then Serverhop.Rebuild() end end) end local function selectedServer() local list = Options and Options.ZSHL1 return list and Serverhop.Labels[list.Value] or nil end -- the picked server first, the rest by ping as the fallbacks function Serverhop.JoinSelected() local picked = selectedServer() if not picked then notify('Pick a server in the list first.') return end local queue = { picked } for _, server in ipairs(Serverhop.Visible()) do if server.id ~= picked.id then queue[#queue + 1] = server end end Serverhop.JoinFrom(queue) end function Serverhop.JoinLowestPing() Serverhop.JoinFrom(Serverhop.SortByPing(Serverhop.Filter(Serverhop.Servers, nil, game.JobId))) end function Serverhop.JoinLowestPlayers() Serverhop.JoinFrom(Serverhop.SortByPlayers(Serverhop.Filter(Serverhop.Servers, nil, game.JobId))) end -- ---------------------------------------------------------------- the tab (called from the menu assembly) function Serverhop.BuildTab(Window) local tab = Window:AddTab('Serverhop', 'server') local group = tab:AddLeftGroupbox('Servers') local counts = { 'Any' } for number = 1, MAX_PLAYERS do counts[#counts + 1] = tostring(number) end group:AddDropdown('ZSHD1', { Text = 'Focus Player Count', Values = counts, Default = 'Any', Multi = false, Searchable = true, Tooltip = 'Only list servers with exactly this many players (1 to 40). If none is loaded yet it is searched for, from whichever end of the list is nearer.', }) if group.AddList then group:AddList('ZSHL1', { Text = 'Server List', Height = 230, Empty = 'Press Refresh' }) else group:AddDropdown('ZSHL1', { Text = 'Server List', Values = {}, Default = nil, Multi = false, Searchable = true }) end Serverhop.StatusLabel = group:AddLabel('Press Refresh to load the servers.') group:AddButton({ Text = 'Join', Tooltip = 'Joins the server picked in the list; if it will not take you the next best one is tried.', Func = GuardRivalsCallback('Serverhop_Join', Serverhop.JoinSelected) }) group:AddButton({ Text = 'Join Lowest Ping', Tooltip = 'Joins the server with the lowest ping.', Func = GuardRivalsCallback('Serverhop_Ping', Serverhop.JoinLowestPing) }) group:AddButton({ Text = 'Join Lowest Player', Tooltip = 'Joins the emptiest server.', Func = GuardRivalsCallback('Serverhop_Players', Serverhop.JoinLowestPlayers) }) group:AddButton({ Text = 'Refresh', Tooltip = 'Loads the server list again. If Roblox limits it, what is loaded stays.', Func = GuardRivalsCallback('Serverhop_Refresh', function() Serverhop.Refresh(true) end) }) group:AddButton({ Text = 'Load More', Tooltip = 'Reads one more page of the list (Roblox only allows a few a minute).', Func = GuardRivalsCallback('Serverhop_More', Serverhop.LoadMore) }) Options.ZSHD1:OnChanged(GuardRivalsCallback('Serverhop_Count', Serverhop.CountChanged)) -- the first list loads shortly after the tab is built, so it is there when the tab is opened task.delay(3, GuardRivalsCallback('Serverhop_First', function() Serverhop.Refresh(false) end)) end if type(getgenv) == 'function' then local probe = getgenv().__ZkxHubTest if type(probe) == 'table' then probe.Serverhop = Serverhop end end end -- ====================================================================== -- Menu: assembly -- ====================================================================== BuildRivalsUi = function(Window) BuildRivalsEspUi(Window) __ZkhHub_genv.ZkxLoad(0.50, 'Building Combat') local CombatTab = BuildRivalsAimbotUi(Window) BuildRivalsModsUi(CombatTab) __ZkhHub_genv.ZkxLoad(0.58, 'Building Ragebot') BuildRivalsRagebotUi(Window) __ZkhHub_genv.ZkxLoad(0.61, 'Building Classic Ragebot') RivalsRuntimeBridge.BuildClassicRagebotUi(Window) __ZkhHub_genv.ZkxLoad(0.64, 'Building Automation') local AutomationTab = BuildRivalsPickupUi(Window) BuildRivalsAntihitUi(AutomationTab) __ZkhHub_genv.ZkxLoad(0.72, 'Building Visuals') BuildRivalsVisualsUi(Window) __ZkhHub_genv.ZkxLoad(0.80, 'Building Misc') local MiscTab = BuildRivalsMiscUi(Window) BuildRivalsCosmeticsUi(MiscTab) RivalsRuntimeBridge.Serverhop.BuildTab(Window) __ZkhHub_genv.ZkxLoad(0.88, 'Finishing the menu') RivalsRuntimeBridge.Extras.CollapseLongGroups() end local RivalsRuntime = {} -- ====================================================================== -- Runtime lifecycle: teardown, loops, startup -- ====================================================================== function RivalsRuntime.ResetState() RivalsRuntimeBridge.Freecam.Disable() RivalsRuntimeBridge.CameraTilt.Destroy() RivalsRuntimeBridge.WorldVisuals.Destroy() RivalsRuntimeBridge.ViewmodelVisuals.Destroy() RivalsRuntimeBridge.NativeRemovals.Destroy() RivalsRagebotState.RespawnBinding = nil RivalsRuntimeBridge.Movement.Destroy() RivalsRuntimeBridge.DeviceSpoof.Destroy() RivalsRuntimeBridge.PlayerSpoofer.Destroy() RivalsRuntimeBridge.AnimationPlayer.Destroy() RivalsRuntimeBridge.MovementRecorder.Destroy() if RivalsRuntimeBridge.ZHubRagebotDestroy then RivalsRuntimeBridge.ZHubRagebotDestroy() end if RivalsRuntimeBridge.DestroyZkhHubRagebot then RivalsRuntimeBridge.DestroyZkhHubRagebot() end RivalsRuntimeBridge.CombatFeedback.Destroy() if RivalsRuntimeBridge.HitLog then RivalsRuntimeBridge.HitLog.Destroy() end if RivalsRuntimeBridge.NameStyle then RivalsRuntimeBridge.NameStyle.Destroy() end if RivalsRuntimeBridge.PlayerChams then RivalsRuntimeBridge.PlayerChams.Destroy() end RivalsRuntimeBridge.AimbotTargeting.Destroy() RivalsRuntimeBridge.CombatBot.Destroy() RivalsRuntimeBridge.CheaterDetector.Reset() RivalsRuntimeBridge.ResetRivalsCosmetics() RivalsModsState.RestoreHooks() AimbotBridge.ResetAimbotRuntimeState() AimbotBridge.ResetTriggerbotReactionState() AimbotBridge.ResetCameraAimRandomState() RivalsRuntimeBridge.ResetFlickbot() RivalsRagebotState.Target = nil RivalsRagebotState.HoldCFrame = nil RivalsRagebotState.LastHoldUpdateAt = 0 RivalsRagebotState.LastRespawnRequestAt = 0 RivalsRagebotState.PhaseClockSec = 0 RivalsRuntimeBridge.ResetAutoLoadoutState() RivalsRuntimeBridge.ResetAutoBanState() RivalsRuntimeBridge.ResetTripmineAutomation() LocalPickupObserver.ResetDropTracking() AimbotTriggerbotLastShotAt = 0 MeleeFastFire.LastShotAt = 0 RivalsRuntimeBridge.DestroyAimbotStatusNotification() AimbotInputState.LeftMouse = false AimbotInputState.RightMouse = false AimbotInputState.TouchCount = 0 AimbotFovCircle.Visible = false pcall(function() AimbotFovCircle:Remove() end) DestroyAllESP() WorldESPState.DestroyAllThrowable() WorldESPState.DestroyAllHazard() WorldESPState.DestroyAllSoundVisualizer() DestroyRuntimeIssueNotification() EventZombieCleanup() end function RivalsRuntime.RegisterPlayerLifecycle() RivalsRuntimeBridge.NextESPReconcileAt = 0 if EspRenderSettings.Enabled then ReconcileESPPlayers() end Library:GiveSignal( Players.PlayerRemoving:Connect(GuardRivalsCallback('Player_Removing', function(player) DestroyESP(player) end)) ) EnsureWeaponInfoTracking() end function RivalsRuntime.RegisterWorldLifecycle() for _, child in ipairs(Workspace:GetChildren()) do WorldESPState.RegisterThrowable(child) LocalPickupObserver.ObserveDrop(child) end Connections:register( 'Throwable_Added', Workspace.ChildAdded:Connect(GuardRivalsCallback('Throwable_Added', WorldESPState.RegisterThrowable)) ) Connections:register( 'Throwable_Removed', Workspace.ChildRemoved:Connect(GuardRivalsCallback('Throwable_Removed', WorldESPState.DestroyThrowable)) ) Connections:register( 'PickupDrop_Added', Workspace.ChildAdded:Connect(GuardRivalsCallback('PickupDrop_Added', LocalPickupObserver.ObserveDrop)) ) Connections:register( 'PickupDrop_Removed', Workspace.ChildRemoved:Connect(GuardRivalsCallback('PickupDrop_Removed', LocalPickupObserver.UnobserveDrop)) ) for _, descendant in ipairs(Workspace:GetDescendants()) do WorldESPState.RegisterHazard(descendant) end Connections:register( 'Hazard_Added', Workspace.DescendantAdded:Connect(GuardRivalsCallback('Hazard_Added', WorldESPState.RegisterHazard)) ) Connections:register( 'Hazard_Removed', Workspace.DescendantRemoving:Connect(GuardRivalsCallback('Hazard_Removed', WorldESPState.DestroyHazard)) ) WorldESPState.SetSoundVisualizerEnabled(Toggles.P1S3T1 and Toggles.P1S3T1.Value == true) RivalsRuntimeBridge.BindTripmineAutomation() end function RivalsRuntime.RegisterInputLifecycle() Connections:register( 'Aimbot_InputBegan', UserInputService.InputBegan:Connect(GuardRivalsCallback('Aimbot_InputBegan', function(input, gameProcessed) if gameProcessed then return end if input.UserInputType == Enum.UserInputType.MouseButton1 then AimbotInputState.LeftMouse = true elseif input.UserInputType == Enum.UserInputType.MouseButton2 then AimbotInputState.RightMouse = true end end)) ) Connections:register( 'Aimbot_InputEnded', UserInputService.InputEnded:Connect(GuardRivalsCallback('Aimbot_InputEnded', function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 then AimbotInputState.LeftMouse = false elseif input.UserInputType == Enum.UserInputType.MouseButton2 then AimbotInputState.RightMouse = false end end)) ) Connections:register( 'Aimbot_TouchStarted', UserInputService.TouchStarted:Connect(GuardRivalsCallback('Aimbot_TouchStarted', function(touch, gameProcessed) if gameProcessed then return end AimbotInputState.TouchCount = AimbotInputState.TouchCount + 1 end)) ) Connections:register( 'Aimbot_TouchEnded', UserInputService.TouchEnded:Connect( GuardRivalsCallback( 'Aimbot_TouchEnded', function() AimbotInputState.TouchCount = math.max(AimbotInputState.TouchCount - 1, 0) end ) ) ) end function RivalsRuntime.WaitForInitialLoadInSettle() RivalsRuntimeBridge.WaitForLocalReady(2) InitCharacter() if RivalsRuntimeBridge.IsAutoQueueLobby and RivalsRuntimeBridge.IsAutoQueueLobby() then if type(RivalsRuntimeBridge.RefreshAutoQueueDropdownValues) == 'function' then pcall(RivalsRuntimeBridge.RefreshAutoQueueDropdownValues) end if Toggles and Toggles.P8S6T1 and Toggles.P8S6T1.Value == true and type(RivalsRuntimeBridge.FireAutoQueueJoin) == 'function' then local belief = type(RivalsRuntimeBridge.GetAutoQueueBelief) == 'function' and RivalsRuntimeBridge.GetAutoQueueBelief() or 'idle' if belief == 'idle' then pcall(RivalsRuntimeBridge.FireAutoQueueJoin) end end end end function RivalsRuntime.StartLoops() Connections:register( 'ESP_Render', RunService.RenderStepped:Connect(GuardRivalsCallback('ESP_Render', RivalsRuntimeBridge.UpdateESP)) ) Connections:register( 'Throwable_Render', RunService.RenderStepped:Connect(GuardRivalsCallback('Throwable_Render', WorldESPState.UpdateThrowable)) ) Connections:register( 'Hazard_Render', RunService.RenderStepped:Connect(GuardRivalsCallback('Hazard_Render', WorldESPState.UpdateHazard)) ) Connections:register( 'CombatFeedback_Render', RunService.RenderStepped:Connect( GuardRivalsCallback('CombatFeedback_Render', RivalsRuntimeBridge.CombatFeedback.Update) ) ) Connections:register( 'ViewmodelVisuals_Render', RunService.RenderStepped:Connect( GuardRivalsCallback('ViewmodelVisuals_Render', RivalsRuntimeBridge.ViewmodelVisuals.Update) ) ) Connections:register( 'Movement_PreSimulation', RunService.PreSimulation:Connect(GuardRivalsCallback('Movement_PreSimulation', RivalsRuntimeBridge.Movement.Update)) ) Connections:register( 'MovementRecorder_PreSimulation', RunService.PreSimulation:Connect( GuardRivalsCallback('MovementRecorder_PreSimulation', RivalsRuntimeBridge.MovementRecorder.Update) ) ) Connections:register( 'Movement_BlankFlight', RunService.RenderStepped:Connect( GuardRivalsCallback('Movement_BlankFlight', RivalsRuntimeBridge.Movement.BlankFlightVelocity) ) ) Connections:register( 'AnimationPlayer_Render', RunService.RenderStepped:Connect( GuardRivalsCallback('AnimationPlayer_Render', RivalsRuntimeBridge.AnimationPlayer.Update) ) ) Connections:register( 'CameraModifiers_Render', RunService.RenderStepped:Connect( GuardRivalsCallback('CameraModifiers_Render', RivalsModsState.UpdateCameraModifiers) ) ) Connections:register( 'MovementRecorder_Render', RunService.RenderStepped:Connect( GuardRivalsCallback('MovementRecorder_Render', RivalsRuntimeBridge.MovementRecorder.UpdateRender) ) ) Connections:register( 'Flickbot_Render', RunService.RenderStepped:Connect(GuardRivalsCallback('Flickbot_Render', RivalsRuntimeBridge.UpdateFlickbot)) ) Connections:register( 'CameraAim_Render', RunService.RenderStepped:Connect(GuardRivalsCallback('CameraAim_Render', RivalsRuntimeBridge.UpdateCameraAim)) ) -- bound at the last render priority so the snapline is projected after the camera moved this frame RunService:BindToRenderStep( 'ZkxHubTargetingDraw', Enum.RenderPriority.Last.Value, GuardRivalsCallback('AimbotTargetCircle_Render', RivalsRuntimeBridge.AimbotTargeting.UpdateCircle) ) Connections:register('AimbotTargetCircle_Render', function() pcall(function() RunService:UnbindFromRenderStep('ZkxHubTargetingDraw') end) end) Connections:register( 'ThirdPersonOffset_Render', RunService.RenderStepped:Connect( GuardRivalsCallback('ThirdPersonOffset_Render', RivalsModsState.UpdateThirdPersonOffset) ) ) RivalsRuntimeBridge.CombatBot.Start() Connections:register( 'CheaterDetector_Heartbeat', RunService.Heartbeat:Connect( GuardRivalsCallback('CheaterDetector_Heartbeat', RivalsRuntimeBridge.CheaterDetector.Step) ) ) Connections:register( 'Aimbot_Render', RunService.Heartbeat:Connect(GuardRivalsCallback('Aimbot_Render', RivalsRuntimeBridge.UpdateAimbot)) ) Connections:register( 'Ragebot_Heartbeat', RunService.Heartbeat:Connect(GuardRivalsCallback('Ragebot_Heartbeat', RivalsRuntimeBridge.UpdateRagebot)) ) Connections:register( 'TripmineAutomation_Heartbeat', RunService.Heartbeat:Connect( GuardRivalsCallback('TripmineAutomation_Heartbeat', RivalsRuntimeBridge.UpdateTripmineAutomation) ) ) local AutoQueueRemotes = ReplicatedStorage:FindFirstChild('Remotes') local AutoQueueMatchmaking = AutoQueueRemotes and AutoQueueRemotes:FindFirstChild('Matchmaking') local AutoQueueStatus = AutoQueueMatchmaking and AutoQueueMatchmaking:FindFirstChild('UpdateQueueStatus') if AutoQueueStatus and AutoQueueStatus:IsA('RemoteEvent') then Connections:register( 'AutoQueue_StatusUpdate', AutoQueueStatus.OnClientEvent:Connect( GuardRivalsCallback('AutoQueue_StatusUpdate', RivalsRuntimeBridge.HandleAutoQueueStatusUpdate) ) ) end Connections:register( 'AutoQueue_MatchmadeGameOver', workspace:GetAttributeChangedSignal('MatchmadeGameOver'):Connect( GuardRivalsCallback('AutoQueue_MatchmadeGameOver', RivalsRuntimeBridge.OnAutoQueueMatchmadeGameOverChanged) ) ) Connections:register( 'AutoQueue_LobbyReturn_Teleport', game:GetService('TeleportService').LocalPlayerArrivedFromTeleport :Connect(GuardRivalsCallback('AutoQueue_LobbyReturn_Teleport', RivalsRuntimeBridge.OnAutoQueueLobbyReturn)) ) RivalsRuntimeBridge.CombatFeedback.RefreshBinding() RivalsRuntimeBridge.MovementRecorder.BindRoundSignal() -- Zombie event loops Connections:register( 'EventZombie_Render', RunService.RenderStepped:Connect(GuardRivalsCallback('EventZombie_Render', EventZombieStep)) ) task.spawn(GuardRivalsCallback('EventZombie_FireLoop', EventZombieFireLoop)) Connections:register( 'Tower_Render', RunService.RenderStepped:Connect(GuardRivalsCallback('Tower_Render', RivalsRuntimeBridge.TowerKit.Step)) ) task.spawn(GuardRivalsCallback('EventZombie_ResolveModules', function() for _ = 1, 60 do EventZombieResolveModules() if EventZombieState.UseItemRemote and EventZombieState.StartShootingToken and EventZombieState.Utility then break end task.wait(0.5) end -- Refresh the status label now that the modules are up if EventZombieState.StatusLabel and EventZombieState.StatusLabel.SetText then pcall(function() EventZombieState.StatusLabel:SetText(EventZombieBuildStatus()) end) end end)) task.spawn(GuardRivalsCallback('EventZombie_StatusLoop', function() while EventZombieState.Running and not Library.Unloaded do task.wait(0.5) local label = EventZombieState.StatusLabel if label and label.SetText then pcall(function() label:SetText(EventZombieBuildStatus()) end) end end end)) end function RivalsRuntime.RegisterCharacterLifecycle() RefreshLocalPickupHealthObserver() AttachWeaponInfoObservers(LP) RivalsModsState.EnsureHooks() RivalsRuntimeBridge.ApplyRivalsCosmetics() RivalsRuntimeBridge.RegisterAutoRespawnSignals() RivalsRuntimeBridge.RegisterAutoBanSignals() RivalsRuntimeBridge.RegisterAutoQueuePlayAgainSignals() RivalsRuntimeBridge.RunPickupSpawnSetup = function() task.defer(function() if not RivalsRuntimeBridge.WaitForLocalReady(30) then RivalsRuntimeBridge.LogDiagnosticEvent('Spawn', 'WaitForLocalReady timed out; per-spawn setup aborted') return end local fighter = ResolveLocalFighter() if not fighter then RivalsRuntimeBridge.LogDiagnosticEvent( 'Spawn', 'Local fighter unavailable after readiness; per-spawn setup aborted' ) return end FighterDataCache.AttachLocalDuelWatchers(fighter) RivalsRuntimeBridge.RegisterAutoRespawnSignals() RivalsRuntimeBridge.RegisterAutoBanSignals() RivalsRuntimeBridge.RegisterAutoQueuePlayAgainSignals() if not InitCharacter() then RivalsRuntimeBridge.LogDiagnosticEvent( 'Spawn', 'InitCharacter failed after fighter ready; per-spawn setup aborted' ) return end if not Humanoid or Humanoid.Health <= 0 then RivalsRuntimeBridge.LogDiagnosticEvent('Spawn', 'Humanoid missing or dead at per-spawn; setup aborted') return end RefreshLocalPickupHealthObserver() AttachWeaponInfoObservers(LP) RivalsModsState.EnsureHooks() UpdateRivalsPickupFeatures() RivalsRuntimeBridge.ApplyRivalsCosmetics() end) end if LP.Character then RivalsRuntimeBridge.RunPickupSpawnSetup() end Library:GiveSignal(LP.CharacterAdded:Connect(GuardRivalsCallback('Pickup_CharacterAdded', function(newChar) RivalsRuntimeBridge.LogDiagnosticEvent( 'CharAdded', 'LP.CharacterAdded fired (new: ' .. tostring(newChar and newChar.Name or '') .. ')' ) RivalsRuntimeBridge.ResetLocalShieldLatch() RivalsRuntimeBridge.RunPickupSpawnSetup() end))) end -- ====================================================================== -- Startup: config profiles, menu settings, loops -- ====================================================================== function RivalsRuntime.Initialize(Window) -- Config & Setup Connections = ConnectionRegistry.new() local ReconcileLoadedState local RivalsConfigProfiles = { Names = { 'Legit', 'Rage' }, } local SharedConfigDefaultObjects = { { idx = 'P1S1T1', type = 'Toggle', value = true }, { idx = 'P1S1T2', type = 'Toggle', value = true }, { idx = 'P1S1T3', type = 'Toggle', value = true }, { idx = 'P1S1T4', type = 'Toggle', value = true }, { idx = 'P1S1T5', type = 'Toggle', value = true }, { idx = 'P1S1T6', type = 'Toggle', value = true }, { idx = 'P1S1T7', type = 'Toggle', value = false }, { idx = 'P1S1T8', type = 'Toggle', value = true }, { idx = 'P1S1T9', type = 'Toggle', value = true }, { idx = 'P1S1T10', type = 'Toggle', value = false }, { idx = 'P1S1T11', type = 'Toggle', value = true }, { idx = 'P1S1C1', type = 'ColorPicker', value = '00ff00', transparency = 0 }, { idx = 'P1S1C2', type = 'ColorPicker', value = 'ff0000', transparency = 0 }, { idx = 'P1S1C4', type = 'ColorPicker', value = 'ffff00', transparency = 0 }, { idx = 'P1S1S3', type = 'Slider', value = 1 }, { idx = 'P1S1S4', type = 'Slider', value = 0 }, { idx = 'P1S2T1', type = 'Toggle', value = true }, { idx = 'P1S2T2', type = 'Toggle', value = true }, { idx = 'P1S2T4', type = 'Toggle', value = true }, { idx = 'P1S2T5', type = 'Toggle', value = true }, { idx = 'P1S2T6', type = 'Toggle', value = true }, { idx = 'P1S3T1', type = 'Toggle', value = false }, { idx = 'P1S3D1', type = 'Dropdown', value = 'Enemies' }, { idx = 'P1S3T2', type = 'Toggle', value = true }, { idx = 'P1S3T3', type = 'Toggle', value = true }, { idx = 'P1S3S1', type = 'Slider', value = 0.05 }, { idx = 'P1S3C1', type = 'ColorPicker', value = 'ffd250', transparency = 0 }, { idx = 'P1S3C2', type = 'ColorPicker', value = 'ff5a5a', transparency = 0 }, { idx = 'P1S4T2', type = 'Toggle', value = false }, { idx = 'P1S4C1', type = 'ColorPicker', value = '3bcbff', transparency = 0 }, { idx = 'P1S4C2', type = 'ColorPicker', value = '01668b', transparency = 0 }, { idx = 'P1S4T3', type = 'Toggle', value = false }, { idx = 'P1S4S1', type = 'Slider', value = 12 }, { idx = 'P1S4T4', type = 'Toggle', value = false }, { idx = 'P1S4S2', type = 'Slider', value = 2 }, { idx = 'P1S4T5', type = 'Toggle', value = false }, { idx = 'P1S4C3', type = 'ColorPicker', value = '64a0ff', transparency = 0 }, { idx = 'P1S4C4', type = 'ColorPicker', value = '14285a', transparency = 0 }, { idx = 'P1S4T6', type = 'Toggle', value = false }, { idx = 'P1S4S3', type = 'Slider', value = 0 }, { idx = 'P1S4T7', type = 'Toggle', value = false }, { idx = 'P1S4S4', type = 'Slider', value = 0 }, { idx = 'P1S4T8', type = 'Toggle', value = false }, { idx = 'P1S4S5', type = 'Slider', value = 0 }, { idx = 'P1S5T1', type = 'Toggle', value = false }, { idx = 'P1S5C1', type = 'ColorPicker', value = 'c0c0c0', transparency = 0 }, { idx = 'P1S5S1', type = 'Slider', value = 0 }, { idx = 'P1S5S2', type = 'Slider', value = 10000 }, { idx = 'P1S5T2', type = 'Toggle', value = false }, { idx = 'P1S5S3', type = 'Slider', value = 41.733 }, { idx = 'P1S5T3', type = 'Toggle', value = false }, { idx = 'P1S5T4', type = 'Toggle', value = false }, { idx = 'P1S5S4', type = 'Slider', value = 0.5 }, { idx = 'P1S5T5', type = 'Toggle', value = false }, { idx = 'P1S5D1', type = 'Dropdown', value = 'Realistic' }, { idx = 'P1S5T6', type = 'Toggle', value = false }, { idx = 'P1S5C2', type = 'ColorPicker', value = 'b3b3b8', transparency = 0 }, { idx = 'P1S5T7', type = 'Toggle', value = false }, { idx = 'P1S6T1', type = 'Toggle', value = false }, { idx = 'P1S6S1', type = 'Slider', value = 0.4 }, { idx = 'P1S6S2', type = 'Slider', value = 24 }, { idx = 'P1S6S3', type = 'Slider', value = 0.95 }, { idx = 'P1S7T1', type = 'Toggle', value = false }, { idx = 'P1S7C1', type = 'ColorPicker', value = 'ffffff', transparency = 0 }, { idx = 'P1S7S1', type = 'Slider', value = 0 }, { idx = 'P1S7S2', type = 'Slider', value = 0 }, { idx = 'P1S7S3', type = 'Slider', value = 0 }, { idx = 'P1S8T1', type = 'Toggle', value = false }, { idx = 'P1S8D1', type = 'Dropdown', value = 'Default' }, { idx = 'P1S10T1', type = 'Toggle', value = false }, { idx = 'P1S10S1', type = 'Slider', value = 0.25 }, { idx = 'P1S10S2', type = 'Slider', value = 1 }, { idx = 'PX1T1', type = 'Toggle', value = false }, { idx = 'PX1S1', type = 'Slider', value = 0.75 }, { idx = 'PX1S2', type = 'Slider', value = 25 }, { idx = 'PX1S3', type = 'Slider', value = 30 }, { idx = 'PX1S4', type = 'Slider', value = 0.5 }, { idx = 'PX2T1', type = 'Toggle', value = false }, { idx = 'PX2S1', type = 'Slider', value = 14 }, { idx = 'PX2S2', type = 'Slider', value = 1 }, { idx = 'PX2D1', type = 'Dropdown', value = 'Camera + Movement' }, { idx = 'PX2S3', type = 'Slider', value = 0 }, { idx = 'PX2S4', type = 'Slider', value = 14 }, { idx = 'PX2S5', type = 'Slider', value = 14 }, { idx = 'PX17T1', type = 'Toggle', value = false }, { idx = 'PX17S1', type = 'Slider', value = 5 }, { idx = 'PX17S2', type = 'Slider', value = 10 }, { idx = 'PX17T2', type = 'Toggle', value = false }, { idx = 'PX18T1K', type = 'KeyPicker', mode = 'Toggle', key = 'Unknown', modifiers = {} }, { idx = 'PX18S1', type = 'Slider', value = 60 }, { idx = 'PX18S2', type = 'Slider', value = 3 }, { idx = 'PX18S3', type = 'Slider', value = 1 }, { idx = 'PX18S4', type = 'Slider', value = 14 }, { idx = 'PX18S5', type = 'Slider', value = 1 }, { idx = 'P1S12T1', type = 'Toggle', value = false }, { idx = 'P1S12D1', type = 'Dropdown', value = '' }, { idx = 'P1S13T1', type = 'Toggle', value = false }, { idx = 'P1S13C1', type = 'ColorPicker', value = 'c8aa6c', transparency = 0 }, { idx = 'P1S13C2', type = 'ColorPicker', value = '5c3c0e', transparency = 0 }, { idx = 'P1S13S1', type = 'Slider', value = 0.395 }, { idx = 'P1S13S2', type = 'Slider', value = 0 }, { idx = 'P1S13S3', type = 'Slider', value = 0 }, { idx = 'P1S13S4', type = 'Slider', value = 0 }, { idx = 'P1S14T1', type = 'Toggle', value = false }, { idx = 'P1S14I1', type = 'Input', text = '' }, { idx = 'P1S14S1', type = 'Slider', value = 1 }, { idx = 'P1S28T1', type = 'Toggle', value = false }, { idx = 'P1S28D1', type = 'Dropdown', value = 'Snow' }, { idx = 'P1S28C1', type = 'ColorPicker', value = 'ffffff', transparency = 0 }, { idx = 'P1S28S1', type = 'Slider', value = 1 }, { idx = 'P1S28S2', type = 'Slider', value = 1 }, { idx = 'P1S28S3', type = 'Slider', value = 1 }, { idx = 'P1S28S4', type = 'Slider', value = 1 }, { idx = 'P1S28S5', type = 'Slider', value = 0.6 }, { idx = 'P1S28S6', type = 'Slider', value = 3 }, { idx = 'P1S28S7', type = 'Slider', value = 45 }, { idx = 'P1S28S8', type = 'Slider', value = 0 }, { idx = 'P1S28S9', type = 'Slider', value = 0 }, { idx = 'P1S29T1', type = 'Toggle', value = false }, { idx = 'P1S29C1', type = 'ColorPicker', value = 'd6e6ff', transparency = 0 }, { idx = 'P1S29S1', type = 'Slider', value = 2 }, { idx = 'P1S29S2', type = 'Slider', value = 80 }, { idx = 'P1S29S3', type = 'Slider', value = 220 }, { idx = 'P1S29S4', type = 'Slider', value = 4 }, { idx = 'P1S29S5', type = 'Slider', value = 7 }, { idx = 'P1S29S6', type = 'Slider', value = 12 }, { idx = 'P1S29S7', type = 'Slider', value = 8 }, { idx = 'P1S29T2', type = 'Toggle', value = true }, { idx = 'P1S29C2', type = 'ColorPicker', value = 'eef6ff', transparency = 0 }, { idx = 'P1S29S8', type = 'Slider', value = 18 }, { idx = 'P1S29S9', type = 'Slider', value = 2.2 }, { idx = 'P1S29S10', type = 'Slider', value = 22 }, { idx = 'P1S29S11', type = 'Slider', value = 22 }, { idx = 'P1S29S12', type = 'Slider', value = 4 }, { idx = 'P1S29T3', type = 'Toggle', value = true }, { idx = 'P1S29C3', type = 'ColorPicker', value = 'eec8ff', transparency = 0 }, { idx = 'P1S29S13', type = 'Slider', value = 0.45 }, { idx = 'P1S29S14', type = 'Slider', value = 14 }, { idx = 'P1S29T4', type = 'Toggle', value = true }, { idx = 'P1S29S15', type = 'Slider', value = 1 }, { idx = 'P1S29T5', type = 'Toggle', value = true }, { idx = 'P1S29I1', type = 'Input', text = '' }, { idx = 'P1S30T1', type = 'Toggle', value = false }, { idx = 'P1S30S1', type = 'Slider', value = 1 }, { idx = 'P1S30S2', type = 'Slider', value = 1 }, { idx = 'P1S31T1', type = 'Toggle', value = false }, { idx = 'P1S31T2', type = 'Toggle', value = false }, { idx = 'P1S31T3', type = 'Toggle', value = false }, { idx = 'P1S31T4', type = 'Toggle', value = false }, { idx = 'P1S31T5', type = 'Toggle', value = false }, { idx = 'P1S15T1', type = 'Toggle', value = false }, { idx = 'P1S15T2', type = 'Toggle', value = false }, { idx = 'P1S15D1', type = 'Dropdown', value = '' }, { idx = 'P1S15S1', type = 'Slider', value = 1 }, { idx = 'P1S15S2', type = 'Slider', value = 1 }, { idx = 'P1S15D2', type = 'Dropdown', value = '' }, { idx = 'P1S15S3', type = 'Slider', value = 1 }, { idx = 'P1S15S4', type = 'Slider', value = 1 }, { idx = 'P1S15T3', type = 'Toggle', value = false }, { idx = 'P1S15T4', type = 'Toggle', value = false }, { idx = 'P1S15C1', type = 'ColorPicker', value = 'ff0000', transparency = 0 }, { idx = 'P1S15S5', type = 'Slider', value = 0.8 }, { idx = 'P1S15D3', type = 'Dropdown', value = 'Ghost' }, { idx = 'P1S16T1', type = 'Toggle', value = false }, { idx = 'P1S16T2', type = 'Toggle', value = false }, { idx = 'P1S16D1', type = 'Dropdown', value = '' }, { idx = 'P1S16S1', type = 'Slider', value = 1 }, { idx = 'P1S16S2', type = 'Slider', value = 1 }, { idx = 'P1S16T3', type = 'Toggle', value = false }, { idx = 'P1S16C1', type = 'ColorPicker', value = 'ff0000', transparency = 0 }, { idx = 'P1S16S3', type = 'Slider', value = 0.8 }, { idx = 'PX4T1', type = 'Toggle', value = false }, { idx = 'PX4C1', type = 'ColorPicker', value = 'ffffff', transparency = 0 }, { idx = 'PX4S1', type = 'Slider', value = 0.3 }, { idx = 'PX4S2', type = 'Slider', value = 0.15 }, { idx = 'PX4T2', type = 'Toggle', value = false }, { idx = 'PX5T1', type = 'Toggle', value = false }, { idx = 'PX5C1', type = 'ColorPicker', value = 'ff3c3c', transparency = 0 }, { idx = 'PX5S1', type = 'Slider', value = 0.4 }, { idx = 'PX5S2', type = 'Slider', value = 0.3 }, { idx = 'PX5T2', type = 'Toggle', value = false }, { idx = 'P1S16D2', type = 'Dropdown', value = 'Ghost' }, { idx = 'P1S18T1', type = 'Toggle', value = false }, { idx = 'P1S18C1', type = 'ColorPicker', value = '78dcff', transparency = 0 }, { idx = 'P1S18D1', type = 'Dropdown', value = 'Beam' }, { idx = 'P1S18S1', type = 'Slider', value = 0.06 }, { idx = 'P1S18S2', type = 'Slider', value = 4 }, { idx = 'P1S18S3', type = 'Slider', value = 1 }, { idx = 'P1S18S4', type = 'Slider', value = 1 }, { idx = 'P1S18S5', type = 'Slider', value = 1 }, { idx = 'P1S18S6', type = 'Slider', value = 0.6 }, { idx = 'P1S18S7', type = 'Slider', value = 0.35 }, { idx = 'P1S18T2', type = 'Toggle', value = true }, { idx = 'P1S18S8', type = 'Slider', value = 18 }, { idx = 'P1S18S9', type = 'Slider', value = 0.7 }, { idx = 'P1S19T1', type = 'Toggle', value = false }, { idx = 'P1S19D1', type = 'Dropdown', value = 'Ghost' }, { idx = 'P1S19C1', type = 'ColorPicker', value = 'ffffff', transparency = 0 }, { idx = 'P1S19S1', type = 'Slider', value = 0 }, { idx = 'P1S19T2', type = 'Toggle', value = false }, { idx = 'P1S20T1', type = 'Toggle', value = false }, { idx = 'P1S20D1', type = 'Dropdown', value = 'Ghost' }, { idx = 'P1S20C1', type = 'ColorPicker', value = 'ffffff', transparency = 0 }, { idx = 'P1S20S1', type = 'Slider', value = 0 }, { idx = 'P1S20T2', type = 'Toggle', value = true }, { idx = 'P1S21T1', type = 'Toggle', value = false }, { idx = 'P1S21D1', type = 'Dropdown', value = 'Ghost' }, { idx = 'P1S21C1', type = 'ColorPicker', value = 'ffffff', transparency = 0 }, { idx = 'P1S21S1', type = 'Slider', value = 0 }, { idx = 'P1S21T2', type = 'Toggle', value = false }, { idx = 'P1S22T1', type = 'Toggle', value = false }, { idx = 'P1S22C1', type = 'ColorPicker', value = 'ffffff', transparency = 0 }, { idx = 'P1S22S1', type = 'Slider', value = 0.5 }, { idx = 'P1S22C2', type = 'ColorPicker', value = 'ffffff', transparency = 0 }, { idx = 'P1S22S2', type = 'Slider', value = 0 }, { idx = 'P1S23T1', type = 'Toggle', value = false }, { idx = 'P1S23C1', type = 'ColorPicker', value = 'ffffff', transparency = 0 }, { idx = 'P1S23S1', type = 'Slider', value = 0.5 }, { idx = 'P1S23C2', type = 'ColorPicker', value = 'ffffff', transparency = 0 }, { idx = 'P1S23S2', type = 'Slider', value = 0 }, { idx = 'P1S26T1', type = 'Toggle', value = false }, { idx = 'P1S26D1', type = 'Dropdown', value = 'Lines' }, { idx = 'P1S26S1', type = 'Slider', value = 12 }, { idx = 'P1S26S2', type = 'Slider', value = 2 }, { idx = 'P1S26S3', type = 'Slider', value = 6 }, { idx = 'P1S26T2', type = 'Toggle', value = false }, { idx = 'P1S26C1', type = 'ColorPicker', value = '000000', transparency = 0 }, { idx = 'P1S26S11', type = 'Slider', value = 1 }, { idx = 'P1S26T3', type = 'Toggle', value = true }, { idx = 'P1S26C2', type = 'ColorPicker', value = 'ffffff', transparency = 0 }, { idx = 'P1S26T4', type = 'Toggle', value = true }, { idx = 'P1S26C3', type = 'ColorPicker', value = 'ffffff', transparency = 0 }, { idx = 'P1S26T5', type = 'Toggle', value = true }, { idx = 'P1S26C4', type = 'ColorPicker', value = 'ffffff', transparency = 0 }, { idx = 'P1S26T6', type = 'Toggle', value = true }, { idx = 'P1S26C5', type = 'ColorPicker', value = 'ffffff', transparency = 0 }, { idx = 'P1S26T7', type = 'Toggle', value = false }, { idx = 'P1S26S8', type = 'Slider', value = 4 }, { idx = 'P1S26S9', type = 'Slider', value = 16 }, { idx = 'P1S26S10', type = 'Slider', value = 3 }, { idx = 'P1S26T8', type = 'Toggle', value = false }, { idx = 'P1S26S4', type = 'Slider', value = 0 }, { idx = 'P1S26S5', type = 'Slider', value = 90 }, { idx = 'P1S26I1', type = 'Input', text = '' }, { idx = 'P1S26S6', type = 'Slider', value = 32 }, { idx = 'P1S26S7', type = 'Slider', value = 0 }, { idx = 'P1S26T9', type = 'Toggle', value = false }, { idx = 'P1S26D2', type = 'Dropdown', value = 'Crosshair' }, { idx = 'P1S26T10', type = 'Toggle', value = false }, { idx = 'P1S26D3', type = 'Dropdown', value = 'Shimmer' }, { idx = 'P1S26S12', type = 'Slider', value = 1 }, { idx = 'P1S26C6', type = 'ColorPicker', value = '282828', transparency = 0 }, { idx = 'P1S26C7', type = 'ColorPicker', value = 'ff0000', transparency = 0 }, { idx = 'P1S26C8', type = 'ColorPicker', value = '0000ff', transparency = 0 }, { idx = 'P1S26C9', type = 'ColorPicker', value = '000000', transparency = 0 }, { idx = 'P1S26C10', type = 'ColorPicker', value = 'ffffff', transparency = 0 }, { idx = 'P1S26S13', type = 'Slider', value = 0 }, { idx = 'P1S26T11', type = 'Toggle', value = false }, { idx = 'P1S26I2', type = 'Input', text = '' }, { idx = 'P1S26D4', type = 'Dropdown', value = 'Inconsolata' }, { idx = 'P1S26S14', type = 'Slider', value = 14 }, { idx = 'P1S26S15', type = 'Slider', value = 24 }, { idx = 'P1S26C11', type = 'ColorPicker', value = 'ffffff', transparency = 0 }, { idx = 'P1S26T12', type = 'Toggle', value = false }, { idx = 'P1S26C12', type = 'ColorPicker', value = '000000', transparency = 0 }, { idx = 'P1S26S16', type = 'Slider', value = 1 }, { idx = 'P1S26T13', type = 'Toggle', value = false }, { idx = 'P1S26D5', type = 'Dropdown', value = 'Shimmer' }, { idx = 'P1S26S17', type = 'Slider', value = 1 }, { idx = 'P1S26C13', type = 'ColorPicker', value = '282828', transparency = 0 }, { idx = 'P1S26C14', type = 'ColorPicker', value = '000000', transparency = 0 }, { idx = 'P1S26C15', type = 'ColorPicker', value = 'ffffff', transparency = 0 }, { idx = 'P1S26S18', type = 'Slider', value = 0 }, { idx = 'P1S27T1', type = 'Toggle', value = false }, { idx = 'P1S27T2', type = 'Toggle', value = false }, { idx = 'P1S27T3', type = 'Toggle', value = false }, { idx = 'P1S27T4', type = 'Toggle', value = false }, { idx = 'P1S27T5', type = 'Toggle', value = false }, { idx = 'P1S27T6', type = 'Toggle', value = false }, { idx = 'P1S27T7', type = 'Toggle', value = false }, { idx = 'P1S27T8', type = 'Toggle', value = false }, { idx = 'P2S1T1', type = 'Toggle', value = true }, { idx = 'P2S1T1K', type = 'KeyPicker', mode = 'Always', key = 'Unknown', modifiers = {} }, { idx = 'P2S1D1', type = 'Dropdown', value = 'Silent' }, { idx = 'P2S1S13', type = 'Slider', value = 100 }, { idx = 'P2S1T3', type = 'Toggle', value = true }, { idx = 'P2S1T5', type = 'Toggle', value = true }, { idx = 'P2S1D2', type = 'Dropdown', value = 'Auto' }, { idx = 'P2S1T12', type = 'Toggle', value = false }, { idx = 'P2S1T12K', type = 'KeyPicker', mode = 'Hold', key = 'Unknown', modifiers = {} }, { idx = 'P2S1T13', type = 'Toggle', value = false }, { idx = 'P2S1S8', type = 'Slider', value = 0 }, { idx = 'P2S1S9', type = 'Slider', value = 250 }, { idx = 'P2S1S10', type = 'Slider', value = 110 }, { idx = 'P2S1S11', type = 'Slider', value = 12 }, { idx = 'P2S1S12', type = 'Slider', value = 30 }, { idx = 'P2S1S2', type = 'Slider', value = 282 }, { idx = 'P2S1T7K', type = 'KeyPicker', mode = 'Always', key = 'Unknown', modifiers = {} }, { idx = 'P2S1S7', type = 'Slider', value = 0 }, { idx = 'P2S1T8', type = 'Toggle', value = true }, { idx = 'P2S1T9', type = 'Toggle', value = true }, { idx = 'P2S1D4', type = 'Dropdown', multi = true, value = { ['Flare Gun'] = true, ['Freeze Ray'] = true, ['Grenade Launcher'] = true, RPG = true, }, }, { idx = 'P2S2S1', type = 'Slider', value = 35 }, { idx = 'PX7D1', type = 'Dropdown', value = 'Smooth' }, { idx = 'PX19T1', type = 'Toggle', value = true }, { idx = 'KE_Enabled', type = 'Toggle', value = false }, { idx = 'KE_Range', type = 'Slider', value = 3 }, { idx = 'KE_Scope', type = 'Dropdown', value = 'Knife' }, { idx = 'KE_Heavy', type = 'Toggle', value = true }, { idx = 'PX12T1', type = 'Toggle', value = false }, { idx = 'PX12T2', type = 'Toggle', value = false }, { idx = 'PX12C1', type = 'ColorPicker', value = 'ffffff', transparency = 0 }, { idx = 'PX12S1', type = 'Slider', value = 0.02 }, { idx = 'PX12S2', type = 'Slider', value = 0 }, { idx = 'PX11T1', type = 'Toggle', value = false }, { idx = 'PX11S1', type = 'Slider', value = 3 }, { idx = 'PX11S2', type = 'Slider', value = 1.5 }, { idx = 'PX11S3', type = 'Slider', value = 7.5 }, { idx = 'PX10T1', type = 'Toggle', value = false }, { idx = 'PX10T2', type = 'Toggle', value = true }, { idx = 'PX10T3', type = 'Toggle', value = true }, { idx = 'PX10S1', type = 'Slider', value = 120 }, { idx = 'PX9T1', type = 'Toggle', value = false }, { idx = 'PX9D1', type = 'Dropdown', value = 'Monospace' }, { idx = 'PX9S1', type = 'Slider', value = 16 }, { idx = 'PX9D2', type = 'Dropdown', value = 'Normal' }, { idx = 'PX9D3', type = 'Dropdown', value = 'None' }, { idx = 'PX9T2', type = 'Toggle', value = false }, { idx = 'PX9S2', type = 'Slider', value = 60 }, { idx = 'PX6S1', type = 'Slider', value = 0 }, { idx = 'PX6S2', type = 'Slider', value = 0 }, { idx = 'PX6T1', type = 'Toggle', value = false }, { idx = 'PX6D1', type = 'Dropdown', value = 'While Aiming' }, { idx = 'PX6D2', type = 'Dropdown', value = 'Crosshair' }, { idx = 'PX14T1', type = 'Toggle', value = false }, { idx = 'PX14T1K', type = 'KeyPicker', mode = 'Always', key = 'Unknown', modifiers = {} }, { idx = 'PX14T2', type = 'Toggle', value = true }, { idx = 'PX14T3', type = 'Toggle', value = true }, { idx = 'PX14D2', type = 'Dropdown', value = 'Reload' }, { idx = 'PX14D3', type = 'Dropdown', value = 'Strafe' }, { idx = 'PX14S1', type = 'Slider', value = 0.45 }, { idx = 'PX14S2', type = 'Slider', value = 30 }, { idx = 'PX8T1', type = 'Toggle', value = false }, { idx = 'PX8C1', type = 'ColorPicker', value = 'ff4646', transparency = 0 }, { idx = 'PX8S1', type = 'Slider', value = 18 }, { idx = 'PX8S2', type = 'Slider', value = 2 }, { idx = 'PX8T2', type = 'Toggle', value = false }, { idx = 'PX8T3', type = 'Toggle', value = false }, { idx = 'PX8S3', type = 'Slider', value = 3 }, { idx = 'P4S1T1', type = 'Toggle', value = true }, { idx = 'P4S1S4', type = 'Slider', value = 85 }, { idx = 'P4S1T2', type = 'Toggle', value = true }, { idx = 'P4S1S3', type = 'Slider', value = 20 }, { idx = 'P4S1T3', type = 'Toggle', value = false }, { idx = 'P4S1T4', type = 'Toggle', value = false }, { idx = 'P4S1S2', type = 'Slider', value = 100 }, { idx = 'P4S1T5', type = 'Toggle', value = false }, { idx = 'P4S1S5', type = 'Slider', value = 75 }, { idx = 'P4S1T6', type = 'Toggle', value = false }, { idx = 'P4S1D1', type = 'Dropdown', value = 'Impact' }, { idx = 'P4S1T7', type = 'Toggle', value = false }, { idx = 'P4S2T2', type = 'Toggle', value = true }, { idx = 'P4S2S2', type = 'Slider', value = 85 }, { idx = 'P4S2T3', type = 'Toggle', value = true }, { idx = 'P4S2S3', type = 'Slider', value = 80 }, { idx = 'P4S2T4', type = 'Toggle', value = false }, { idx = 'P4S2T5', type = 'Toggle', value = false }, { idx = 'P4S2T6', type = 'Toggle', value = false }, { idx = 'P4S2T8', type = 'Toggle', value = false }, { idx = 'P4S2T9', type = 'Toggle', value = false }, { idx = 'P4S2S4', type = 'Slider', value = 80 }, { idx = 'P4S2T10', type = 'Toggle', value = false }, { idx = 'P4S2S5', type = 'Slider', value = 0 }, { idx = 'P4S2S6', type = 'Slider', value = 0 }, { idx = 'P4S2S7', type = 'Slider', value = 0 }, { idx = 'PX3S1', type = 'Slider', value = 0 }, { idx = 'PX3S2', type = 'Slider', value = 0 }, { idx = 'PX3S3', type = 'Slider', value = 0 }, { idx = 'P8S1T1', type = 'Toggle', value = true }, { idx = 'P8S1T2', type = 'Toggle', value = true }, { idx = 'P8S8T1', type = 'Toggle', value = false }, { idx = 'P8S8T2', type = 'Toggle', value = false }, { idx = 'P8S4T1K', type = 'KeyPicker', mode = 'Always', key = 'Unknown', modifiers = {} }, { idx = 'P8S5T3', type = 'Toggle', value = false }, { idx = 'P8S5D7', type = 'Dropdown', value = RivalsRuntimeBridge.ResolveAutoLoadoutMapValues()[1] }, { idx = 'P8S5T4', type = 'Toggle', value = false }, { idx = 'P8S5D8', type = 'Dropdown', value = '1' }, { idx = 'P8S5D8KC', type = 'KeyPicker', mode = 'Press', key = 'Unknown', modifiers = {} }, { idx = 'P8S5T6', type = 'Toggle', value = true }, { idx = 'P8S5T7', type = 'Toggle', value = true }, { idx = 'P8S5N', type = 'Slider', value = 3 }, { idx = 'P8S5D1', type = 'Dropdown', value = RivalsRuntimeBridge.ResolveAutoLoadoutDefaultWeapon('Primary') }, { idx = 'P8S5D2', type = 'Dropdown', value = RivalsRuntimeBridge.ResolveAutoLoadoutDefaultWeapon('Secondary') }, { idx = 'P8S5D3', type = 'Dropdown', value = RivalsRuntimeBridge.ResolveAutoLoadoutDefaultWeapon('Melee') }, { idx = 'P8S5D4', type = 'Dropdown', value = RivalsRuntimeBridge.ResolveAutoLoadoutDefaultWeapon('Utility') }, { idx = 'P8S5T2', type = 'Toggle', value = false }, { idx = 'P8S5D5', type = 'Dropdown', multi = true, value = {} }, { idx = 'P8S5D6', type = 'Dropdown', multi = true, value = {} }, { idx = 'P8S5T5', type = 'Toggle', value = false }, { idx = 'P8S5D9', type = 'Dropdown', multi = true, value = {} }, { idx = 'P8S6D1', type = 'Dropdown', value = '1v1' }, { idx = 'P10S3T1', type = 'Toggle', value = false }, { idx = 'P10S3T1K', type = 'KeyPicker', mode = 'Always', key = 'Unknown', modifiers = {} }, { idx = 'P10S3S1', type = 'Slider', value = 2 }, { idx = 'P10S3T2', type = 'Toggle', value = false }, { idx = 'P10S3T2K', type = 'KeyPicker', mode = 'Always', key = 'Unknown', modifiers = {} }, { idx = 'P10S3S2', type = 'Slider', value = 10 }, { idx = 'P10S3T3', type = 'Toggle', value = false }, { idx = 'P10S3S3', type = 'Slider', value = 2 }, { idx = 'P10S3T4', type = 'Toggle', value = false }, { idx = 'P10S3T4K', type = 'KeyPicker', mode = 'Always', key = 'Unknown', modifiers = {} }, { idx = 'P10S3S4', type = 'Slider', value = 100 }, { idx = 'P10S3T5', type = 'Toggle', value = false }, { idx = 'P10S3T5K', type = 'KeyPicker', mode = 'Always', key = 'Unknown', modifiers = {} }, { idx = 'P10S3T6', type = 'Toggle', value = false }, { idx = 'P10S3T6K', type = 'KeyPicker', mode = 'Always', key = 'Unknown', modifiers = {} }, { idx = 'P10S3S5', type = 'Slider', value = 50 }, { idx = 'P10S3T7', type = 'Toggle', value = false }, { idx = 'P10S3T7K', type = 'KeyPicker', mode = 'Hold', key = 'Unknown', modifiers = {} }, { idx = 'P10S3D1', type = 'Dropdown', value = 'Under Feet' }, { idx = 'P10S3S6', type = 'Slider', value = 60 }, { idx = 'P10S3S7', type = 'Slider', value = 20 }, { idx = 'P10S3S8', type = 'Slider', value = 8 }, { idx = 'P10S4T1', type = 'Toggle', value = false }, { idx = 'P10S4T2', type = 'Toggle', value = false }, { idx = 'P10S4T3', type = 'Toggle', value = false }, { idx = 'P10S4S1', type = 'Slider', value = 3 }, { idx = 'P10S4S2', type = 'Slider', value = 50 }, { idx = 'P10S4S3', type = 'Slider', value = 180 }, { idx = 'P10S4S4', type = 'Slider', value = 35 }, { idx = 'P10S4S5', type = 'Slider', value = 0.2 }, { idx = 'P10S4T4', type = 'Toggle', value = false }, { idx = 'P10S4T4K', type = 'KeyPicker', mode = 'Toggle', key = 'Unknown', modifiers = {} }, { idx = 'P10S4T5', type = 'Toggle', value = false }, { idx = 'P10S4T5K', type = 'KeyPicker', mode = 'Toggle', key = 'Unknown', modifiers = {} }, { idx = 'P10S4I1', type = 'Input', text = '' }, { idx = 'P10S6T1', type = 'Toggle', value = false }, { idx = 'P10S6I1', type = 'Input', text = '' }, { idx = 'P10S6S1', type = 'Slider', value = 1 }, { idx = 'P10S6S2', type = 'Slider', value = 0 }, { idx = 'P10S6S3', type = 'Slider', value = 100 }, { idx = 'P10S7T1', type = 'Toggle', value = false }, { idx = 'P10S7D1', type = 'Dropdown', value = 'VR' }, { idx = 'P10S2T1', type = 'Toggle', value = false }, { idx = 'P10S2D1', type = 'Dropdown', multi = true, value = {} }, { idx = 'P10S2D2', type = 'Dropdown', multi = true, value = {} }, { idx = 'P10S2C1', type = 'ColorPicker', value = 'faaa46', transparency = 0 }, { idx = 'P5S1T2', type = 'Toggle', value = false }, { idx = 'P5S1T4', type = 'Toggle', value = false }, { idx = 'EventZombieIncludeInAimbot', type = 'Toggle', value = false }, { idx = 'EventZombieAimPart', type = 'Dropdown', value = 'Auto' }, } for _, object in ipairs((RivalsRuntimeBridge.ZHub and RivalsRuntimeBridge.ZHub.ProfileDefaults) or {}) do SharedConfigDefaultObjects[#SharedConfigDefaultObjects + 1] = object end for _, object in ipairs((RivalsRuntimeBridge.RankCharm and RivalsRuntimeBridge.RankCharm.ProfileDefaults) or {}) do SharedConfigDefaultObjects[#SharedConfigDefaultObjects + 1] = object end local ConfigProfileDefaults = { Legit = { { idx = 'P2S1T7', type = 'Toggle', value = false }, { idx = 'KR_Enabled', type = 'Toggle', value = false }, { idx = 'P8S4T1', type = 'Toggle', value = false }, { idx = 'P8S4T2', type = 'Toggle', value = false }, { idx = 'P8S5T1', type = 'Toggle', value = false }, { idx = 'P8S6T1', type = 'Toggle', value = false }, { idx = 'P4S1T3', type = 'Toggle', value = false }, { idx = 'P4S1T4', type = 'Toggle', value = false }, }, Rage = { { idx = 'P2S1T7', type = 'Toggle', value = true }, { idx = 'KR_Enabled', type = 'Toggle', value = true }, { idx = 'P8S4T1', type = 'Toggle', value = false }, { idx = 'P8S4T2', type = 'Toggle', value = true }, { idx = 'P8S5T1', type = 'Toggle', value = true }, { idx = 'P8S6T1', type = 'Toggle', value = true }, { idx = 'P4S1T3', type = 'Toggle', value = true }, { idx = 'P4S1T4', type = 'Toggle', value = true }, }, } local function CloneRivalsConfigProfileValue(value) if type(value) ~= 'table' then return value end local copy = {} for key, child in pairs(value) do copy[key] = CloneRivalsConfigProfileValue(child) end return copy end local function CloneRivalsConfigProfileObject(object) local copy = {} for key, value in pairs(object) do copy[key] = CloneRivalsConfigProfileValue(value) end return copy end local function BuildRivalsConfigProfileDefaultObjects(config_name) local profile_defaults = ConfigProfileDefaults[config_name] if type(profile_defaults) ~= 'table' then return nil end local objects = {} local indexes_by_id = {} local function upsert(object) if type(object) ~= 'table' or type(object.idx) ~= 'string' then return end local cloned = CloneRivalsConfigProfileObject(object) local existing_index = indexes_by_id[cloned.idx] if existing_index then objects[existing_index] = cloned else indexes_by_id[cloned.idx] = #objects + 1 objects[#objects + 1] = cloned end end for _, object in ipairs(SharedConfigDefaultObjects) do upsert(object) end for _, object in ipairs(profile_defaults) do upsert(object) end return objects end local function WriteRivalsConfigProfileDefault(config_name) local objects = BuildRivalsConfigProfileDefaultObjects(config_name) if not objects then return false, 'unknown RIVALS config profile: ' .. tostring(config_name) end local settings_folder = ScriptPaths.storage_root() .. '/settings' if not isfolder(settings_folder) then makefolder(settings_folder) end local ok, err = pcall( function() writefile(ScriptPaths.config_path(config_name), HttpService:JSONEncode({ objects = objects })) end ) if not ok then return false, err end return true end RivalsConfigProfiles.WriteDefault = WriteRivalsConfigProfileDefault SaveManager:SetIgnoreIndexes({ 'P1S32D1', 'P1S26D6', 'P1S14D1', 'PX18T1' }) local SharedUI = GameBootstrap.create_standard_shared_ui({ ui_settings = UISettings, window = Window, library = Library, theme_manager = ThemeManager, script_paths = ScriptPaths, save_manager = SaveManager, current_config_name = 'RIVALS', default_config_name = 'RIVALS Default', config_names = RivalsConfigProfiles.Names, use_global_config_names = true, toggles = Toggles, options = Options, local_player = LP, defaults = { auto_show = true, show_notification = true, show_keybind_states = true, auto_execute_on_teleport = true, auto_load_config = true, auto_save_config = true, load_config_name = 'Legit', menu_keybind = 'RightControl', }, persist_menu_keybind = true, queue_on_teleport = queue_on_teleport, before_save = function(shared_ui, config_name) local saveName = config_name or (shared_ui and shared_ui:resolve_save_config_name()) or 'Legit' RivalsRuntimeBridge.SetRivalsCosmeticsConfigName(saveName) RivalsRuntimeBridge.SetAutoLoadoutConfigName(saveName) RivalsRuntimeBridge.CaptureSavedRivalsCosmeticsSelections() end, on_save = function(shared_ui, config_name) local saveName = config_name or (shared_ui and shared_ui:resolve_save_config_name()) or 'Legit' RivalsRuntimeBridge.SetRivalsCosmeticsConfigName(saveName) RivalsRuntimeBridge.SetAutoLoadoutConfigName(saveName) RivalsRuntimeBridge.SaveRivalsCosmeticsSelections() RivalsRuntimeBridge.SaveAutoLoadoutProfiles() end, on_load_saved = function(shared_ui, config_name) local loadName = config_name or (shared_ui and shared_ui:resolve_load_config_name()) or 'Legit' RivalsRuntimeBridge.SetRivalsCosmeticsConfigName(loadName) RivalsRuntimeBridge.SetAutoLoadoutConfigName(loadName) task.defer(GuardRivalsCallback('Cosmetics_LoadSavedSelections', function() RivalsRuntimeBridge.LoadSavedRivalsCosmeticsSelections() RivalsRuntimeBridge.RefreshRivalsCosmetics() RivalsRuntimeBridge.ApplyRivalsCosmetics() end)) task.defer(GuardRivalsCallback('AutoLoadout_LoadSavedProfiles', RivalsRuntimeBridge.LoadAutoLoadoutProfiles)) end, on_load_default = function(shared_ui, config_name) local loadName = config_name or (shared_ui and shared_ui:resolve_save_config_name()) or 'Legit' RivalsRuntimeBridge.SetRivalsCosmeticsConfigName(loadName) RivalsRuntimeBridge.SetAutoLoadoutConfigName(loadName) task.defer(GuardRivalsCallback('Cosmetics_LoadDefaultSelections', function() RivalsRuntimeBridge.LoadSavedRivalsCosmeticsSelections() RivalsRuntimeBridge.RefreshRivalsCosmetics() RivalsRuntimeBridge.ApplyRivalsCosmetics() end)) task.defer(GuardRivalsCallback('AutoLoadout_LoadDefaultProfiles', RivalsRuntimeBridge.LoadAutoLoadoutProfiles)) end, reset_config_to_default = function(shared_ui, config_name) return RivalsConfigProfiles.WriteDefault(config_name) end, reconcile_loaded_state = function() if ReconcileLoadedState then ReconcileLoadedState() end end, }) RivalsRuntimeBridge.IsConfigLoading = function() return SharedUI ~= nil and SharedUI.is_config_loading ~= nil and SharedUI:is_config_loading() == true end __ZkhHub_genv.ZkxLoad(0.90, 'Loading config') RivalsRuntimeBridge.Extras.Reconcile = function() if ReconcileLoadedState then ReconcileLoadedState() end end RivalsRuntimeBridge.Extras.BuildConfigUi(Library.Tabs and Library.Tabs['UI Settings']) local initialRivalsConfigName = (SharedUI:should_auto_load_config() and SharedUI:resolve_load_config_name()) or 'RIVALS Default' RivalsRuntimeBridge.SetRivalsCosmeticsConfigName(initialRivalsConfigName) RivalsRuntimeBridge.SetAutoLoadoutConfigName(initialRivalsConfigName) ReconcileLoadedState = function() if RivalsRuntimeBridge.RagebotExclusive then RivalsRuntimeBridge.RagebotExclusive.Reconcile() end if RivalsRuntimeBridge.ZHub then RivalsRuntimeBridge.ZHub.PushAll() RivalsRuntimeBridge.ZHubRagebotSync() end if RivalsRuntimeBridge.RankCharm then RivalsRuntimeBridge.RankCharm.PushAll() end RefreshEspRenderSettings() RivalsModsState.EnsureHooks() RivalsRuntimeBridge.WorldVisuals.RefreshAll() RivalsRuntimeBridge.ViewmodelVisuals.RefreshAll() RivalsRuntimeBridge.NativeRemovals.RefreshAll() RivalsRuntimeBridge.Movement.RefreshAll() RivalsRuntimeBridge.DeviceSpoof.RefreshAll() RivalsRuntimeBridge.PlayerSpoofer.RefreshAll() RivalsRuntimeBridge.AnimationPlayer.RefreshAll() RivalsRuntimeBridge.RefreshRivalsCosmetics() RivalsRuntimeBridge.ApplyRivalsCosmetics() end GameBootstrap.prepare_setup({ save_manager = SaveManager, script_paths = ScriptPaths, current_config_name = 'RIVALS', default_config_name = 'RIVALS Default', shared_ui = SharedUI, toggles = Toggles, options = Options, reset_config_to_default = function(shared_ui, config_name) return RivalsConfigProfiles.WriteDefault(config_name) end, }) __ZkhHub_genv.ZkxLoad(0.93, 'Applying cosmetics') RivalsRuntimeBridge.LoadSavedRivalsCosmeticsSelections() task.defer(GuardRivalsCallback('Cosmetics_LoadCache', function() if type(RivalsRuntimeBridge.LoadCosmeticsCache) == 'function' then if pcall(RivalsRuntimeBridge.LoadCosmeticsCache) then RivalsRuntimeBridge.RefreshRivalsCosmetics() RivalsRuntimeBridge.ApplyRivalsCosmetics() end end end)) task.defer(GuardRivalsCallback('AutoLoadout_LoadInitialProfiles', RivalsRuntimeBridge.LoadAutoLoadoutProfiles)) EnsureEspRenderSettingsTracking() RefreshEspRenderSettings() RivalsRuntimeBridge.WorldVisuals.RefreshAll() RivalsRuntimeBridge.ViewmodelVisuals.RefreshAll() RivalsRuntimeBridge.NativeRemovals.RefreshAll() RivalsRuntimeBridge.Movement.RefreshAll() RivalsRuntimeBridge.DeviceSpoof.RefreshAll() RivalsRuntimeBridge.PlayerSpoofer.RefreshAll() RivalsRuntimeBridge.AnimationPlayer.RefreshAll() if RivalsRuntimeBridge.ZHub then RivalsRuntimeBridge.ZHub.PushAll() end if RivalsRuntimeBridge.RankCharm then RivalsRuntimeBridge.RankCharm.PushAll() end -- Lifecycle Library:OnUnload(GuardRivalsCallback('Runtime_Unload', function() RivalsRuntime.ResetState() Connections:disconnect_all() end)) __ZkhHub_genv.ZkxLoad(0.95, 'Finalizing') RivalsRuntime.WaitForInitialLoadInSettle() __ZkhHub_genv.ZkxLoad(0.97, 'Starting systems') RivalsRuntime.RegisterPlayerLifecycle() RivalsRuntime.RegisterWorldLifecycle() RivalsRuntime.RegisterInputLifecycle() RivalsRuntime.RegisterCharacterLifecycle() RivalsRuntime.StartLoops() RivalsRuntimeBridge.EnsureMatchBoundaryDetection() RivalsRuntimeBridge.BindRivalsCosmetics() RivalsRuntimeBridge.BindAutoLoadoutControllers() RivalsRuntimeBridge.ApplyRivalsCosmetics() RivalsRuntimeBridge.QueueAutoLoadoutSubmission() __ZkhHub_genv.ZkxLoad(0.99, 'Almost ready') SharedUI:notify_loaded(5) task.spawn(function() local splash = __ZkhHub_genv.__ZkxSplash if splash then __ZkhHub_genv.__ZkxSplash = nil pcall(splash.Done) pcall(splash.Wait, 4) end task.wait(0.35) local wanted = true pcall(function() wanted = RequireSharedModule('script_paths').load_ui_settings().auto_show ~= false end) if wanted and Library and not Library.Unloaded and not Library.Toggled then __ZkxHubElevateIdentity() pcall(Library.Toggle, Library, true) end end) task.defer(GuardRivalsCallback('AutoLoadConfig_Deterministic', function() task.wait() if not (SharedUI and type(SharedUI.should_auto_load_config) == 'function') then return end local okDecide, wants = pcall(function() return SharedUI:should_auto_load_config() end) if not okDecide or not wants then return end local configName pcall(function() configName = SharedUI:resolve_load_config_name() end) if type(configName) ~= 'string' or configName == '' then Library:Notify({ Title = 'Auto Load', Description = 'Auto Load is ON but no config is selected.', Time = 5 }) return end local ok, err if type(SharedUI.with_config_load_suspended) == 'function' then ok, err = SharedUI:with_config_load_suspended(function() return SaveManager:Load(configName) end) else ok, err = SaveManager:Load(configName) end if ok then if type(ReconcileLoadedState) == 'function' then task.defer(function() pcall(ReconcileLoadedState) end) end else Library:Notify({ Title = 'Auto Load', Description = string.format('Auto Load could not load %q: %s', configName, tostring(err)), Time = 7, }) end end)) end __ZkhHub_genv.ZkxLoad(nil, 'Waiting for the game to finish loading') if GuardRivalsCallback('EnumLibrary_StartupBarrier', function() local enumLibrary = ResolveEnumLibrary() if not enumLibrary then error('EnumLibrary is unavailable') end if enumLibrary._enum_builder_complete ~= true then local builderDeadline = os.clock() + 30 while os.clock() < builderDeadline and enumLibrary._enum_builder_complete ~= true do task.wait(0.1) end end if enumLibrary._enum_builder_complete ~= true then local fromEnum = rawget(enumLibrary, '_from_enum') if type(fromEnum) ~= 'table' or next(fromEnum) == nil then local executorName = 'this executor' if type(identifyexecutor) == 'function' then local ok, name = pcall(identifyexecutor) if ok and type(name) == 'string' and name ~= '' then executorName = name end end TryShowRivalsRuntimeIssueNotification("RIVALS isn't supported on " .. executorName .. '.') return false end error('EnumLibrary builder did not complete') end if type(enumLibrary.WaitForEnumBuilder) == 'function' then enumLibrary:WaitForEnumBuilder() end return true end)() ~= true then return end __ZkhHub_genv.ZkxLoad(0.40, 'Building ESP') BuildRivalsUi(Window) RivalsRuntime.Initialize(Window) end)() else if Library and type(Library.Notify) == 'function' then Library:Notify({ Title = 'ZkxHub', Description = 'This script only supports RIVALS.', Time = 5 }) end end end, function(e) return debug.traceback(tostring(e), 2) end) if not __game_ok then task.spawn(function() local splash = __ZkhHub_genv.__ZkxSplash if splash then __ZkhHub_genv.__ZkxSplash = nil pcall(splash.Done) pcall(splash.Wait, 4) end task.wait(0.35) local wanted = true pcall(function() wanted = RequireSharedModule('script_paths').load_ui_settings().auto_show ~= false end) if wanted and Library and not Library.Unloaded and not Library.Toggled then __ZkxHubElevateIdentity() pcall(Library.Toggle, Library, true) end end) if ErrorReporter and type(ErrorReporter.report) == 'function' then ErrorReporter.report(__game_err, nil, 'game_init') end if Library and type(Library.Notify) == 'function' then Library:Notify({ Title = 'ZkxHub', Description = 'Fatal error: ' .. tostring(__game_err), Time = 8 }) end end end)()