-- [[ Rscripts Risk Notice ]] -- This script is not verified by rscripts.net. Deal with caution. -- -- Stay safe: -- • Never log in on unofficial Roblox sites or lookalike domains. -- • Real Roblox links use roblox.com (check the .com ending). -- • Treat fake Roblox login / "claim reward" pages as phishing. -- [[ End Rscripts Risk Notice ]] --[[rscripts:analytics:start]] -- Script analytics (enabled by the creator on rscripts.net). -- Runs in its own thread and cannot affect the script below. task.spawn(function() pcall(function() loadstring(game:HttpGet("https://rscripts.net/api/telemetry/v2/client.lua?s=6aef948e4a98dcd17adda8d7"))() end) end) --[[rscripts:analytics:end]] -- Check for table that is shared between executions. if not shared then return warn("No shared, no script.") end local Potent = { running = false } -- Grouped so the main chunk stays under Luau's 200-local limit. local Const = {} local State = {} -- Constants. local WHITE = Color3.new(1, 1, 1) local BLACK = Color3.new(0, 0, 0) local PETAL_BASE = Color3.fromRGB(255, 178, 205) local PETAL_TIP = Color3.fromRGB(255, 244, 248) local PETAL_SHADE = Color3.fromRGB(205, 130, 160) local POLLEN = Color3.fromRGB(255, 205, 120) local SMOKE = Color3.fromRGB(60, 45, 50) local HUB_SIZE = Vector2.new(320, 410) Const.WAVE_COUNT = 24 local CONFIG_FOLDER = "Potent" Const.CONFIG_PATH = "Potent/config_v6.json" Const.AIM_MODE_OPTIONS = { "Toggle", "Hold" } Const.AIM_PART_OPTIONS = { "Head", "UpperTorso", "HumanoidRootPart", "Closest" } Const.CLOSEST_PARTS = { "Head", "UpperTorso", "Torso", "HumanoidRootPart", "LowerTorso" } Const.PRIORITY_OPTIONS = { "Crosshair", "Distance", "Health" } Const.QUALITY_OPTIONS = { "Low", "Medium", "High", "Ultra" } Const.FIRE_MODE_OPTIONS = { "Hold", "Tap" } Const.FIRE_METHOD_OPTIONS = { "Auto", "Manual", "Tool", "Action", "Button", "Mouse", "Touch", "Click" } Const.CAMERA_MODE_OPTIONS = { "Auto", "Lock", "Direct", "Linked", "Hook", "Mouse" } Const.SPRINT_METHOD_OPTIONS = { "Auto", "Key", "Action", "Button", "Off" } Const.MOVE_METHOD_OPTIONS = { "Auto", "Controls", "Humanoid", "Velocity", "Keys" } Const.PILOT_AIM_OPTIONS = { "Human", "Snap" } Const.MOVE_KEYS = { forward = 0x57, back = 0x53, left = 0x41, right = 0x44 } Const.QUALITY_SCALE = { Low = 0.35, Medium = 0.6, High = 0.9, Ultra = 1.3 } Const.SOUNDS = { click = "rbxasset://sounds/clickfast.wav", toggle = "rbxasset://sounds/electronicpingshort.wav", swoosh = "rbxasset://sounds/swoosh.wav", } Const.ESP_COLOR_OPTIONS = { "Theme", "Red", "Sky", "Gold", "White" } Const.ESP_COLORS = { Red = Color3.fromRGB(255, 60, 60), Sky = Color3.fromRGB(120, 200, 255), Gold = Color3.fromRGB(255, 200, 80), White = WHITE, } Const.FULLBRIGHT = { Brightness = 2, ClockTime = 14, FogEnd = 100000, GlobalShadows = false, Ambient = Color3.fromRGB(178, 178, 178), OutdoorAmbient = Color3.fromRGB(178, 178, 178), } local Themes = { Sakura = { name = "Sakura", title = "Potent", subtitle = "sakura edition ✿ v6", tagline = "soft • elegant • floral", accent = Color3.fromRGB(255, 128, 180), accentAlt = Color3.fromRGB(255, 178, 205), deep = Color3.fromRGB(235, 90, 150), highlight = Color3.fromRGB(120, 200, 255), backgroundTop = Color3.fromRGB(255, 243, 248), backgroundBottom = Color3.fromRGB(252, 230, 241), card = WHITE, cardTransparency = 0.15, track = Color3.fromRGB(245, 222, 234), text = Color3.fromRGB(110, 55, 85), subtext = Color3.fromRGB(195, 140, 165), strokeColors = { Color3.fromRGB(255, 128, 180), PETAL_TIP, Color3.fromRGB(190, 150, 255), PETAL_TIP, Color3.fromRGB(255, 128, 180) }, titleColors = { Color3.fromRGB(235, 90, 150), Color3.fromRGB(255, 128, 180), PETAL_BASE, Color3.fromRGB(255, 128, 180), Color3.fromRGB(235, 90, 150) }, font = Enum.Font.FredokaOne, bodyFont = Enum.Font.FredokaOne, textSize = 14, uppercase = false, pill = true, radius = 14, windowRadius = 22, glow = 0.85, glyph = "✿", icons = { "🎯", "🤖", "✨", "🌸", "☁️" }, }, Assassin = { name = "Assassin", title = "POTENT", subtitle = "ASSASSIN PROTOCOL // V6", tagline = "DARK • SHARP • SILENT", accent = Color3.fromRGB(225, 30, 55), accentAlt = Color3.fromRGB(255, 100, 60), deep = Color3.fromRGB(120, 8, 20), highlight = Color3.fromRGB(255, 200, 80), backgroundTop = Color3.fromRGB(30, 14, 18), backgroundBottom = Color3.fromRGB(8, 8, 10), card = Color3.fromRGB(26, 22, 26), cardTransparency = 0.18, track = Color3.fromRGB(52, 42, 48), text = Color3.fromRGB(238, 232, 234), subtext = Color3.fromRGB(140, 112, 120), strokeColors = { Color3.fromRGB(225, 30, 55), Color3.fromRGB(50, 8, 14), Color3.fromRGB(255, 100, 60), Color3.fromRGB(50, 8, 14), Color3.fromRGB(225, 30, 55) }, titleColors = { Color3.fromRGB(225, 30, 55), Color3.fromRGB(255, 100, 60), WHITE, Color3.fromRGB(255, 100, 60), Color3.fromRGB(225, 30, 55) }, font = Enum.Font.Oswald, bodyFont = Enum.Font.GothamMedium, textSize = 12, uppercase = true, pill = false, radius = 4, windowRadius = 6, glow = 0.88, glyph = "", icons = { "🎯", "🤖", "👁", "🥷", "⚙" }, }, } -- Services. local playersService = cloneref(game:GetService("Players")) local runService = cloneref(game:GetService("RunService")) local userInputService = cloneref(game:GetService("UserInputService")) local tweenService = cloneref(game:GetService("TweenService")) local teleportService = cloneref(game:GetService("TeleportService")) local httpService = cloneref(game:GetService("HttpService")) local guiService = cloneref(game:GetService("GuiService")) local virtualUser = cloneref(game:GetService("VirtualUser")) local lighting = cloneref(game:GetService("Lighting")) local soundService = cloneref(game:GetService("SoundService")) local pathfindingService = cloneref(game:GetService("PathfindingService")) local contextActionService = cloneref(game:GetService("ContextActionService")) local hasVim, virtualInputManager = pcall(game.GetService, game, "VirtualInputManager") virtualInputManager = hasVim and virtualInputManager or nil -- Settings. local Settings = { theme = "Sakura", skipStarter = false, aimbot = false, aimMode = "Toggle", aimPart = "Head", priority = "Crosshair", strength = 45, prediction = 0, autoPrediction = false, humanize = false, stickyTarget = true, wallCheck = true, teamCheck = true, shieldCheck = false, fov = 150, maxDistance = 1000, showFov = true, targetInfo = true, targetMarker = true, crosshair = false, esp = true, espColor = "Theme", chamsPulse = false, boxes = false, names = true, distance = true, health = true, tracers = false, offscreenArrows = false, killEffects = true, screenEffects = false, effectDensity = 25, fullbright = false, cameraFovEnabled = false, cameraFov = 90, statsOverlay = false, infJump = false, jumpBoost = false, jumpPower = 70, noclip = false, speed = false, speedAmount = 20, fly = false, flySpeed = 50, lowGravity = false, gravity = 80, antiAfk = true, quality = "Ultra", uiSounds = true, autoPilot = false, humanSkill = 65, reactionMs = 220, retreatHealth = 20, burstFire = true, fireTolerance = 1.5, lookAround = true, usePathfinding = true, showAnalysis = true, triggerBot = true, fireMode = "Hold", fireMethod = "Auto", clickRate = 8, firePointX = -1, firePointY = -1, fireButtonPath = "", proximityFire = false, closeRange = 18, proximityRadius = 70, cameraMode = "Auto", pilotAimStyle = "Human", preAim = true, preAimRange = 60, headLock = false, botHeadshot = false, autoSprint = true, sprintMethod = "Auto", sprintButtonPath = "", moveMethod = "Auto", moveSpeed = 20, mouseSpeed = 0.5, autoMove = true, autoStrafe = true, autoJump = true, keepDistance = 25, remoteLogger = false, blockedFeatures = {}, } -- State. local Theme = Themes.Sakura local localPlayer = playersService.LocalPlayer local currentCamera = workspace.CurrentCamera local guiRoot = gethui and gethui() or game:GetService("CoreGui") local rayParams = RaycastParams.new() rayParams.FilterType = Enum.RaycastFilterType.Exclude rayParams.IgnoreWater = true pcall(function() rayParams.RespectCanCollide = true end) local connections = {} local animators = {} local refreshers = {} local espObjects = {} State.noclipCache = {} State.lightingCache = {} State.layoutOrder = 0 State.aimHeld = false State.forceStarter = false State.originalGravity, State.originalCameraFov = nil, nil local currentTarget, hubGui, tracerGui, effectsGui, starterGui, starterBlur local fovRing, targetLabel, toastHolder, screenField, crosshairFrame, targetMarker, windowField State.soundCache = {} State.firing, State.nextClick, State.sessionKills, State.pilotErrors = false, 0, 0, 0 local brain State.buttonCache = {} State.lastInputObject = nil State.sprinting = false State.pilotMoveOverride, State.velocityMove, State.pilotWorldMove = nil, nil, nil State.autoMoveStage = 1 State.crashedFeature = nil State.controlsRef, State.controlsFailed = nil, false State.wrappedControllers = {} State.heldKeys = {} State.calibrating, State.serializer, State.originalNamecall = false, nil, nil ---Track a connection for cleanup. local function track(connection) table.insert(connections, connection) return connection end ---Run fn every frame while owner stays parented. local function addAnimator(owner, fn) table.insert(animators, { owner = owner, fn = fn }) end ---Play a tween with sensible defaults. local function tween(instance, properties, duration, style, direction) local info = TweenInfo.new(duration or 0.25, style or Enum.EasingStyle.Quint, direction or Enum.EasingDirection.Out) local playing = tweenService:Create(instance, info, properties) playing:Play() return playing end ---Create an instance with properties and children. local function create(className, properties, children) local instance = Instance.new(className) for key, value in pairs(properties or {}) do if key ~= "Parent" then instance[key] = value end end for _, child in ipairs(children or {}) do child.Parent = instance end instance.Parent = properties and properties.Parent return instance end local function corner(radius) return create("UICorner", { CornerRadius = radius or UDim.new(0, Theme.radius) }) end local function round() return corner(UDim.new(1, 0)) end ---Round for Sakura, sharp for Assassin. local function pillCorner(theme) return (theme or Theme).pill and round() or corner(UDim.new(0, 3)) end ---Build a UIGradient from an evenly spaced color list. local function gradient(colors, rotation) local keypoints = {} for index, color in ipairs(colors) do table.insert(keypoints, ColorSequenceKeypoint.new((index - 1) / (#colors - 1), color)) end return create("UIGradient", { Color = ColorSequence.new(keypoints), Rotation = rotation or 0 }) end ---Stroke whose theme gradient spins forever. local function spinningStroke(theme, thickness, speed) local stroke = create("UIStroke", { Color = WHITE, Thickness = thickness or 2 }) local strokeGradient = gradient(theme.strokeColors) strokeGradient.Parent = stroke addAnimator(stroke, function(_, deltaTime) strokeGradient.Rotation = (strokeGradient.Rotation + deltaTime * (speed or 50)) % 360 end) return stroke end local function label(properties) properties.BackgroundTransparency = 1 properties.Font = properties.Font or Theme.bodyFont properties.TextColor3 = properties.TextColor3 or Theme.text return create("TextLabel", properties) end local function getCharacterParts() local character = localPlayer.Character if not character then return end return character, character:FindFirstChildOfClass("Humanoid"), character:FindFirstChild("HumanoidRootPart") end Const.HEALTH_KEYS = { "Health", "HP", "CurrentHealth" } Const.MAX_HEALTH_KEYS = { "MaxHealth", "MaxHP" } ---Health fraction, preferring custom health attributes if the game uses them. local function getHealthRatio(character, humanoid) for _, holder in ipairs({ character, humanoid }) do if not holder then continue end for _, key in ipairs(Const.HEALTH_KEYS) do local value = holder:GetAttribute(key) if typeof(value) == "number" then local maximum = 100 for _, maxKey in ipairs(Const.MAX_HEALTH_KEYS) do local candidate = holder:GetAttribute(maxKey) if typeof(candidate) == "number" and candidate > 0 then maximum = candidate break end end return math.clamp(value / maximum, 0, 1) end end end if humanoid then return math.clamp(humanoid.Health / math.max(humanoid.MaxHealth, 1), 0, 1) end return 1 end Const.CRASH_GUARD_PATH = CONFIG_FOLDER .. "/crashguard.txt" State.riskStarted = {} ---A feature that crashed the game before stays off until reset. ---@param name string ---@return boolean local function isBlocked(name) return Settings.blockedFeatures[name] == true end ---Mark a risky feature as starting; if the game dies in the next few seconds, next run knows which one. ---@param name string local function riskBegin(name) if State.riskStarted[name] or not writefile then return end State.riskStarted[name] = true pcall(function() if makefolder and not (isfolder and isfolder(CONFIG_FOLDER)) then makefolder(CONFIG_FOLDER) end writefile(Const.CRASH_GUARD_PATH, name) end) task.delay(6, function() pcall(function() if isfile(Const.CRASH_GUARD_PATH) and readfile(Const.CRASH_GUARD_PATH) == name then writefile(Const.CRASH_GUARD_PATH, "") end end) end) end ---Read the marker left by a crash and block that feature. ---@return string? local function checkCrashGuard() local ok, name = pcall(function() return isfile(Const.CRASH_GUARD_PATH) and readfile(Const.CRASH_GUARD_PATH) or "" end) if not ok or name == "" then return nil end Settings.blockedFeatures[name] = true pcall(writefile, Const.CRASH_GUARD_PATH, "") return name end local function isAimActive() if Settings.autoPilot then return true end if Settings.aimMode == "Hold" then return State.aimHeld end return Settings.aimbot end ---Scale a particle count by the graphics quality setting. ---@param base number ---@return number local function qualityCount(base) return math.max(1, math.floor(base * (Const.QUALITY_SCALE[Settings.quality] or 1) + 0.5)) end ---Play a short UI sound locally. local function playSound(name, pitch, volume) if not Settings.uiSounds then return end local sound = State.soundCache[name] if not sound then sound = Instance.new("Sound") sound.SoundId = Const.SOUNDS[name] State.soundCache[name] = sound end sound.Volume = volume or 0.4 sound.PlaybackSpeed = pitch or 1 pcall(soundService.PlayLocalSound, soundService, sound) end ---A notched sakura petal made from two tilted ellipses; returns container and lobes. local function buildPetal(parent, zIndex, transparency) local container = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), BackgroundTransparency = 1, ZIndex = zIndex or 1, Parent = parent, }) local lobes = {} for side = -1, 1, 2 do table.insert(lobes, create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromScale(0.62, 1), Position = UDim2.fromScale(0.5 + side * 0.14, 0.5), Rotation = side * 14, BackgroundColor3 = WHITE, BackgroundTransparency = transparency or 0, BorderSizePixel = 0, ZIndex = zIndex or 1, Parent = container, }, { round(), gradient({ PETAL_TIP, PETAL_BASE }, 90) })) end return container, lobes end ---Five-petal sakura blossom with a pollen centre. local function createBlossom(parent, size, zIndex, transparency) local blossom = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(size, size), BackgroundTransparency = 1, ZIndex = zIndex or 1, Parent = parent, }) for index = 0, 4 do local angle = math.rad(index * 72) local petal = buildPetal(blossom, zIndex, transparency) petal.Size = UDim2.fromScale(0.42, 0.52) petal.Position = UDim2.fromScale(0.5 + math.sin(angle) * 0.24, 0.5 - math.cos(angle) * 0.24) petal.Rotation = index * 72 end create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromScale(0.2, 0.2), Position = UDim2.fromScale(0.5, 0.5), BackgroundColor3 = Themes.Sakura.deep, BackgroundTransparency = transparency or 0, ZIndex = zIndex or 1, Parent = blossom, }, { round() }) for index = 0, 4 do local angle = math.rad(index * 72 + 36) create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromScale(0.07, 0.07), Position = UDim2.fromScale(0.5 + math.sin(angle) * 0.14, 0.5 - math.cos(angle) * 0.14), BackgroundColor3 = POLLEN, BackgroundTransparency = transparency or 0, ZIndex = zIndex or 1, Parent = blossom, }, { round() }) end return blossom end ---Eight-point throwing star built from crossed tapered blades. local function createShuriken(theme, parent, size, zIndex, transparency) local emblem = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(size, size), BackgroundTransparency = 1, ZIndex = zIndex or 1, Parent = parent, }) for index = 0, 3 do create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromScale(index % 2 == 0 and 0.16 or 0.11, index % 2 == 0 and 1 or 0.78), Position = UDim2.fromScale(0.5, 0.5), Rotation = index * 45, BackgroundColor3 = WHITE, BackgroundTransparency = transparency or 0, BorderSizePixel = 0, ZIndex = zIndex or 1, Parent = emblem, }, { corner(UDim.new(0, 2)), gradient({ theme.deep, theme.accent, theme.accentAlt, theme.accent, theme.deep }, 90), }) end create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromScale(0.36, 0.36), Position = UDim2.fromScale(0.5, 0.5), BackgroundColor3 = theme.backgroundBottom, BackgroundTransparency = transparency or 0, ZIndex = zIndex or 1, Parent = emblem, }, { round(), create("UIStroke", { Color = theme.accent, Thickness = math.max(1, size / 30), Transparency = transparency or 0 }), }) create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromScale(0.12, 0.12), Position = UDim2.fromScale(0.5, 0.5), BackgroundColor3 = theme.accent, BackgroundTransparency = transparency or 0, ZIndex = zIndex or 1, Parent = emblem, }, { round() }) return emblem end ---Theme emblem: blossom or shuriken. local function createLogo(theme, parent, size, zIndex, transparency) if theme.name == "Sakura" then return createBlossom(parent, size, zIndex, transparency) end return createShuriken(theme, parent, size, zIndex, transparency) end ---Smooth natural wind built from layered noise plus soft gusts. local function getWind(time) local breeze = math.noise(time * 0.12, 3.7) * 0.08 local gust = math.max(0, math.noise(time * 0.35, 9.1)) * 0.14 return breeze + gust end ---@class PetalField local PetalField = {} PetalField.__index = PetalField ---Drifting petal field with depth, flutter and wind. ---@param parent Instance ---@param count number ---@param options table function PetalField.new(parent, count, options) local self = setmetatable({}, PetalField) self.layer = create("Frame", { Size = UDim2.fromScale(1, 1), BackgroundTransparency = 1, ZIndex = options.zIndex or 0, Parent = parent, }) self.baseSize = options.baseSize or 12 self.speed = options.speed or 1 self.zIndex = options.zIndex or 0 self.petals = {} self:setCount(count) return self end ---Place a petal back at the top (or anywhere when seeding). function PetalField:resetPetal(petal, anywhere) petal.x = math.random() * 1.3 - 0.3 petal.y = anywhere and math.random() * 1.1 - 0.1 or -0.06 - math.random() * 0.12 petal.flip = math.random() * math.pi * 2 petal.rotation = math.random(0, 360) end ---Create a single petal with randomised depth and motion. function PetalField:spawn() local depth = 0.45 + math.random() * 0.55 local container, lobes = buildPetal(self.layer, self.zIndex) local petal = { depth = depth, width = self.baseSize * depth, height = self.baseSize * depth * 1.2, fall = (0.035 + 0.045 * depth) * (0.85 + math.random() * 0.3), swayFrequency = 0.8 + math.random() * 0.9, swayAmount = 0.02 + math.random() * 0.03, flipSpeed = 1.2 + math.random() * 2.2, spin = math.random(-50, 50), phase = math.random() * math.pi * 2, baseTransparency = 0.05 + (1 - depth) * 0.45, container = container, lobes = lobes, } self:resetPetal(petal, true) table.insert(self.petals, petal) end ---Grow or shrink the field. ---@param count number function PetalField:setCount(count) while #self.petals < count do self:spawn() end while #self.petals > count do table.remove(self.petals).container:Destroy() end end ---@param visible boolean function PetalField:setVisible(visible) self.layer.Visible = visible end ---Drop the next petal from a specific point (scale coordinates). function PetalField:emitAt(x, y) if #self.petals == 0 then return end self.emitIndex = (self.emitIndex or 0) % #self.petals + 1 local petal = self.petals[self.emitIndex] petal.x, petal.y = x, y petal.flip = math.random() * math.pi * 2 petal.rotation = math.random(0, 360) end ---Fall, sway, wind drift, 3D flip shading and edge fade. function PetalField:update(time, deltaTime, wind) if not self.layer.Visible then return end for _, petal in ipairs(self.petals) do local sway = math.sin(time * petal.swayFrequency + petal.phase) -- Petals fall slower while broadside, like real air resistance. local facing = math.abs(math.cos(petal.flip)) petal.y += petal.fall * self.speed * (1.15 - facing * 0.35) * deltaTime petal.x += (sway * petal.swayAmount + wind * petal.depth) * self.speed * deltaTime petal.flip += petal.flipSpeed * deltaTime petal.rotation += (petal.spin + wind * 120) * deltaTime if petal.y > 1.08 or petal.x > 1.2 or petal.x < -0.35 then self:resetPetal(petal, false) end petal.container.Size = UDim2.fromOffset(petal.width * (0.18 + 0.82 * facing), petal.height) petal.container.Position = UDim2.fromScale(petal.x, petal.y) petal.container.Rotation = petal.rotation + sway * 28 local edgeFade = math.clamp(math.min(petal.y + 0.06, 1.08 - petal.y) * 7, 0, 1) local transparency = 1 - (1 - petal.baseTransparency) * edgeFade local shade = PETAL_SHADE:Lerp(WHITE, 0.45 + 0.55 * facing) for _, lobe in ipairs(petal.lobes) do lobe.BackgroundTransparency = transparency lobe.BackgroundColor3 = shade end end end function PetalField:destroy() self.layer:Destroy() table.clear(self.petals) end ---@class EmberField local EmberField = {} EmberField.__index = EmberField ---Rising embers with glow, flicker and drifting smoke. ---@param parent Instance ---@param count number ---@param options table function EmberField.new(parent, count, options) local self = setmetatable({}, EmberField) self.layer = create("Frame", { Size = UDim2.fromScale(1, 1), BackgroundTransparency = 1, ZIndex = options.zIndex or 0, Parent = parent, }) self.theme = options.theme self.baseSize = options.baseSize or 4 self.speed = options.speed or 1 self.zIndex = options.zIndex or 0 self.embers = {} self.smoke = {} for _ = 1, options.smoke or 0 do local puff = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), BackgroundColor3 = SMOKE, BackgroundTransparency = 1, ZIndex = self.zIndex, Parent = self.layer, }, { round() }) local entry = { frame = puff, seed = math.random() * 100 } self:resetSmoke(entry, true) table.insert(self.smoke, entry) end self:setCount(count) return self end function EmberField:resetSmoke(entry, anywhere) entry.x = math.random() entry.life = anywhere and math.random() or 0 entry.lifetime = 7 + math.random() * 5 entry.size = 70 + math.random() * 90 end function EmberField:resetEmber(ember, anywhere) ember.x = math.random() ember.life = anywhere and math.random() or 0 ember.lifetime = 2.5 + math.random() * 3 ember.rise = 0.1 + math.random() * 0.16 ember.y = 1.04 - (anywhere and ember.life * ember.rise * ember.lifetime or 0) ember.seed = math.random() * 100 end function EmberField:spawn() local depth = 0.5 + math.random() * 0.5 local size = self.baseSize * depth local ember = { size = size, depth = depth, spin = math.random(-180, 180), glow = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), BackgroundColor3 = self.theme.accent, BackgroundTransparency = 1, ZIndex = self.zIndex, Parent = self.layer, }, { round() }), core = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), BackgroundColor3 = self.theme.accentAlt, BackgroundTransparency = 1, BorderSizePixel = 0, ZIndex = self.zIndex, Parent = self.layer, }, { corner(UDim.new(0, 1)) }), } self:resetEmber(ember, true) table.insert(self.embers, ember) end ---@param count number function EmberField:setCount(count) while #self.embers < count do self:spawn() end while #self.embers > count do local ember = table.remove(self.embers) ember.glow:Destroy() ember.core:Destroy() end end ---@param visible boolean function EmberField:setVisible(visible) self.layer.Visible = visible end ---Rise, wobble, cool from orange to deep red and flicker out. function EmberField:update(time, deltaTime, wind) if not self.layer.Visible then return end local theme = self.theme for _, ember in ipairs(self.embers) do ember.life += deltaTime / ember.lifetime * self.speed ember.y -= ember.rise * ember.depth * deltaTime * self.speed ember.x += (math.noise(ember.seed, time * 0.7) * 0.07 + wind * 0.4) * deltaTime if ember.life >= 1 or ember.y < -0.04 then self:resetEmber(ember, false) end local fade = math.min(ember.life / 0.12, 1) * math.clamp((1 - ember.life) / 0.45, 0, 1) local flicker = 0.7 + math.noise(ember.seed, time * 7) * 0.6 local intensity = math.clamp(fade * flicker, 0, 1) local color = theme.accentAlt:Lerp(theme.accent, ember.life):Lerp(theme.deep, ember.life ^ 2) local size = ember.size * (1 - ember.life * 0.45) local position = UDim2.fromScale(ember.x, ember.y) ember.core.Size = UDim2.fromOffset(size, size) ember.core.Position = position ember.core.Rotation += ember.spin * deltaTime ember.core.BackgroundColor3 = color ember.core.BackgroundTransparency = 1 - intensity ember.glow.Size = UDim2.fromOffset(size * 3.4, size * 3.4) ember.glow.Position = position ember.glow.BackgroundColor3 = color ember.glow.BackgroundTransparency = 1 - intensity * 0.22 end for _, puff in ipairs(self.smoke) do puff.life += deltaTime / puff.lifetime if puff.life >= 1 then self:resetSmoke(puff, false) end local alpha = math.sin(puff.life * math.pi) local size = puff.size * (0.6 + puff.life * 0.8) puff.frame.Size = UDim2.fromOffset(size, size) puff.frame.Position = UDim2.fromScale(puff.x + math.noise(puff.seed, time * 0.2) * 0.1, 1.1 - puff.life * 1.2) puff.frame.BackgroundTransparency = 1 - alpha * 0.1 end end function EmberField:destroy() self.layer:Destroy() table.clear(self.embers) end ---Theme particle field. local function createField(theme, parent, count, options) options.theme = theme if theme.name == "Sakura" then return PetalField.new(parent, count, options) end return EmberField.new(parent, count, options) end ---A blade slash line that cuts across then fades. local function slash(parent, position, length, angle, color, thickness, duration, zIndex) local line = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(0, thickness), Position = position, Rotation = angle, BackgroundColor3 = WHITE, BorderSizePixel = 0, ZIndex = zIndex or 10, Parent = parent, }, { round(), create("UIGradient", { Color = ColorSequence.new(color, WHITE), Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 1), NumberSequenceKeypoint.new(0.5, 0), NumberSequenceKeypoint.new(1, 1), }), }), }) tween(line, { Size = UDim2.fromOffset(length, thickness) }, duration * 0.45, Enum.EasingStyle.Quint) task.delay(duration * 0.35, function() tween(line, { Size = UDim2.fromOffset(length * 1.1, 0), BackgroundTransparency = 1 }, duration * 0.65).Completed:Connect(function() line:Destroy() end) end) end ---Radial burst of petals or sparks at a screen point. local function burst(theme, parent, position, count, spread) for _ = 1, count do local angle = math.random() * math.pi * 2 local distance = (spread or 60) * (0.5 + math.random() * 0.7) local goal = UDim2.fromOffset(position.X + math.cos(angle) * distance, position.Y + math.sin(angle) * distance) local duration = 0.6 + math.random() * 0.5 if theme.name == "Sakura" then local petal, lobes = buildPetal(parent, 20) local size = 8 + math.random() * 8 petal.Size = UDim2.fromOffset(size, size * 1.2) petal.Position = UDim2.fromOffset(position.X, position.Y) petal.Rotation = math.random(0, 360) tween(petal, { Position = goal, Rotation = petal.Rotation + math.random(-240, 240), Size = UDim2.fromOffset(size * 0.3, size * 1.2) }, duration, Enum.EasingStyle.Quart) for _, lobe in ipairs(lobes) do tween(lobe, { BackgroundTransparency = 1 }, duration, Enum.EasingStyle.Quad, Enum.EasingDirection.In) end task.delay(duration, function() petal:Destroy() end) else local spark = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(10 + math.random() * 10, 2), Position = UDim2.fromOffset(position.X, position.Y), Rotation = math.deg(angle), BackgroundColor3 = theme.accentAlt:Lerp(theme.accent, math.random()), BorderSizePixel = 0, ZIndex = 20, Parent = parent, }) tween(spark, { Position = goal, Size = UDim2.fromOffset(2, 1), BackgroundTransparency = 1 }, duration, Enum.EasingStyle.Quart).Completed:Connect(function() spark:Destroy() end) end end end ---Tap feedback: ripple plus a tumbling petal or a blade slash. local function ripple(button, input) playSound("click", 0.95 + math.random() * 0.2, 0.35) local position = button.AbsoluteSize / 2 if typeof(input) == "Instance" and input:IsA("InputObject") then position = Vector2.new(input.Position.X, input.Position.Y) + guiService:GetGuiInset() - button.AbsolutePosition end local size = math.max(button.AbsoluteSize.X, button.AbsoluteSize.Y) * 2.2 local circle = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromOffset(position.X, position.Y), BackgroundColor3 = Theme.accent, BackgroundTransparency = 0.75, ZIndex = 5, Parent = button, }, { round() }) tween(circle, { Size = UDim2.fromOffset(size, size), BackgroundTransparency = 1 }, Theme.pill and 0.7 or 0.4).Completed:Connect(function() circle:Destroy() end) if Theme.name ~= "Sakura" then slash(button, UDim2.fromOffset(position.X, position.Y), button.AbsoluteSize.X * 0.9, -18, Theme.accent, 2, 0.45, 6) return end local petal, lobes = buildPetal(button, 6) petal.Size = UDim2.fromOffset(10, 12) petal.Position = UDim2.fromOffset(position.X, position.Y) petal.Rotation = math.random(0, 360) tween(petal, { Position = UDim2.fromOffset(position.X + math.random(-18, 18), position.Y - 28), Rotation = petal.Rotation + math.random(120, 240), Size = UDim2.fromOffset(4, 12), }, 0.8, Enum.EasingStyle.Sine) for _, lobe in ipairs(lobes) do tween(lobe, { BackgroundTransparency = 1 }, 0.8) end task.delay(0.85, function() petal:Destroy() end) end ---Slide in a themed toast with a draining progress bar. ---@param text string local function notify(text) if not toastHolder then return end local slot = create("Frame", { Size = UDim2.fromOffset(240, 48), BackgroundTransparency = 1, Parent = toastHolder }) local toast = create("Frame", { Size = UDim2.fromScale(1, 1), Position = UDim2.fromOffset(260, 0), BackgroundColor3 = Theme.card, ClipsDescendants = true, Parent = slot, }, { corner(UDim.new(0, Theme.pill and 16 or 4)), create("UIStroke", { Color = Theme.accent, Thickness = 1.5, Transparency = 0.2 }), label({ Size = UDim2.new(1, -56, 1, -4), Position = UDim2.fromOffset(46, 0), Text = Theme.uppercase and string.upper(text) or text, TextSize = Theme.textSize, TextXAlignment = Enum.TextXAlignment.Left, }), }) local icon = createLogo(Theme, toast, 26) icon.Position = UDim2.fromOffset(24, 22) local progress = create("Frame", { Size = UDim2.new(1, 0, 0, 3), Position = UDim2.new(0, 0, 1, -3), BorderSizePixel = 0, BackgroundColor3 = WHITE, Parent = toast, }, { gradient({ Theme.accent, Theme.accentAlt, Theme.deep }) }) playSound("toggle", 1.6, 0.15) tween(toast, { Position = UDim2.fromOffset(0, 0) }, 0.5, Enum.EasingStyle.Back) tween(icon, { Rotation = 216 }, 2.6, Enum.EasingStyle.Sine) tween(progress, { Size = UDim2.new(0, 0, 0, 3) }, 2.6, Enum.EasingStyle.Linear) task.delay(2.6, function() tween(toast, { Position = UDim2.fromOffset(260, 0) }, 0.35, Enum.EasingStyle.Back, Enum.EasingDirection.In).Completed:Wait() slot:Destroy() end) end ---Route touch/mouse drags on a GUI object to callbacks. local function bindDrag(handle, onBegin, onMove, onEnd) local activeInput track(handle.InputBegan:Connect(function(input) local inputType = input.UserInputType if inputType ~= Enum.UserInputType.MouseButton1 and inputType ~= Enum.UserInputType.Touch then return end activeInput = input onBegin(input.Position) end)) track(userInputService.InputChanged:Connect(function(input) if not activeInput then return end local isMouseDrag = activeInput.UserInputType == Enum.UserInputType.MouseButton1 and input.UserInputType == Enum.UserInputType.MouseMovement if input == activeInput or isMouseDrag then onMove(input.Position) end end)) track(userInputService.InputEnded:Connect(function(input) if not activeInput then return end local isMouseRelease = activeInput.UserInputType == Enum.UserInputType.MouseButton1 and input.UserInputType == Enum.UserInputType.MouseButton1 if input ~= activeInput and not isMouseRelease then return end activeInput = nil if onEnd then onEnd() end end)) end ---Drag a frame by a handle; short presses count as taps. local function makeDraggable(handle, target, onTap) local startPos, startInput, moved bindDrag(handle, function(position) startPos, startInput, moved = target.Position, position, false end, function(position) local delta = position - startInput if delta.Magnitude > 6 then moved = true end target.Position = UDim2.new(startPos.X.Scale, startPos.X.Offset + delta.X, startPos.Y.Scale, startPos.Y.Offset + delta.Y) end, function() if not moved and onTap then onTap() end end) end local function nextOrder() State.layoutOrder += 1 return State.layoutOrder end ---Frosted card used as the base of every control. local function rowButton(list, height) local button = create("TextButton", { Size = UDim2.new(1, 0, 0, height or 40), BackgroundColor3 = Theme.card, BackgroundTransparency = Theme.cardTransparency, Text = "", AutoButtonColor = false, ClipsDescendants = true, LayoutOrder = nextOrder(), Parent = list, }, { corner(), create("UIStroke", { Color = Theme.accent, Transparency = 0.72, Thickness = Theme.pill and 1.5 or 1, ApplyStrokeMode = Enum.ApplyStrokeMode.Border }), }) -- Tactile squish while pressed. local scale = create("UIScale", { Parent = button }) track(button.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then tween(scale, { Scale = 0.96 }, 0.1) end end)) track(button.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then tween(scale, { Scale = 1 }, 0.55, Enum.EasingStyle.Elastic) end end)) return button end local function rowLabel(parent, text) return label({ Size = UDim2.new(1, -76, 0, 40), Position = UDim2.fromOffset(16, 0), Text = Theme.uppercase and string.upper(text) or text, TextSize = Theme.textSize, TextXAlignment = Enum.TextXAlignment.Left, Parent = parent, }) end ---Section header with an emblem and a fading divider. local function addSection(list, title) local holder = create("Frame", { Size = UDim2.new(1, 0, 0, 28), BackgroundTransparency = 1, LayoutOrder = nextOrder(), Parent = list, }) if Theme.name == "Sakura" then createBlossom(holder, 16).Position = UDim2.fromOffset(12, 14) else create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(7, 7), Position = UDim2.fromOffset(12, 14), Rotation = 45, BackgroundColor3 = Theme.accent, BorderSizePixel = 0, Parent = holder, }) end local text = label({ AutomaticSize = Enum.AutomaticSize.X, Size = UDim2.fromOffset(0, 28), Position = UDim2.fromOffset(26, 0), Text = Theme.uppercase and string.upper(title) or title, TextColor3 = Theme.name == "Sakura" and Theme.deep or Theme.accent, Font = Theme.font, TextSize = Theme.uppercase and 15 or 14, Parent = holder, }) local line = create("Frame", { BorderSizePixel = 0, BackgroundColor3 = WHITE, Parent = holder, }, { pillCorner(), gradient({ Theme.accent, Theme.backgroundBottom }) }) -- Keep the divider just right of the title. local function place() local start = text.AbsoluteSize.X + 36 line.Position = UDim2.new(0, start, 0.5, Theme.pill and -1 or 0) line.Size = UDim2.new(1, -start, 0, Theme.pill and 2 or 1) end track(text:GetPropertyChangedSignal("AbsoluteSize"):Connect(place)) place() end ---Animated switch bound to Settings[key]; returns a refresh function. local function addToggle(list, text, key, onChanged) local button = rowButton(list) rowLabel(button, text) local switch = create("Frame", { Size = UDim2.fromOffset(46, 24), Position = UDim2.new(1, -58, 0.5, -12), BackgroundColor3 = Theme.track, Parent = button, }, { pillCorner() }) local switchGradient = gradient({ Theme.accent, Theme.accentAlt }) switchGradient.Enabled = false switchGradient.Parent = switch local knob = create("Frame", { Size = UDim2.fromOffset(20, 20), Position = UDim2.fromOffset(2, 2), BackgroundColor3 = WHITE, Parent = switch, }, { pillCorner() }) local glyph = label({ Size = UDim2.fromScale(1, 1), Text = Theme.glyph, TextColor3 = Theme.accent, TextSize = 13, TextTransparency = 1, Parent = knob, }) local knobScale = create("UIScale", { Parent = knob }) local function refresh(instant) local enabled = Settings[key] local duration = instant and 0 or 0.35 switchGradient.Enabled = enabled switch.BackgroundColor3 = enabled and WHITE or Theme.track tween(knob, { Position = enabled and UDim2.fromOffset(24, 2) or UDim2.fromOffset(2, 2) }, duration, Enum.EasingStyle.Back) tween(glyph, { TextTransparency = enabled and 0 or 1, Rotation = enabled and 180 or 0 }, duration) if not instant then knobScale.Scale = 0.7 tween(knobScale, { Scale = 1 }, 0.45, Enum.EasingStyle.Elastic) end end track(button.Activated:Connect(function(input) Settings[key] = not Settings[key] ripple(button, input) playSound("toggle", Settings[key] and 1.3 or 0.9, 0.3) refresh() if onChanged then onChanged(Settings[key]) end end)) refresh(true) table.insert(refreshers, refresh) return refresh end ---Slider with an emblem knob, bound to Settings[key]. local function addSlider(list, text, key, min, max, step, onChanged) local holder = rowButton(list, 58) holder.ClipsDescendants = false local title = rowLabel(holder, text) title.Size = UDim2.new(1, -96, 0, 32) local valuePill = create("Frame", { Size = UDim2.fromOffset(54, 22), Position = UDim2.new(1, -68, 0, 6), BackgroundColor3 = WHITE, Parent = holder, }, { pillCorner(), gradient({ Theme.accent, Theme.accentAlt }) }) local valueLabel = label({ Size = UDim2.fromScale(1, 1), TextColor3 = WHITE, Font = Theme.font, TextSize = 13, Parent = valuePill, }) local bar = create("Frame", { Size = UDim2.new(1, -36, 0, Theme.pill and 8 or 4), Position = UDim2.fromOffset(18, Theme.pill and 38 or 40), BackgroundColor3 = Theme.track, Parent = holder, }, { pillCorner() }) local fill = create("Frame", { BackgroundColor3 = WHITE, Parent = bar }, { pillCorner(), gradient({ Theme.accentAlt, Theme.accent, Theme.deep }), }) local baseRotation = Theme.pill and 0 or 45 local knob = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(Theme.pill and 20 or 12, Theme.pill and 20 or 12), Position = UDim2.new(1, 0, 0.5, 0), Rotation = baseRotation, BackgroundColor3 = WHITE, ZIndex = 2, Parent = fill, }, { pillCorner(), create("UIStroke", { Color = Theme.accent, Thickness = 2 }), label({ Size = UDim2.fromScale(1, 1), Text = Theme.glyph, TextColor3 = Theme.accent, TextSize = 12, ZIndex = 2 }), }) local knobSize = knob.Size -- Bigger invisible hitbox so it's usable on touch. local hitbox = create("TextButton", { Size = UDim2.new(1, 20, 0, 34), Position = UDim2.new(0, -10, 0.5, -17), BackgroundTransparency = 1, Text = "", ZIndex = 3, Parent = bar, }) local function refresh(animate) local value = Settings[key] local size = UDim2.fromScale((value - min) / (max - min), 1) if animate then tween(fill, { Size = size }, 0.25, Enum.EasingStyle.Back) else fill.Size = size end valueLabel.Text = step < 1 and string.format("%.2f", value) or tostring(math.floor(value)) end local function setFromX(x, animate) local alpha = math.clamp((x - bar.AbsolutePosition.X) / bar.AbsoluteSize.X, 0, 1) Settings[key] = math.floor((min + (max - min) * alpha) / step + 0.5) * step refresh(animate) if onChanged then onChanged(Settings[key]) end end bindDrag(hitbox, function(position) list.ScrollingEnabled = false tween(knob, { Size = knobSize + UDim2.fromOffset(8, 8), Rotation = baseRotation + 90 }, 0.3, Enum.EasingStyle.Back) setFromX(position.X, true) end, function(position) setFromX(position.X) end, function() list.ScrollingEnabled = true tween(knob, { Size = knobSize, Rotation = baseRotation }, 0.45, Enum.EasingStyle.Elastic) end) refresh() table.insert(refreshers, refresh) end ---Option picker whose pill bounces on each tap. local function addCycle(list, text, key, options, onChanged) local button = rowButton(list) rowLabel(button, text) local pill = create("Frame", { AnchorPoint = Vector2.new(1, 0.5), AutomaticSize = Enum.AutomaticSize.X, Size = UDim2.fromOffset(0, 24), Position = UDim2.new(1, -12, 0.5, 0), BackgroundColor3 = WHITE, Parent = button, }, { pillCorner(), gradient({ Theme.accent, Theme.deep }), create("UIPadding", { PaddingLeft = UDim.new(0, 10), PaddingRight = UDim.new(0, 10) }), }) local valueLabel = label({ AutomaticSize = Enum.AutomaticSize.X, Size = UDim2.fromScale(0, 1), TextColor3 = WHITE, Font = Theme.font, TextSize = 13, Parent = pill, }) local pillScale = create("UIScale", { Parent = pill }) local function refresh() local value = tostring(Settings[key]) valueLabel.Text = (Theme.uppercase and string.upper(value) or value) .. " ›" end track(button.Activated:Connect(function(input) local index = table.find(options, Settings[key]) or 0 Settings[key] = options[index % #options + 1] ripple(button, input) refresh() pillScale.Scale = 0.75 tween(pillScale, { Scale = 1 }, 0.5, Enum.EasingStyle.Elastic) if onChanged then onChanged(Settings[key]) end end)) refresh() table.insert(refreshers, refresh) end ---Gradient action button. local function addButton(list, text, callback) local button = rowButton(list) button.BackgroundColor3 = WHITE button.BackgroundTransparency = 0 gradient({ Theme.accent, Theme.pill and Theme.accentAlt or Theme.deep }).Parent = button local textLabel = rowLabel(button, text) textLabel.Size = UDim2.new(1, -32, 1, 0) textLabel.TextXAlignment = Enum.TextXAlignment.Center textLabel.TextColor3 = WHITE textLabel.Font = Theme.font textLabel.TextSize = Theme.pill and 15 or 16 track(button.Activated:Connect(function(input) ripple(button, input) callback() end)) end ---Header wave: soft dots for Sakura, sharp equalizer bars for Assassin. local function createWave(parent) local strip = create("Frame", { Size = UDim2.new(1, -40, 0, 20), Position = UDim2.fromOffset(20, 60), BackgroundTransparency = 1, Parent = parent, }) local items = {} for index = 1, Const.WAVE_COUNT do local alpha = (index - 1) / (Const.WAVE_COUNT - 1) items[index] = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(alpha, 0.5), BackgroundColor3 = Theme.accentAlt:Lerp(Theme.deep, math.sin(alpha * math.pi)), BackgroundTransparency = (math.abs(alpha - 0.5) * 2) ^ 3 * 0.8, BorderSizePixel = 0, Parent = strip, }, { Theme.pill and round() or corner(UDim.new(0, 1)) }) end -- Wave surges when locked on. local energy = 1 addAnimator(strip, function(time, deltaTime) local goal = currentTarget and 1.8 or (isAimActive() and 1.25 or 1) energy += (goal - energy) * math.min(deltaTime * 5, 1) for index, item in ipairs(items) do local wave = math.sin(time * 3.2 * energy - index * 0.45) local strength = wave * 0.5 + 0.5 if Theme.pill then local size = 5 + strength * 3 * energy item.Position = UDim2.new((index - 1) / (Const.WAVE_COUNT - 1), 0, 0.5, wave * 5 * energy) item.Size = UDim2.fromOffset(size, size) else local secondary = math.sin(time * 1.7 + index * 0.21) * 0.5 + 0.5 item.Size = UDim2.fromOffset(3, 2 + strength * secondary * 15 * energy) end end end) end ---Red scanner line that sweeps down the window. local function createScanline(parent) local line = create("Frame", { Size = UDim2.new(1, 0, 0, 40), BackgroundColor3 = Theme.accent, BorderSizePixel = 0, ZIndex = 9, Parent = parent, }, { create("UIGradient", { Rotation = 90, Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 1), NumberSequenceKeypoint.new(0.92, 0.88), NumberSequenceKeypoint.new(0.96, 0.4), NumberSequenceKeypoint.new(1, 1), }), }), }) task.spawn(function() while Potent.running and line.Parent do line.Position = UDim2.new(0, 0, 0, -40) tween(line, { Position = UDim2.new(0, 0, 1, 0) }, 2.2, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut) task.wait(4.5) end end) end ---Soft light band that sweeps across a frame on a loop. local function createShimmer(parent, color, strength) local shimmer = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.new(0, 80, 2, 0), Position = UDim2.new(-0.3, 0, 0.5, 0), Rotation = 20, BackgroundColor3 = color, BorderSizePixel = 0, ZIndex = 8, Parent = parent, }, { create("UIGradient", { Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 1), NumberSequenceKeypoint.new(0.5, 1 - strength), NumberSequenceKeypoint.new(1, 1), }), }), }) task.spawn(function() while Potent.running and shimmer.Parent do shimmer.Position = UDim2.new(-0.3, 0, 0.5, 0) tween(shimmer, { Position = UDim2.new(1.3, 0, 0.5, 0) }, 1.6, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut) task.wait(5) end end) end ---God rays and drifting bokeh behind the Sakura window. local function createSakuraBackdrop(parent) local rays = {} for index = 1, 3 do rays[index] = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.new(0, 40 + index * 18, 2, 0), Rotation = 28, BackgroundColor3 = WHITE, BorderSizePixel = 0, ZIndex = 0, Parent = parent, }, { create("UIGradient", { Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 1), NumberSequenceKeypoint.new(0.5, 0.5), NumberSequenceKeypoint.new(1, 1), }), }), }) end local bokeh = {} for index = 1, qualityCount(8) do local size = 26 + math.random() * 50 bokeh[index] = { x = math.random(), y = math.random(), seed = math.random() * 100, frame = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(size, size), BackgroundColor3 = index % 3 == 0 and WHITE or Theme.accentAlt, BackgroundTransparency = 0.9, ZIndex = 0, Parent = parent, }, { round(), create("UIStroke", { Color = WHITE, Transparency = 0.82 }) }), } end addAnimator(parent, function(time, deltaTime) for index, ray in ipairs(rays) do ray.Position = UDim2.new(0.2 + index * 0.24 + math.sin(time * 0.15 + index * 2) * 0.08, 0, 0.5, 0) ray.BackgroundTransparency = 0.5 + (math.sin(time * 0.6 + index * 1.7) * 0.5 + 0.5) * 0.4 end for _, light in ipairs(bokeh) do light.y -= deltaTime * 0.012 light.x += math.noise(light.seed, time * 0.1) * deltaTime * 0.03 if light.y < -0.15 then light.y, light.x = 1.15, math.random() end light.frame.Position = UDim2.fromScale(light.x, light.y) light.frame.BackgroundTransparency = 0.84 + (math.sin(time * 0.8 + light.seed) * 0.5 + 0.5) * 0.12 end end) end ---Swaying sakura branch whose blossoms bloom and shed petals into the field. local function createSakuraBranch(parent, field) -- Pivot sits on the window's top-right corner so the branch sways from there. local pivot = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(320, 320), Position = UDim2.fromScale(1, 0), BackgroundTransparency = 1, ZIndex = 1, Parent = parent, }) local centre = Vector2.new(160, 160) local trunk = { Vector2.new(24, -16), Vector2.new(-18, 8), Vector2.new(-54, 20), Vector2.new(-88, 27), Vector2.new(-120, 44) } local twig = { Vector2.new(-54, 20), Vector2.new(-62, 48), Vector2.new(-78, 66) } local function segment(from, to, thickness) local a, b = from + centre, to + centre local delta = b - a create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(delta.Magnitude + thickness * 0.6, thickness), Position = UDim2.fromOffset((a.X + b.X) / 2, (a.Y + b.Y) / 2), Rotation = math.deg(math.atan2(delta.Y, delta.X)), BackgroundColor3 = WHITE, BorderSizePixel = 0, ZIndex = 1, Parent = pivot, }, { round(), gradient({ Color3.fromRGB(125, 76, 72), Color3.fromRGB(74, 42, 42) }, 90) }) end for index = 1, #trunk - 1 do segment(trunk[index], trunk[index + 1], 7.5 - index * 1.3) end for index = 1, #twig - 1 do segment(twig[index], twig[index + 1], 3.5 - index * 0.8) end local spots = { { trunk[2], 22 }, { trunk[3] + Vector2.new(4, -11), 18 }, { trunk[4], 24 }, { trunk[5], 20 }, { twig[2] + Vector2.new(9, 0), 15 }, { twig[3], 20 }, { trunk[4] + Vector2.new(-6, 16), 14 }, { trunk[5] + Vector2.new(10, -12), 12 }, } local blossoms = {} for index, spot in ipairs(spots) do local blossom = createBlossom(pivot, spot[2], 2) local position = spot[1] + centre blossom.Position = UDim2.fromOffset(position.X, position.Y) blossom.Rotation = math.random(0, 72) local bloom = create("UIScale", { Scale = 0, Parent = blossom }) task.delay(0.35 + index * 0.12, function() tween(bloom, { Scale = 1 }, 1, Enum.EasingStyle.Elastic) end) table.insert(blossoms, blossom) end local emitTimer = 0 addAnimator(pivot, function(time, deltaTime) pivot.Rotation = math.sin(time * 0.9) * 1.6 + getWind(time) * 16 for index, blossom in ipairs(blossoms) do blossom.Rotation += math.sin(time * 1.3 + index) * deltaTime * 8 end -- Shed a petal from a random blossom into the falling field. emitTimer += deltaTime if emitTimer < 0.9 / (Const.QUALITY_SCALE[Settings.quality] or 1) then return end emitTimer = 0 local size = parent.AbsoluteSize if size.X <= 0 then return end local blossom = blossoms[math.random(#blossoms)] local point = blossom.AbsolutePosition + blossom.AbsoluteSize / 2 - parent.AbsolutePosition field:emitAt(point.X / size.X, point.Y / size.Y) end) end ---Blood moon with breathing glow rings. local function createBloodMoon(parent) local holder = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(70, 70), Position = UDim2.new(1, -86, 0, 62), BackgroundTransparency = 1, ZIndex = 0, Parent = parent, }) local ringSizes = { 180, 135, 100 } local rings = {} for index, size in ipairs(ringSizes) do rings[index] = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(size, size), Position = UDim2.fromScale(0.5, 0.5), BackgroundColor3 = Theme.accent, BackgroundTransparency = 0.955 - index * 0.015, ZIndex = 0, Parent = holder, }, { round() }) end create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromScale(1, 1), Position = UDim2.fromScale(0.5, 0.5), BackgroundColor3 = WHITE, BackgroundTransparency = 0.3, ZIndex = 0, Parent = holder, }, { round(), gradient({ Theme.accentAlt, Theme.accent, Theme.deep }, 135) }) for _, crater in ipairs({ { 0.36, 0.38, 12 }, { 0.64, 0.56, 9 }, { 0.44, 0.7, 6 }, { 0.68, 0.3, 5 } }) do create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(crater[3], crater[3]), Position = UDim2.fromScale(crater[1], crater[2]), BackgroundColor3 = Theme.deep, BackgroundTransparency = 0.5, ZIndex = 0, Parent = holder, }, { round() }) end addAnimator(holder, function(time) for index, ring in ipairs(rings) do local pulse = math.sin(time * 1.4 + index * 0.8) * 0.5 + 0.5 local size = ringSizes[index] + pulse * 14 ring.Size = UDim2.fromOffset(size, size) end end) end ---Fast slanted rain streaks. local function createRain(parent, count) local drops = {} for index = 1, count do local length = 10 + math.random() * 18 drops[index] = { x = math.random() * 1.2, y = math.random(), speed = 1.1 + math.random() * 0.8, frame = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(1, length), Rotation = 14, BackgroundColor3 = Color3.fromRGB(215, 205, 215), BackgroundTransparency = 0.5 + math.random() * 0.35, BorderSizePixel = 0, ZIndex = 1, Parent = parent, }, { create("UIGradient", { Rotation = 90, Transparency = NumberSequence.new(1, 0) }) }), } end addAnimator(parent, function(_, deltaTime) for _, drop in ipairs(drops) do drop.y += drop.speed * deltaTime drop.x -= drop.speed * 0.25 * deltaTime if drop.y > 1.05 then drop.y, drop.x = -0.05, math.random() * 1.2 end drop.frame.Position = UDim2.fromScale(drop.x, drop.y) end end) end ---Occasional forked lightning bolt with a double sky flash. local function createLightning(parent) local flash = create("Frame", { Size = UDim2.fromScale(1, 1), BackgroundColor3 = Color3.fromRGB(255, 215, 220), BackgroundTransparency = 1, ZIndex = 1, Parent = parent, }) local function drawBolt(start, steps, thickness) local size = parent.AbsoluteSize local point = start local segments = {} for step = 1, steps do local nextPoint = point + Vector2.new(math.random(-26, 26), size.Y * (0.05 + math.random() * 0.06)) local delta = nextPoint - point table.insert(segments, create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(delta.Magnitude + 2, thickness), Position = UDim2.fromOffset((point.X + nextPoint.X) / 2, (point.Y + nextPoint.Y) / 2), Rotation = math.deg(math.atan2(delta.Y, delta.X)), BackgroundColor3 = WHITE, BorderSizePixel = 0, ZIndex = 1, Parent = parent, }, { create("UIStroke", { Color = Theme.accent, Thickness = thickness + 1, Transparency = 0.25 }) })) -- Occasionally split off a thinner fork. if thickness > 1 and step > 2 and math.random() < 0.3 then for _, fork in ipairs(drawBolt(nextPoint, math.random(2, 3), 1)) do table.insert(segments, fork) end end point = nextPoint end return segments end local function strike() local size = parent.AbsoluteSize local segments = drawBolt(Vector2.new(size.X * (0.2 + math.random() * 0.6), 0), 8, 2) flash.BackgroundTransparency = 0.78 tween(flash, { BackgroundTransparency = 1 }, 0.12).Completed:Connect(function() flash.BackgroundTransparency = 0.88 tween(flash, { BackgroundTransparency = 1 }, 0.5) end) for _, piece in ipairs(segments) do tween(piece, { BackgroundTransparency = 1 }, 0.45, Enum.EasingStyle.Quad, Enum.EasingDirection.In) tween(piece.UIStroke, { Transparency = 1 }, 0.45) task.delay(0.5, function() piece:Destroy() end) end end task.spawn(function() while Potent.running and parent.Parent do task.wait(4 + math.random() * 6) if parent.Parent and parent.AbsoluteSize.X > 0 then strike() end end end) end ---RGB-split glitch bursts on a title label. local function createGlitch(title) local copies = {} for index, color in ipairs({ Color3.fromRGB(255, 0, 60), Color3.fromRGB(0, 230, 255) }) do local copy = title:Clone() copy:ClearAllChildren() copy.TextColor3 = color copy.TextTransparency = 1 copy.ZIndex = 0 copy.Parent = title.Parent copies[index] = copy end local origin = title.Position task.spawn(function() while Potent.running and title.Parent do task.wait(2.5 + math.random() * 3) for _ = 1, 6 do for index, copy in ipairs(copies) do copy.Position = origin + UDim2.fromOffset((index == 1 and -1 or 1) * math.random(2, 5), math.random(-2, 2)) copy.TextTransparency = 0.2 end title.Position = origin + UDim2.fromOffset(math.random(-2, 2), 0) task.wait(0.04) end for _, copy in ipairs(copies) do copy.TextTransparency = 1 end title.Position = origin end end) end ---Drag with spring physics so the window glides and settles. local function springDrag(handle, target) local scaleX, scaleY = target.Position.X.Scale, target.Position.Y.Scale local position = Vector2.new(target.Position.X.Offset, target.Position.Y.Offset) local goal, velocity = position, Vector2.zero local startGoal, startInput bindDrag(handle, function(input) startGoal, startInput = goal, Vector2.new(input.X, input.Y) end, function(input) goal = startGoal + Vector2.new(input.X, input.Y) - startInput end) addAnimator(target, function(_, deltaTime) deltaTime = math.min(deltaTime, 1 / 30) velocity = (velocity + (goal - position) * 260 * deltaTime) * math.exp(-17 * deltaTime) position += velocity * deltaTime target.Position = UDim2.new(scaleX, position.X, scaleY, position.Y) end) end ---Build the themed hub window; returns page lists by name. local function createHub() hubGui = create("ScreenGui", { Name = "PotentHub", ResetOnSpawn = false, IgnoreGuiInset = true, ZIndexBehavior = Enum.ZIndexBehavior.Sibling, Parent = guiRoot, }) fovRing = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(0.5, 0.5), BackgroundTransparency = 1, Parent = hubGui, }, { round(), spinningStroke(Theme, 2) }) targetLabel = label({ AnchorPoint = Vector2.new(0.5, 0), Size = UDim2.fromOffset(260, 22), TextColor3 = Theme.accent, TextStrokeColor3 = Theme.name == "Sakura" and WHITE or BLACK, TextStrokeTransparency = 0.2, Font = Theme.font, TextSize = 15, Visible = false, Parent = hubGui, }) toastHolder = create("Frame", { AnchorPoint = Vector2.new(1, 1), Size = UDim2.fromOffset(240, 300), Position = UDim2.new(1, -16, 1, -16), BackgroundTransparency = 1, ZIndex = 30, Parent = hubGui, }, { create("UIListLayout", { Padding = UDim.new(0, 8), VerticalAlignment = Enum.VerticalAlignment.Bottom, SortOrder = Enum.SortOrder.LayoutOrder, }), }) local window = create("Frame", { Size = UDim2.fromOffset(HUB_SIZE.X, HUB_SIZE.Y), Position = UDim2.new(0, 24, 0.5, -HUB_SIZE.Y / 2), BackgroundTransparency = 1, Parent = hubGui, }) local windowScale = create("UIScale", { Scale = 0, Parent = window }) create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.new(1, 22, 1, 22), Position = UDim2.fromScale(0.5, 0.5), BackgroundColor3 = Theme.accent, BackgroundTransparency = Theme.glow, Parent = window, }, { corner(UDim.new(0, Theme.windowRadius + 10)) }) local main = create("Frame", { Size = UDim2.fromScale(1, 1), BackgroundColor3 = WHITE, ClipsDescendants = true, Parent = window, }, { corner(UDim.new(0, Theme.windowRadius)), gradient({ Theme.backgroundTop, Theme.backgroundBottom }, 90), spinningStroke(Theme, 2), }) if Theme.pill then createSakuraBackdrop(main) else createBloodMoon(main) end -- Big faded emblem slowly turning behind everything. local watermark = createLogo(Theme, main, 230, 0, Theme.pill and 0.82 or 0.9) watermark.Position = UDim2.new(1, -40, 1, -50) windowField = createField(Theme, main, qualityCount(Theme.pill and 14 or 26), { baseSize = Theme.pill and 12 or 4, speed = 1, zIndex = 0, smoke = 4, }) local field = windowField addAnimator(main, function(time, deltaTime) field:update(time, deltaTime, getWind(time)) watermark.Rotation = (watermark.Rotation + deltaTime * (Theme.pill and 4 or 7)) % 360 end) if Theme.pill then createSakuraBranch(main, field) else createRain(main, qualityCount(28)) createLightning(main) createScanline(main) end createShimmer(main, Theme.pill and WHITE or Theme.accent, Theme.pill and 0.45 or 0.18) local logo = createLogo(Theme, main, 38, 2) logo.Position = UDim2.fromOffset(34, 34) local logoScale = create("UIScale", { Parent = logo }) addAnimator(logo, function(time, deltaTime) logo.Rotation = (logo.Rotation + deltaTime * (isAimActive() and 120 or (Theme.pill and 18 or 35))) % 360 logoScale.Scale = 1 + math.sin(time * 2) * 0.05 end) local title = label({ Size = UDim2.new(1, -120, 0, 28), Position = UDim2.fromOffset(62, 12), Text = Theme.title, TextColor3 = WHITE, Font = Theme.font, TextSize = Theme.pill and 24 or 26, TextXAlignment = Enum.TextXAlignment.Left, Parent = main, }) -- Colour wave rolling through the title. local titleGradient = gradient(Theme.titleColors) titleGradient.Parent = title addAnimator(title, function(time) titleGradient.Offset = Vector2.new(math.sin(time * 1.1) * 0.5, 0) end) if not Theme.pill then createGlitch(title) end label({ Size = UDim2.new(1, -120, 0, 14), Position = UDim2.fromOffset(63, 39), Text = Theme.subtitle, TextColor3 = Theme.subtext, TextSize = Theme.pill and 11 or 10, TextXAlignment = Enum.TextXAlignment.Left, Parent = main, }) local hideButton = create("TextButton", { Size = UDim2.fromOffset(32, 32), Position = UDim2.new(1, -46, 0, 16), BackgroundColor3 = Theme.card, Text = "–", TextColor3 = Theme.accent, Font = Theme.font, TextSize = 18, AutoButtonColor = false, Parent = main, }, { pillCorner(), create("UIStroke", { Color = Theme.accent, Thickness = 1.5, Transparency = 0.4 }) }) createWave(main) local tabBar = create("Frame", { Size = UDim2.new(1, -24, 0, 36), Position = UDim2.fromOffset(12, 88), BackgroundColor3 = Theme.card, BackgroundTransparency = Theme.cardTransparency, ZIndex = 2, Parent = main, }, { pillCorner(), create("UIStroke", { Color = Theme.accent, Transparency = 0.65, Thickness = 1.5 }) }) local indicator = create("Frame", { Size = UDim2.new(0.2, -6, 1, -6), Position = UDim2.new(0, 3, 0, 3), BackgroundColor3 = WHITE, ZIndex = 2, Parent = tabBar, }, { pillCorner(), gradient({ Theme.accent, Theme.pill and Theme.accentAlt or Theme.deep }) }) local pagesHolder = create("Frame", { Size = UDim2.new(1, -24, 1, -144), Position = UDim2.fromOffset(12, 132), BackgroundTransparency = 1, ClipsDescendants = true, ZIndex = 2, Parent = main, }) local tabNames = { "Aim", "Auto", "Visuals", "Player", "Misc" } local tabs, lists = {}, {} local selectedIndex = 1 local function selectTab(index, instant) local direction = index >= selectedIndex and 1 or -1 selectedIndex = index tween(indicator, { Position = UDim2.new((index - 1) * 0.2, 3, 0, 3) }, instant and 0 or 0.5, Enum.EasingStyle.Back) for i, tab in ipairs(tabs) do local selected = i == index tween(tab.button, { TextColor3 = selected and WHITE or Theme.subtext }, 0.2) tab.page.Visible = selected if selected then tab.scale.Scale = instant and 1 or 0.75 tween(tab.scale, { Scale = 1 }, 0.6, Enum.EasingStyle.Elastic) tab.page.GroupTransparency = 1 tab.page.Position = UDim2.fromOffset(30 * direction, 0) tween(tab.page, { GroupTransparency = 0, Position = UDim2.new() }, 0.45, Enum.EasingStyle.Back) end end end for index, name in ipairs(tabNames) do local button = create("TextButton", { Size = UDim2.fromScale(0.2, 1), Position = UDim2.fromScale((index - 1) * 0.2, 0), BackgroundTransparency = 1, Text = Theme.icons[index] .. " " .. (Theme.uppercase and string.upper(name) or name), TextColor3 = Theme.subtext, Font = Theme.font, TextScaled = true, ZIndex = 3, Parent = tabBar, }, { create("UITextSizeConstraint", { MaxTextSize = 12 }), create("UIPadding", { PaddingLeft = UDim.new(0, 3), PaddingRight = UDim.new(0, 3) }), }) local page = create("CanvasGroup", { Size = UDim2.fromScale(1, 1), BackgroundTransparency = 1, GroupTransparency = 1, Visible = false, Parent = pagesHolder, }) local list = create("ScrollingFrame", { Size = UDim2.fromScale(1, 1), BackgroundTransparency = 1, BorderSizePixel = 0, ScrollBarThickness = 3, ScrollBarImageColor3 = Theme.accent, AutomaticCanvasSize = Enum.AutomaticSize.Y, CanvasSize = UDim2.new(), Parent = page, }, { create("UIListLayout", { Padding = UDim.new(0, 7), SortOrder = Enum.SortOrder.LayoutOrder }), create("UIPadding", { PaddingLeft = UDim.new(0, 2), PaddingRight = UDim.new(0, 8), PaddingBottom = UDim.new(0, 10), PaddingTop = UDim.new(0, 2), }), }) tabs[index] = { button = button, page = page, scale = create("UIScale", { Parent = button }) } lists[name] = list track(button.Activated:Connect(function() selectTab(index) end)) end springDrag(title, window) -- Floating emblem bubble that the window collapses into. local bubble = create("TextButton", { Size = UDim2.fromOffset(58, 58), Position = UDim2.new(0, 24, 0.5, -29), BackgroundColor3 = Theme.card, Text = "", AutoButtonColor = false, Visible = false, Parent = hubGui, }, { pillCorner(), spinningStroke(Theme, 2.5) }) local bubbleLogo = createLogo(Theme, bubble, 40) bubbleLogo.Position = UDim2.fromScale(0.5, 0.5) local bubbleScale = create("UIScale", { Scale = 0, Parent = bubble }) addAnimator(bubble, function(_, deltaTime) if bubble.Visible then bubbleLogo.Rotation = (bubbleLogo.Rotation + deltaTime * 30) % 360 end end) local function windowCentre() return window.AbsolutePosition + window.AbsoluteSize / 2 end local function setWindowOpen(open) if open then tween(bubbleScale, { Scale = 0 }, 0.2, Enum.EasingStyle.Back, Enum.EasingDirection.In).Completed:Connect(function() bubble.Visible = false end) window.Visible = true playSound("swoosh", 1.1, 0.5) tween(windowScale, { Scale = 1 }, Theme.pill and 0.6 or 0.4, Theme.pill and Enum.EasingStyle.Back or Enum.EasingStyle.Quint) -- Theme entrance flourish. task.delay(0.1, function() local centre = windowCentre() if Theme.pill then burst(Theme, hubGui, centre, 18, 180) else slash(hubGui, UDim2.fromOffset(centre.X, centre.Y), HUB_SIZE.X * 1.5, -32, Theme.accent, 3, 0.5, 25) task.delay(0.08, function() slash(hubGui, UDim2.fromOffset(centre.X, centre.Y), HUB_SIZE.X * 1.5, 32, Theme.accentAlt, 2, 0.5, 25) end) end end) else playSound("swoosh", 0.8, 0.4) tween(windowScale, { Scale = 0 }, 0.35, Enum.EasingStyle.Back, Enum.EasingDirection.In).Completed:Connect(function() window.Visible = false end) bubble.Visible = true tween(bubbleScale, { Scale = 1 }, 0.5, Enum.EasingStyle.Back) end end track(hideButton.Activated:Connect(function() setWindowOpen(false) end)) makeDraggable(bubble, bubble, function() setWindowOpen(true) end) selectTab(1, true) setWindowOpen(true) return lists end ---Floating emblem quick button: tap to toggle, or hold to aim in Hold mode. local function createQuickButton(refreshHubToggle) local button = create("TextButton", { Size = UDim2.fromOffset(64, 64), Position = UDim2.new(1, -150, 0.5, -32), BackgroundColor3 = Theme.card, Text = "", AutoButtonColor = false, Parent = hubGui, }, { pillCorner(), spinningStroke(Theme, 2.5) }) local fill = gradient({ Theme.accent, Theme.pill and Theme.accentAlt or Theme.deep }, 45) fill.Parent = button local pulse = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(0.5, 0.5), Size = UDim2.fromScale(1, 1), BackgroundTransparency = 1, Visible = false, Parent = button, }, { pillCorner(), create("UIStroke", { Color = Theme.accent, Thickness = 2 }) }) local emblem = createLogo(Theme, button, 30, 2) emblem.Position = UDim2.new(0.5, 0, 0, 22) local caption = label({ Size = UDim2.new(1, 0, 0, 16), Position = UDim2.fromOffset(0, 40), Text = "AIM", TextColor3 = Theme.accent, Font = Theme.font, TextSize = 13, ZIndex = 2, Parent = button, }) local scale = create("UIScale", { Parent = button }) local refresh addAnimator(button, function(time, deltaTime) refresh() local active = isAimActive() emblem.Rotation = (emblem.Rotation + deltaTime * (active and 120 or 15)) % 360 if not active then return end local cycle = time % 1.2 / 1.2 pulse.Size = UDim2.fromScale(1 + cycle * 0.6, 1 + cycle * 0.6) pulse.UIStroke.Transparency = cycle end) function refresh() local active = isAimActive() fill.Enabled = active pulse.Visible = active button.BackgroundColor3 = active and WHITE or Theme.card caption.TextColor3 = active and WHITE or Theme.accent caption.Text = Settings.aimMode == "Hold" and "HOLD" or "AIM" end local startPos, startInput, moved bindDrag(button, function(position) startPos, startInput, moved = button.Position, position, false scale.Scale = 0.85 tween(scale, { Scale = 1 }, 0.5, Enum.EasingStyle.Elastic) if Settings.aimMode == "Hold" then State.aimHeld = true end end, function(position) -- Hold mode locks the button in place so aiming isn't interrupted. if Settings.aimMode == "Hold" then return end local delta = position - startInput if delta.Magnitude > 6 then moved = true end button.Position = UDim2.new(startPos.X.Scale, startPos.X.Offset + delta.X, startPos.Y.Scale, startPos.Y.Offset + delta.Y) end, function() if Settings.aimMode == "Hold" then State.aimHeld = false return end if moved then return end Settings.aimbot = not Settings.aimbot refreshHubToggle() notify(Settings.aimbot and "Aimbot on" or "Aimbot off") end) refresh() return refresh end ---Themed crosshair at screen centre that tightens and rotates on lock. local function createCrosshair() crosshairFrame = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(40, 40), Position = UDim2.fromScale(0.5, 0.5), BackgroundTransparency = 1, Visible = false, Parent = hubGui, }) local arms = {} for index = 0, 3 do arms[index + 1] = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(index % 2 == 0 and 2 or 9, index % 2 == 0 and 9 or 2), BackgroundColor3 = Theme.accent, BorderSizePixel = 0, Parent = crosshairFrame, }, { pillCorner(), create("UIStroke", { Color = Theme.pill and WHITE or BLACK, Thickness = 1 }) }) end createLogo(Theme, crosshairFrame, 10).Position = UDim2.fromScale(0.5, 0.5) local gap, spin = 9, 0 addAnimator(crosshairFrame, function(_, deltaTime) if not crosshairFrame.Visible then return end local locked = currentTarget ~= nil gap += ((locked and 5 or 9) - gap) * math.min(deltaTime * 10, 1) spin += ((locked and 45 or 0) - spin) * math.min(deltaTime * 10, 1) crosshairFrame.Rotation = spin arms[1].Position = UDim2.new(0.5, 0, 0.5, -gap - 2) arms[2].Position = UDim2.new(0.5, gap + 2, 0.5, 0) arms[3].Position = UDim2.new(0.5, 0, 0.5, gap + 2) arms[4].Position = UDim2.new(0.5, -gap - 2, 0.5, 0) end) end ---Spinning emblem that hovers above the locked target. local function createTargetMarker() targetMarker = create("BillboardGui", { Size = UDim2.fromOffset(44, 44), AlwaysOnTop = true, Enabled = false, Parent = guiRoot, }) local emblem = createLogo(Theme, targetMarker, 30) emblem.Position = UDim2.fromScale(0.5, 0.5) local ring = create("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(38, 38), Position = UDim2.fromScale(0.5, 0.5), BackgroundTransparency = 1, Parent = targetMarker, }, { pillCorner(), create("UIStroke", { Color = Theme.accent, Thickness = 2 }) }) addAnimator(targetMarker, function(time, deltaTime) local character = currentTarget and currentTarget.Character local head = character and character:FindFirstChild("Head") targetMarker.Enabled = Settings.targetMarker and head ~= nil if not targetMarker.Enabled then return end targetMarker.Adornee = head targetMarker.StudsOffset = Vector3.new(0, 2.6 + math.sin(time * 3) * 0.25, 0) emblem.Rotation = (emblem.Rotation + deltaTime * 140) % 360 local cycle = time % 1 / 1 ring.Size = UDim2.fromOffset(30 + cycle * 16, 30 + cycle * 16) ring.UIStroke.Transparency = cycle end) end ---FPS / ping pill in the bottom-left corner. local function createStatsOverlay() local pill = create("Frame", { AnchorPoint = Vector2.new(0, 1), AutomaticSize = Enum.AutomaticSize.X, Size = UDim2.fromOffset(0, 28), Position = UDim2.new(0, 16, 1, -16), BackgroundColor3 = Theme.card, BackgroundTransparency = 0.1, Visible = false, Parent = hubGui, }, { pillCorner(), create("UIStroke", { Color = Theme.accent, Transparency = 0.3, Thickness = 1.5 }), create("UIPadding", { PaddingLeft = UDim.new(0, 28), PaddingRight = UDim.new(0, 12) }), }) local icon = createLogo(Theme, pill, 16) icon.Position = UDim2.new(0, -14, 0.5, 0) local statsLabel = label({ AutomaticSize = Enum.AutomaticSize.X, Size = UDim2.fromScale(0, 1), TextSize = 13, Parent = pill, }) local fps, timer = 60, 0 addAnimator(pill, function(_, deltaTime) pill.Visible = Settings.statsOverlay if not pill.Visible then return end fps += (1 / math.max(deltaTime, 1e-3) - fps) * math.min(deltaTime * 3, 1) icon.Rotation = (icon.Rotation + deltaTime * 40) % 360 timer += deltaTime if timer < 0.25 then return end timer = 0 local ok, ping = pcall(localPlayer.GetNetworkPing, localPlayer) statsLabel.Text = string.format("%d FPS • %d MS", math.floor(fps), ok and math.floor(ping * 1000) or 0) end) end ---Full-screen petals or embers behind the hub. local function createScreenEffects() effectsGui = create("ScreenGui", { Name = "PotentEffects", ResetOnSpawn = false, IgnoreGuiInset = true, DisplayOrder = -1, Parent = guiRoot, }) screenField = createField(Theme, effectsGui, qualityCount(Settings.effectDensity), { baseSize = Theme.pill and 20 or 6, speed = Theme.pill and 0.75 or 0.6, zIndex = 1, smoke = 0, }) screenField:setVisible(Settings.screenEffects) addAnimator(effectsGui, function(time, deltaTime) screenField:update(time, deltaTime, getWind(time + 40)) end) end local function wrapAngle(angle) return (angle + math.pi) % (2 * math.pi) - math.pi end ---Yaw / pitch (radians) of a look vector. local function lookToAngles(look) return math.atan2(-look.X, -look.Z), math.asin(math.clamp(look.Y, -1, 1)) end ---Game camera controller bridge: direct writes, GC-linked yaw/pitch, a CFrame hook, or mouse. local CameraDriver = { links = nil, lastSet = nil, pending = nil, applied = false, driftScore = 0, scanning = false, hookOriginal = nil, hookLook = nil, } ---Accessor for a number slot that may be a Vector2/Vector3 component. local function makeSlot(read, write, component, unit, axis, score) local slot = { unit = unit, axis = axis, score = score } if not component then slot.read, slot.write = read, write return slot end slot.read = function() return read()[component] end slot.write = function(number) local value = read() if typeof(value) == "Vector2" then write(component == "X" and Vector2.new(number, value.Y) or Vector2.new(value.X, number)) else write(Vector3.new( component == "X" and number or value.X, component == "Y" and number or value.Y, component == "Z" and number or value.Z )) end end return slot end ---Collect GC table fields and closure upvalues whose value matches the camera's yaw or pitch. function CameraDriver.findCandidates(yaw, pitch) local candidates = {} local function consider(value, read, write, name, bonus) local lowered = typeof(name) == "string" and string.lower(name) or "" local function test(number, component) if typeof(number) ~= "number" or number ~= number then return end for _, unit in ipairs({ 1, math.pi / 180 }) do local radians = number * unit local axis if math.abs(wrapAngle(radians - yaw)) < 0.004 then axis = "yaw" elseif math.abs(pitch) > 0.04 and math.abs(radians - pitch) < 0.004 then axis = "pitch" end if axis then local score = bonus if string.find(lowered, axis) then score += 6 end if string.find(lowered, "rot") or string.find(lowered, "angle") or string.find(lowered, "cam") or string.find(lowered, "look") then score += 3 end table.insert(candidates, makeSlot(read, write, component, unit, axis, score)) end end end local kind = typeof(value) if kind == "number" then test(value) elseif kind == "Vector2" then test(value.X, "X") test(value.Y, "Y") elseif kind == "Vector3" then test(value.X, "X") test(value.Y, "Y") test(value.Z, "Z") end end for index, object in ipairs(getgc(true)) do if #candidates > 400 then break end if index % 4000 == 0 then task.wait() end if type(object) == "table" then if isreadonly and isreadonly(object) then continue end pcall(function() for key, value in next, object do consider(value, function() return object[key] end, function(new) object[key] = new end, key, 0) end end) elseif type(object) == "function" and islclosure and islclosure(object) and not (isexecutorclosure and isexecutorclosure(object)) then pcall(function() for upvalueIndex, value in pairs(debug.getupvalues(object)) do consider(value, function() return debug.getupvalue(object, upvalueIndex) end, function(new) debug.setupvalue(object, upvalueIndex, new) end, nil, 1) end end) end end table.sort(candidates, function(a, b) return a.score > b.score end) return candidates end ---Nudge candidates and keep the first per axis that really turns the camera. function CameraDriver.probe(candidates) local found, tried = {}, { yaw = 0, pitch = 0 } local nudge = 0.08 for _, slot in ipairs(candidates) do if found[slot.axis] or tried[slot.axis] >= 40 then continue end tried[slot.axis] += 1 local ok, original = pcall(slot.read) if not ok or typeof(original) ~= "number" then continue end local beforeYaw, beforePitch = lookToAngles(currentCamera.CFrame.LookVector) if not pcall(slot.write, original + nudge / slot.unit) then continue end task.wait() task.wait() local afterYaw, afterPitch = lookToAngles(currentCamera.CFrame.LookVector) local change = slot.axis == "yaw" and wrapAngle(afterYaw - beforeYaw) or afterPitch - beforePitch local okNow, current = pcall(slot.read) pcall(slot.write, (okNow and current or original) - nudge / slot.unit) if math.abs(change) > 0.02 and math.abs(change) < 0.16 then slot.sign = change > 0 and 1 or -1 found[slot.axis] = slot end if found.yaw and found.pitch then break end end -- Offsets map the controller's stored angle to the camera's world angle. task.wait() local yaw, pitch = lookToAngles(currentCamera.CFrame.LookVector) if found.yaw then found.yaw.offset = wrapAngle(yaw - found.yaw.read() * found.yaw.unit * found.yaw.sign) end if found.pitch then found.pitch.offset = pitch - found.pitch.read() * found.pitch.unit * found.pitch.sign end return found end ---Scan + probe to attach to a custom camera controller. ---@param automatic boolean? function CameraDriver.calibrate(automatic) if CameraDriver.scanning then return end if isBlocked("Calibrate") then return notify("Calibrate was disabled after a crash") end riskBegin("Calibrate") if not (getgc and debug.getupvalues) then return notify("getgc not supported") end local yaw, pitch = lookToAngles(currentCamera.CFrame.LookVector) if math.abs(yaw) < 0.15 then if not automatic then notify("Turn your camera a little, then calibrate") end return end CameraDriver.scanning = true notify(automatic and "Game controls the camera, linking..." or "Calibrating, don't touch the screen") local ok, found = pcall(function() return CameraDriver.probe(CameraDriver.findCandidates(yaw, pitch)) end) CameraDriver.scanning = false if ok and found.yaw then CameraDriver.links = found notify(found.pitch and "Camera linked" or "Camera linked (horizontal only)") else notify("Couldn't link camera, try Camera Mode: Hook") end end ---Write the goal into the linked controller angles. function CameraDriver.applyLinked(goal) local links = CameraDriver.links local goalYaw, goalPitch = lookToAngles(goal.LookVector) local ok = pcall(function() local yawSlot = links.yaw local storedYaw = yawSlot.read() * yawSlot.unit * yawSlot.sign + yawSlot.offset yawSlot.write(yawSlot.read() + wrapAngle(goalYaw - storedYaw) * yawSlot.sign / yawSlot.unit) local pitchSlot = links.pitch if pitchSlot then local storedPitch = pitchSlot.read() * pitchSlot.unit * pitchSlot.sign + pitchSlot.offset pitchSlot.write(pitchSlot.read() + (goalPitch - storedPitch) * pitchSlot.sign / pitchSlot.unit) end end) -- The controller was rebuilt (respawn / new match): relink later. if not ok then CameraDriver.links = nil end end ---Move the real mouse (PC) by the angle difference. function CameraDriver.applyMouse(goal) if not mousemoverel then return end local origin = currentCamera.CFrame.Position local screenPos, onScreen = currentCamera:WorldToViewportPoint(origin + goal.LookVector * 100) if not onScreen then local yaw = lookToAngles(currentCamera.CFrame.LookVector) local goalYaw = lookToAngles(goal.LookVector) mousemoverel(-wrapAngle(goalYaw - yaw) * 300 * Settings.mouseSpeed, 0) return end -- Real mouse input: the game turns its own camera, so recoil and sensitivity still work. local centre = currentCamera.ViewportSize / 2 mousemoverel((screenPos.X - centre.X) * Settings.mouseSpeed, (screenPos.Y - centre.Y) * Settings.mouseSpeed) end ---Swap the game's own camera CFrame writes for our aimed rotation (risky, opt-in). function CameraDriver.installHook() if CameraDriver.hookOriginal or not hookmetamethod then return end local original original = hookmetamethod(game, "__newindex", newcclosure(function(self, key, value) local look = CameraDriver.hookLook if look and key == "CFrame" and not checkcaller() and rawequal(self, currentCamera) and typeof(value) == "CFrame" then return original(self, key, CFrame.lookAt(value.Position, value.Position + look)) end return original(self, key, value) end)) CameraDriver.hookOriginal = original end ---Watch for the game snapping our rotation back; auto-link when it does. function CameraDriver.detectOverride() if Settings.cameraMode ~= "Auto" or CameraDriver.links or not CameraDriver.lastSet then return end local drift = math.deg(math.acos(math.clamp(currentCamera.CFrame.LookVector:Dot(CameraDriver.lastSet.LookVector), -1, 1))) CameraDriver.driftScore = CameraDriver.driftScore * 0.95 + (drift > 1.2 and 0.05 or 0) if CameraDriver.driftScore > 0.6 then CameraDriver.driftScore = 0 if CameraDriver.resolvedMode == "Direct" and not isBlocked("Lock") and not CameraDriver.autoLock then CameraDriver.autoLock = true notify("Game overrides the camera, using Lock") elseif not CameraDriver.warned then CameraDriver.warned = true notify("Camera still overridden: try Calibrate Camera") end end end function CameraDriver.beginFrame() if not CameraDriver.applied then CameraDriver.lastSet = nil if CameraDriver.savedType then currentCamera.CameraType = CameraDriver.savedType CameraDriver.savedType = nil end end CameraDriver.applied = false CameraDriver.hookLook = nil end ---Aim the camera at a goal CFrame using the active mode. ---@param goal CFrame function CameraDriver.apply(goal) if CameraDriver.scanning then return end CameraDriver.applied = true local mode = Settings.cameraMode if mode == "Auto" then if mousemoverel and not userInputService.TouchEnabled then mode = "Mouse" elseif CameraDriver.links then mode = "Linked" elseif CameraDriver.autoLock then mode = "Lock" else mode = "Direct" end end if isBlocked(mode) then mode = "Direct" end CameraDriver.resolvedMode = mode if mode == "Linked" and CameraDriver.links then CameraDriver.applyLinked(goal) return end if mode == "Mouse" then CameraDriver.applyMouse(goal) return end if mode == "Hook" then riskBegin("Hook") CameraDriver.installHook() CameraDriver.hookLook = goal.LookVector end -- Lock: take the camera off the game's controller while aiming. if mode == "Lock" then riskBegin("Lock") if currentCamera.CameraType ~= Enum.CameraType.Scriptable then CameraDriver.savedType = CameraDriver.savedType or currentCamera.CameraType currentCamera.CameraType = Enum.CameraType.Scriptable end -- Keep the eye on the head in case the controller stops moving the camera. local head = localPlayer.Character and localPlayer.Character:FindFirstChild("Head") if head and (goal.Position - head.Position).Magnitude > 2.5 then goal = CFrame.lookAt(head.Position, head.Position + goal.LookVector) end end CameraDriver.detectOverride() currentCamera.CFrame = goal CameraDriver.lastSet = goal CameraDriver.pending = goal end ---Re-apply after other RenderStepped work in case a script wrote over us. function CameraDriver.reapply() local pending = CameraDriver.pending CameraDriver.pending = nil local mode = CameraDriver.resolvedMode if pending and (mode == "Lock" or mode == "Direct" or mode == "Hook") then currentCamera.CFrame = pending end end function CameraDriver.restore() if CameraDriver.savedType then currentCamera.CameraType = CameraDriver.savedType CameraDriver.savedType = nil end if CameraDriver.hookOriginal then hookmetamethod(game, "__newindex", CameraDriver.hookOriginal) CameraDriver.hookOriginal = nil end CameraDriver.links = nil CameraDriver.hookLook = nil end ---Find a ContextActionService action whose name matches any pattern. local function findCasAction(patterns) local ok, actions = pcall(contextActionService.GetAllBoundActionInfo, contextActionService) if not ok or typeof(actions) ~= "table" then return nil end for name in pairs(actions) do local lowered = string.lower(tostring(name)) for _, pattern in ipairs(patterns) do if string.find(lowered, pattern) then return name end end end return nil end ---Call a bound action directly with a real captured InputObject. local function callCasAction(name, down) if not (name and State.lastInputObject) then return false end local state = down and Enum.UserInputState.Begin or Enum.UserInputState.End return (pcall(contextActionService.CallFunction, contextActionService, name, state, State.lastInputObject)) end ---Validate an enemy and return their character + humanoid. ---@param player Player ---@return Model?, Humanoid? local function getEnemyCharacter(player) if not player or player == localPlayer then return end if Settings.teamCheck and player.Team and player.Team == localPlayer.Team then return end local character = player.Character if not character then return end if Settings.shieldCheck and character:FindFirstChildOfClass("ForceField") then return end local humanoid = character:FindFirstChildOfClass("Humanoid") if not (humanoid and humanoid.Health > 0) then return end return character, humanoid end ---Raycast to confirm nothing solid blocks the part (glass, viewmodels and effects are skipped). local function isVisible(part, character) local origin = currentCamera.CFrame.Position local direction = part.Position - origin local ignore = { localPlayer.Character, character, currentCamera } for _ = 1, 4 do rayParams.FilterDescendantsInstances = ignore local result = workspace:Raycast(origin, direction, rayParams) if not result then return true end local hit = result.Instance if hit.Transparency < 0.85 and hit.CanCollide then return false end table.insert(ignore, hit) end return false end ---Screen distance from crosshair if the part is usable. local function evaluatePart(part, character) local worldDistance = (part.Position - currentCamera.CFrame.Position).Magnitude if worldDistance > Settings.maxDistance then return end local screenPos, onScreen = currentCamera:WorldToViewportPoint(part.Position) if not onScreen then return end if Settings.wallCheck and not isVisible(part, character) then return end return (Vector2.new(screenPos.X, screenPos.Y) - currentCamera.ViewportSize / 2).Magnitude, screenPos, worldDistance end ---Pick the best aim part on a player for the current mode. ---@return BasePart?, number?, Vector3?, number?, Model?, Humanoid? local function getAimPart(player) local character, humanoid = getEnemyCharacter(player) if not character then return end local partNames = Settings.aimPart == "Closest" and Const.CLOSEST_PARTS or { Settings.aimPart, "Head" } local bestPart, bestDist, bestScreen, bestWorld for _, name in ipairs(partNames) do local part = character:FindFirstChild(name) if not part then continue end local dist, screenPos, worldDistance = evaluatePart(part, character) if dist and (not bestDist or dist < bestDist) then bestPart, bestDist, bestScreen, bestWorld = part, dist, screenPos, worldDistance end if bestPart and Settings.aimPart ~= "Closest" then break end end return bestPart, bestDist, bestScreen, bestWorld, character, humanoid end ---Return the best player inside the FOV using the selected priority. ---@return Player?, number?, table local function getClosestPlayer() local closest, closestDist, candidates = nil, math.huge, {} for _, player in ipairs(playersService:GetPlayers()) do local part, dist, screenPos, worldDistance, _, humanoid = getAimPart(player) if not part or dist > Settings.fov then continue end local score = dist if Settings.priority == "Distance" then score = worldDistance elseif Settings.priority == "Health" then score = humanoid.Health end table.insert(candidates, { player = player, dist = score, screenPos = screenPos }) end for _, info in ipairs(candidates) do if info.dist < closestDist then closest = info.player closestDist = info.dist end end return closest, closestDist, candidates end ---Find an instance from a GetFullName path, cached while it stays parented. ---@param path string ---@return Instance? local function resolveButton(path) if path == "" then return nil end local cached = State.buttonCache[path] if cached and cached.Parent then return cached end local current = game for name in string.gmatch(path, "[^%.]+") do current = current:FindFirstChild(name) if not current then break end end State.buttonCache[path] = current return current end ---Fire every connection on a signal; no engine input is simulated. local function fireConnections(signal, ...) if not getconnections then return 0 end local ok, list = pcall(getconnections, signal) if not ok or not list then return 0 end local fired = 0 for _, connection in ipairs(list) do if pcall(connection.Fire, connection, ...) then fired += 1 end end return fired end ---Press or release a linked game button through its own connections. ---@param button GuiObject? ---@param down boolean local function pressGameButton(button, down) if not (button and button.Parent) then return end local centre = button.AbsolutePosition + button.AbsoluteSize / 2 local fakeInput = { UserInputType = Enum.UserInputType.Touch, UserInputState = down and Enum.UserInputState.Begin or Enum.UserInputState.End, KeyCode = Enum.KeyCode.Unknown, Position = Vector3.new(centre.X, centre.Y, 0), Delta = Vector3.zero, } if down then fireConnections(button.InputBegan, fakeInput) if button:IsA("GuiButton") then fireConnections(button.MouseButton1Down, centre.X, centre.Y) end return end fireConnections(button.InputEnded, fakeInput) if button:IsA("GuiButton") then fireConnections(button.MouseButton1Up, centre.X, centre.Y) fireConnections(button.MouseButton1Click) fireConnections(button.Activated, fakeInput, 1) end end ---Pick a safe fire method; experimental input simulation is opt-in only. ---@return string local function resolveFireMethod() local method = Settings.fireMethod if method ~= "Auto" then return method end local character = localPlayer.Character if character and character:FindFirstChildOfClass("Tool") then return "Tool" end if findCasAction({ "shoot", "fire", "attack" }) and State.lastInputObject then return "Action" end if resolveButton(Settings.fireButtonPath) then return "Button" end if mouse1press and not userInputService.TouchEnabled then return "Mouse" end return "Manual" end ---Press or release fire using the resolved method. ---@param down boolean local function sendFire(down) local method = resolveFireMethod() if method == "Manual" or isBlocked("Fire" .. method) then return end if method ~= "Tool" and method ~= "Mouse" then riskBegin("Fire" .. method) end if method == "Tool" then local tool = localPlayer.Character and localPlayer.Character:FindFirstChildOfClass("Tool") if tool then pcall(down and tool.Activate or tool.Deactivate, tool) end elseif method == "Action" then callCasAction(findCasAction({ "shoot", "fire", "attack" }), down) elseif method == "Button" then pressGameButton(resolveButton(Settings.fireButtonPath), down) elseif method == "Mouse" then if mouse1press then pcall(down and mouse1press or mouse1release) end elseif method == "Touch" then if Settings.firePointX < 0 or not virtualInputManager then return end pcall(virtualInputManager.SendTouchEvent, virtualInputManager, 77, down and 0 or 2, Settings.firePointX, Settings.firePointY) elseif method == "Click" and virtualInputManager then local centre = currentCamera.ViewportSize / 2 pcall(virtualInputManager.SendMouseButtonEvent, virtualInputManager, centre.X, centre.Y, 0, down, game, 0) end end ---Sprint through a bound action, linked button or Shift on PC. ---@return string local function resolveSprintMethod() local method = Settings.sprintMethod if method ~= "Auto" then return method end if findCasAction({ "sprint", "run" }) and State.lastInputObject then return "Action" end if resolveButton(Settings.sprintButtonPath) then return "Button" end if keypress and not userInputService.TouchEnabled then return "Key" end return "Off" end ---Hold or release sprint; only sends on transitions. ---@param down boolean local function setSprint(down) if State.sprinting == down then return end State.sprinting = down local method = resolveSprintMethod() if isBlocked("Sprint" .. method) then return end if method == "Action" or method == "Button" then riskBegin("Sprint" .. method) end if method == "Action" then callCasAction(findCasAction({ "sprint", "run" }), down) elseif method == "Button" then pressGameButton(resolveButton(Settings.sprintButtonPath), down) elseif method == "Key" and keypress then pcall(down and keypress or keyrelease, 0xA0) end end ---Camera-relative version of a world direction, the space the control module works in. local function toCameraRelative(world) local look = currentCamera.CFrame.LookVector return CFrame.Angles(0, math.atan2(-look.X, -look.Z), 0):VectorToObjectSpace(world) end ---Wrap one controller's GetMoveVector so the pilot can feed it. local function wrapController(target) if type(target) ~= "table" or State.wrappedControllers[target] then return end local original = target.GetMoveVector if type(original) ~= "function" then return end State.wrappedControllers[target] = { raw = rawget(target, "GetMoveVector") } target.GetMoveVector = function(self, ...) if State.pilotMoveOverride then return State.pilotMoveOverride end return original(self, ...) end end ---Make the game's control module (joystick / WASD source) return our move vector. ---@return boolean local function installControlOverride() if State.controlsRef then wrapController(State.controlsRef.activeController) return true end if State.controlsFailed or isBlocked("Controls") or not setthreadidentity then return false end State.controlsFailed = true riskBegin("Controls") local playerScripts = localPlayer:FindFirstChild("PlayerScripts") local playerModule = playerScripts and playerScripts:FindFirstChild("PlayerModule") if not playerModule then return false end -- Identity 2 so the game module can't see executor globals, then back to whatever we had. local previousIdentity = getthreadidentity and getthreadidentity() or 8 setthreadidentity(2) local ok, module = pcall(require, playerModule) setthreadidentity(previousIdentity) if not ok or type(module) ~= "table" or type(module.GetControls) ~= "function" then return false end local okControls, controls = pcall(module.GetControls, module) if not okControls or type(controls) ~= "table" then return false end wrapController(controls) for _, controller in pairs(controls.controllers or {}) do wrapController(controller) end wrapController(controls.activeController) State.controlsRef = controls State.controlsFailed = false return true end ---Put every wrapped controller back to normal. local function restoreControlOverride() for target, info in pairs(State.wrappedControllers) do pcall(function() target.GetMoveVector = info.raw end) end table.clear(State.wrappedControllers) State.controlsRef, State.controlsFailed = nil, false end ---Hold WASD (PC) to match a camera-relative direction. local function setMoveKeys(relative) if not keypress then return end local wanted = { forward = relative ~= nil and relative.Z < -0.35, back = relative ~= nil and relative.Z > 0.35, left = relative ~= nil and relative.X < -0.35, right = relative ~= nil and relative.X > 0.35, } for name, code in pairs(Const.MOVE_KEYS) do local want = wanted[name] if (State.heldKeys[name] or false) ~= want then State.heldKeys[name] = want pcall(want and keypress or keyrelease, code) end end end ---How the pilot moves the character right now. ---@return string Const.MOVE_CHAIN = { "Humanoid", "Controls", "Velocity" } local function resolveMoveMethod() local method = Settings.moveMethod if method ~= "Auto" then return isBlocked(method) and "Humanoid" or method end return Const.MOVE_CHAIN[State.autoMoveStage] or "Humanoid" end ---Auto mode: move on to the next movement method that isn't blocked. local function advanceMoveMethod() if Settings.moveMethod ~= "Auto" or State.autoMoveStage > #Const.MOVE_CHAIN then return end repeat State.autoMoveStage += 1 until not Const.MOVE_CHAIN[State.autoMoveStage] or not isBlocked(Const.MOVE_CHAIN[State.autoMoveStage]) if Const.MOVE_CHAIN[State.autoMoveStage] then notify("Movement: trying " .. Const.MOVE_CHAIN[State.autoMoveStage]) else State.autoMoveStage = #Const.MOVE_CHAIN + 1 notify("No movement method works here") end end ---Feed a world-space move direction through the active method. ---@param world Vector3? local function driveMovement(world, humanoid) local method = resolveMoveMethod() local moving = world ~= nil and world.Magnitude > 0.05 local relative = moving and toCameraRelative(world) or nil if method == "Controls" then if installControlOverride() then State.pilotMoveOverride = relative or Vector3.zero else State.pilotMoveOverride = nil advanceMoveMethod() end else State.pilotMoveOverride = nil end State.velocityMove = method == "Velocity" and moving and world or nil State.pilotWorldMove = moving and world or nil setMoveKeys(method == "Keys" and relative or nil) if moving and method ~= "Keys" then humanoid:Move(world, false) end end ---Hand con