--[[ Moon's FE Ring v5 - ONE LocalScript, fully client-side Put it in: StarterPlayer > StarterPlayerScripts What's new in v5 • Ring is now STABLE: every part owns a fixed slot. Nothing moves, swaps or reshuffles unless a part actually disappears, and then only that one slot gets a replacement. • Ring never drops parts on its own (range / lag / "lost" checks are gone). Parts are only released when you turn the ring off, rebuild it, or they are destroyed. • Launcher: parts fly in, HIT the spot and carry on with their momentum (no more parking them there). Modes: Strike, Gather, Vortex, Slam. • Ring + Launcher work together: the launcher borrows the ring's parts, and after the hit they fly back to their own slots in the ring. • Fly: Superman pose (arm out front, head up, legs trailing), banking along your long axis, flowing cape, speed trail, FOV kick on boost, take-off hop. • Brand-new GUI with a Home dashboard, quick tiles, glass cards, glow and animations. Hotkeys: Right Ctrl panel - F fly - C carpet - T launch - E / Q up / down - Shift boost ]] local Players = game:GetService("Players") local RunService = game:GetService("RunService") local UIS = game:GetService("UserInputService") local TweenService = game:GetService("TweenService") local SoundService = game:GetService("SoundService") local LocalPlayer = Players.LocalPlayer local mouse = LocalPlayer:GetMouse() local playerGui = LocalPlayer:WaitForChild("PlayerGui") -- SETUP ------------------------------------------------------------------- local ADMIN_IDS = {} -- your UserId(s); leave empty to allow everyone local CLICK_SOUND = "12221967" -- swap for your own sound ID ---------------------------------------------------------------------------- if #ADMIN_IDS > 0 and not table.find(ADMIN_IDS, LocalPlayer.UserId) then return end for _, name in ipairs({ "MoonsFERing", "MoonsFERingOverlay" }) do local old = playerGui:FindFirstChild(name) if old then old:Destroy() end end local TAU = math.pi * 2 local WHITE = Color3.new(1, 1, 1) local GOOD = Color3.fromRGB(84, 224, 150) local BAD = Color3.fromRGB(255, 96, 112) -- Helpers ----------------------------------------------------------------- local connections = {} local function on(signal, fn) local c = signal:Connect(fn) connections[#connections + 1] = c return c end local function new(class, props, children) local inst = Instance.new(class) for k, v in pairs(props) do if k ~= "Parent" then inst[k] = v end end for _, c in ipairs(children or {}) do c.Parent = inst end if props.Parent then inst.Parent = props.Parent end return inst end local function tween(obj, props, t, style, dir) local tw = TweenService:Create( obj, TweenInfo.new(t or 0.18, style or Enum.EasingStyle.Quad, dir or Enum.EasingDirection.Out), props ) tw:Play() return tw end local function corner(r) return new("UICorner", { CornerRadius = UDim.new(0, r) }) end local function pad(top, right, bottom, left) right = right or top bottom = bottom or top left = left or right return new("UIPadding", { PaddingTop = UDim.new(0, top), PaddingRight = UDim.new(0, right), PaddingBottom = UDim.new(0, bottom), PaddingLeft = UDim.new(0, left), }) end local function list(padding, extra) local l = new("UIListLayout", { Padding = UDim.new(0, padding), SortOrder = Enum.SortOrder.LayoutOrder, }) if extra then for k, v in pairs(extra) do l[k] = v end end return l end local function getHumanoid() local c = LocalPlayer.Character return c and c:FindFirstChildOfClass("Humanoid") end local function getRoot() local c = LocalPlayer.Character return c and c:FindFirstChild("HumanoidRootPart") end local function isCharacterPart(part) local m = part:FindFirstAncestorOfClass("Model") while m do if Players:GetPlayerFromCharacter(m) then return true end m = m.Parent and m.Parent:FindFirstAncestorOfClass("Model") end return false end local function lerpAngle(a, b, t) local d = (b - a + math.pi) % TAU - math.pi return a + d * t end -- runs a feature step safely so one bug can never freeze the whole panel local function guard(name, fn, ...) local ok, err = pcall(fn, ...) if not ok then warn("[Moon's FE Ring] " .. name .. " error: " .. tostring(err)) end return ok end -- Theme ------------------------------------------------------------------- local THEMES = { Midnight = { bg = Color3.fromRGB(10, 10, 17), bg2 = Color3.fromRGB(20, 18, 34), surface = Color3.fromRGB(22, 22, 36), surface2 = Color3.fromRGB(31, 31, 49), surface3 = Color3.fromRGB(45, 45, 68), stroke = Color3.fromRGB(44, 44, 68), text = Color3.fromRGB(244, 244, 255), sub = Color3.fromRGB(165, 167, 196), dim = Color3.fromRGB(110, 112, 144), off = Color3.fromRGB(58, 58, 84), accent = Color3.fromRGB(138, 99, 255), a2 = Color3.fromRGB(214, 100, 255), }, Obsidian = { bg = Color3.fromRGB(8, 8, 8), bg2 = Color3.fromRGB(18, 18, 18), surface = Color3.fromRGB(22, 22, 22), surface2 = Color3.fromRGB(31, 31, 31), surface3 = Color3.fromRGB(46, 46, 46), stroke = Color3.fromRGB(44, 44, 44), text = Color3.fromRGB(242, 242, 242), sub = Color3.fromRGB(162, 162, 162), dim = Color3.fromRGB(110, 110, 110), off = Color3.fromRGB(62, 62, 62), accent = Color3.fromRGB(96, 150, 255), a2 = Color3.fromRGB(160, 110, 255), }, Ocean = { bg = Color3.fromRGB(6, 13, 24), bg2 = Color3.fromRGB(11, 27, 46), surface = Color3.fromRGB(15, 31, 52), surface2 = Color3.fromRGB(23, 44, 72), surface3 = Color3.fromRGB(36, 62, 98), stroke = Color3.fromRGB(34, 58, 92), text = Color3.fromRGB(238, 246, 255), sub = Color3.fromRGB(150, 174, 206), dim = Color3.fromRGB(100, 124, 158), off = Color3.fromRGB(50, 72, 104), accent = Color3.fromRGB(56, 150, 255), a2 = Color3.fromRGB(0, 214, 214), }, Nebula = { bg = Color3.fromRGB(12, 7, 26), bg2 = Color3.fromRGB(26, 12, 48), surface = Color3.fromRGB(28, 16, 52), surface2 = Color3.fromRGB(40, 24, 72), surface3 = Color3.fromRGB(58, 36, 100), stroke = Color3.fromRGB(62, 38, 104), text = Color3.fromRGB(248, 240, 255), sub = Color3.fromRGB(186, 164, 224), dim = Color3.fromRGB(128, 106, 170), off = Color3.fromRGB(76, 54, 118), accent = Color3.fromRGB(176, 92, 255), a2 = Color3.fromRGB(255, 92, 200), }, Crimson = { bg = Color3.fromRGB(14, 6, 8), bg2 = Color3.fromRGB(30, 10, 14), surface = Color3.fromRGB(32, 14, 18), surface2 = Color3.fromRGB(46, 20, 26), surface3 = Color3.fromRGB(68, 30, 38), stroke = Color3.fromRGB(74, 32, 40), text = Color3.fromRGB(255, 240, 240), sub = Color3.fromRGB(222, 160, 166), dim = Color3.fromRGB(156, 100, 108), off = Color3.fromRGB(92, 52, 58), accent = Color3.fromRGB(240, 60, 84), a2 = Color3.fromRGB(255, 140, 70), }, Forest = { bg = Color3.fromRGB(6, 14, 10), bg2 = Color3.fromRGB(10, 28, 20), surface = Color3.fromRGB(14, 32, 24), surface2 = Color3.fromRGB(22, 48, 36), surface3 = Color3.fromRGB(34, 70, 52), stroke = Color3.fromRGB(34, 66, 50), text = Color3.fromRGB(238, 252, 244), sub = Color3.fromRGB(156, 204, 176), dim = Color3.fromRGB(100, 148, 120), off = Color3.fromRGB(50, 86, 66), accent = Color3.fromRGB(46, 190, 112), a2 = Color3.fromRGB(150, 225, 80), }, Sunset = { bg = Color3.fromRGB(18, 8, 14), bg2 = Color3.fromRGB(40, 14, 24), surface = Color3.fromRGB(42, 20, 28), surface2 = Color3.fromRGB(60, 28, 38), surface3 = Color3.fromRGB(84, 40, 52), stroke = Color3.fromRGB(88, 42, 54), text = Color3.fromRGB(255, 242, 236), sub = Color3.fromRGB(232, 170, 150), dim = Color3.fromRGB(164, 110, 100), off = Color3.fromRGB(100, 56, 56), accent = Color3.fromRGB(255, 112, 86), a2 = Color3.fromRGB(255, 70, 150), }, Cyber = { bg = Color3.fromRGB(5, 8, 18), bg2 = Color3.fromRGB(10, 16, 36), surface = Color3.fromRGB(14, 20, 44), surface2 = Color3.fromRGB(22, 30, 62), surface3 = Color3.fromRGB(34, 44, 86), stroke = Color3.fromRGB(30, 44, 92), text = Color3.fromRGB(232, 248, 255), sub = Color3.fromRGB(140, 190, 230), dim = Color3.fromRGB(92, 130, 176), off = Color3.fromRGB(44, 60, 100), accent = Color3.fromRGB(0, 178, 230), a2 = Color3.fromRGB(255, 40, 170), }, Sakura = { bg = Color3.fromRGB(21, 10, 17), bg2 = Color3.fromRGB(36, 17, 29), surface = Color3.fromRGB(40, 22, 33), surface2 = Color3.fromRGB(54, 31, 45), surface3 = Color3.fromRGB(76, 44, 64), stroke = Color3.fromRGB(72, 42, 62), text = Color3.fromRGB(255, 241, 247), sub = Color3.fromRGB(212, 162, 186), dim = Color3.fromRGB(152, 108, 132), off = Color3.fromRGB(90, 56, 76), accent = Color3.fromRGB(255, 110, 165), a2 = Color3.fromRGB(190, 120, 255), fx = "petals", }, Frost = { -- light theme bg = Color3.fromRGB(230, 235, 246), bg2 = Color3.fromRGB(244, 246, 252), surface = Color3.fromRGB(255, 255, 255), surface2 = Color3.fromRGB(237, 241, 250), surface3 = Color3.fromRGB(222, 228, 242), stroke = Color3.fromRGB(208, 215, 232), text = Color3.fromRGB(24, 26, 44), sub = Color3.fromRGB(80, 88, 120), dim = Color3.fromRGB(126, 134, 166), off = Color3.fromRGB(176, 184, 210), accent = Color3.fromRGB(84, 108, 255), a2 = Color3.fromRGB(150, 90, 255), }, Halloween = { bg = Color3.fromRGB(11, 7, 10), bg2 = Color3.fromRGB(30, 15, 14), surface = Color3.fromRGB(28, 19, 17), surface2 = Color3.fromRGB(40, 27, 21), surface3 = Color3.fromRGB(60, 40, 27), stroke = Color3.fromRGB(74, 46, 26), text = Color3.fromRGB(255, 238, 220), sub = Color3.fromRGB(214, 160, 110), dim = Color3.fromRGB(152, 108, 72), off = Color3.fromRGB(86, 58, 38), accent = Color3.fromRGB(255, 118, 20), a2 = Color3.fromRGB(150, 60, 230), fx = "embers", }, Christmas = { bg = Color3.fromRGB(7, 16, 12), bg2 = Color3.fromRGB(14, 36, 26), surface = Color3.fromRGB(18, 40, 30), surface2 = Color3.fromRGB(26, 56, 42), surface3 = Color3.fromRGB(38, 78, 58), stroke = Color3.fromRGB(46, 92, 66), text = Color3.fromRGB(255, 246, 240), sub = Color3.fromRGB(190, 222, 200), dim = Color3.fromRGB(120, 162, 136), off = Color3.fromRGB(60, 100, 78), accent = Color3.fromRGB(226, 44, 56), a2 = Color3.fromRGB(56, 188, 96), fx = "snow", }, } local THEME_NAMES = { "Midnight", "Obsidian", "Ocean", "Nebula", "Crimson", "Forest", "Sunset", "Cyber", "Sakura", "Frost", "Halloween", "Christmas", } local theme = THEMES.Midnight local accent = Color3.fromRGB(138, 99, 255) local useThemeA2 = true -- the theme picks the second gradient color until you choose your own accent local function accent2() if useThemeA2 and theme.a2 then return theme.a2 end local h, s, v = accent:ToHSV() return Color3.fromHSV((h + 0.09) % 1, s, v) end local function resolve(key) if key == "accent" then return accent elseif key == "accent2" then return accent2() elseif key == "accentSeq" then return ColorSequence.new(accent, accent2()) elseif key == "bgSeq" then return ColorSequence.new(theme.bg2, theme.bg) elseif key == "frameSeq" then return ColorSequence.new({ ColorSequenceKeypoint.new(0, accent), ColorSequenceKeypoint.new(0.5, theme.stroke), ColorSequenceKeypoint.new(1, accent2()), }) end return theme[key] end -- every themed instance registers here so theme / accent changes repaint everything local styled = {} local hooks = {} local function S(inst, map) local usesAccent = false for prop, key in pairs(map) do inst[prop] = resolve(key) if string.sub(key, 1, 6) == "accent" or key == "frameSeq" then usesAccent = true end end styled[#styled + 1] = { inst = inst, map = map, acc = usesAccent } return inst end -- hook(anchor, fn): fn repaints something custom, and re-runs on every theme / accent change local function hook(anchor, fn) hooks[#hooks + 1] = { a = anchor, f = fn } fn() end local function restyle(accentOnly) local keep = {} for _, e in ipairs(styled) do if e.inst.Parent ~= nil then if not accentOnly or e.acc then for prop, key in pairs(e.map) do e.inst[prop] = resolve(key) end end keep[#keep + 1] = e end end styled = keep local keepH = {} for _, h in ipairs(hooks) do if h.a.Parent ~= nil then h.f() keepH[#keepH + 1] = h end end hooks = keepH end local function setAccent(c) accent = c useThemeA2 = false restyle(true) end local function setTheme(name) theme = THEMES[name] or theme if theme.accent then accent = theme.accent end useThemeA2 = true restyle(false) end local function stroke(key, thickness, transparency) return S(new("UIStroke", { Thickness = thickness or 1, Transparency = transparency or 0, ApplyStrokeMode = Enum.ApplyStrokeMode.Border, }), { Color = key or "stroke" }) end -- Sound ------------------------------------------------------------------- local soundsOn = true local clickSounds, clickIdx = {}, 0 local function playClick() if not soundsOn then return end clickIdx = clickIdx % 3 + 1 local s = clickSounds[clickIdx] if not s then s = Instance.new("Sound") s.SoundId = "rbxassetid://" .. CLICK_SOUND s.Volume = 0.3 s.Parent = SoundService clickSounds[clickIdx] = s end s.TimePosition = 0 s:Play() end -- Forward declarations ---------------------------------------------------- local UI = {} -- toggle / control handles, filled in when the panel is built local toast = function() end local refreshStatus = function() end local setRing, setFly, setCarpet -- feature switches (defined below) local function syncToggle(name, state) if UI[name] then UI[name].Set(state, true) end if UI[name .. "Tile"] then UI[name .. "Tile"].Set(state, true) end end -- State ------------------------------------------------------------------- local config = { shape = "Ring", radius = 30, height = 20, spin = 90, strength = 800, -- max speed parts may travel at (AlignPosition.MaxVelocity) snap = 150, -- how tightly parts hug their slot (AlignPosition.Responsiveness) range = 250, density = 60, -- number of slots in the shape (the shape never changes size by itself) maxParts = 200, maxSize = 40, featherweight = false, -- parts get ~zero density while held, so they are easy to steer and keep control of antiSleep = true, -- tiny constant nudge so held parts never fall asleep (asleep parts lose their owner) tidy = true, reclaim = true, -- swap parts that fell out of the ring for good (never touches parts that are fine) scanRate = 0.25, center = "Me", } local touchV = 0 local hotkeysOn = true local function cameraMoveDir(hum) local cam = workspace.CurrentCamera local mv = hum.MoveDirection if mv.Magnitude < 0.05 then return Vector3.zero end local obj = cam.CFrame:VectorToObjectSpace(mv) local f, s = -obj.Z, obj.X local len = math.sqrt(f * f + s * s) if len < 1e-3 then return Vector3.zero end return cam.CFrame.LookVector * (f / len) + cam.CFrame.RightVector * (s / len) end local function verticalInput(allowSpace) local v = touchV if not UIS:GetFocusedTextBox() then if UIS:IsKeyDown(Enum.KeyCode.E) then v = v + 1 end if allowSpace and UIS:IsKeyDown(Enum.KeyCode.Space) then v = v + 1 end if UIS:IsKeyDown(Enum.KeyCode.Q) or UIS:IsKeyDown(Enum.KeyCode.LeftControl) then v = v - 1 end end return math.clamp(v, -1, 1) end -- Part manager: one owner per part ------------------------------------------ local PM = { owner = {}, collide = {}, props = {} } function PM.claim(part, tag) if PM.owner[part] then return false end PM.owner[part] = tag PM.collide[part] = part.CanCollide return true end function PM.release(part) if PM.owner[part] == nil then return end if part.Parent and PM.collide[part] ~= nil then part.CanCollide = PM.collide[part] end local pr = PM.props[part] if pr then if part.Parent then part.CustomPhysicalProperties = pr.p end PM.props[part] = nil end PM.owner[part] = nil PM.collide[part] = nil end local scanParams = OverlapParams.new() scanParams.FilterType = Enum.RaycastFilterType.Exclude scanParams.MaxParts = 4000 -- nearest-first list of free, unanchored assemblies near a point local function collectLoose(center, radius, maxSize, limit, extraFilter) local char = LocalPlayer.Character scanParams.FilterDescendantsInstances = char and { char } or {} local seen, found = {}, {} for _, p in ipairs(workspace:GetPartBoundsInRadius(center, radius, scanParams)) do local top = p.AssemblyRootPart or p if not seen[top] then seen[top] = true if not top.Anchored and not PM.owner[top] and top.Size.Magnitude <= maxSize and not isCharacterPart(top) and (not extraFilter or extraFilter(top)) then found[#found + 1] = { part = top, d = (top.Position - center).Magnitude } end end end table.sort(found, function(a, b) return a.d < b.d end) local out = {} for i = 1, math.min(limit, #found) do out[i] = found[i].part end return out end -- Shapes ------------------------------------------------------------------ -- every shape returns an offset from the centre: f(slot, layoutSize, theta, now, radius, height) local SHAPES = {} local SHAPE_NAMES = { "Ring", "Double Ring", "Tornado", "Sphere", "Helix", "Heart", "Wave", "Galaxy", "Torus", "Infinity", "Atom", } SHAPES["Ring"] = function(i, n, th, _, R, H) local layers = math.clamp(math.ceil(n / 40), 1, 6) local layer = (i - 1) % layers local per = math.ceil(n / layers) local slot = math.floor((i - 1) / layers) local t = slot / per * TAU + th local r = R + layer * 3 return Vector3.new(math.cos(t) * r, H + layer * 1.5, math.sin(t) * r) end SHAPES["Double Ring"] = function(i, n, th, _, R, H) local inner = i % 2 == 0 local per = math.ceil(n / 2) local slot = math.floor((i - 1) / 2) local t = slot / per * TAU + (inner and -th * 1.5 or th) local r = inner and R * 0.6 or R return Vector3.new(math.cos(t) * r, H + (inner and 4 or 0), math.sin(t) * r) end SHAPES["Tornado"] = function(i, n, th, _, R, H) local f = (i - 1) / n local t = f * TAU * 5 + th local r = R * (0.15 + 0.85 * f) return Vector3.new(math.cos(t) * r, f * H, math.sin(t) * r) end SHAPES["Sphere"] = function(i, n, th, _, R, H) local y = 1 - 2 * (i - 0.5) / n local rr = math.sqrt(math.max(0, 1 - y * y)) local phi = (i - 1) * 2.399963 + th return Vector3.new(math.cos(phi) * rr * R, H + y * R, math.sin(phi) * rr * R) end SHAPES["Helix"] = function(i, n, th, _, R, H) local strand = i % 2 local f = math.floor((i - 1) / 2) / math.max(1, math.ceil(n / 2)) local t = f * TAU * 3 + th + strand * math.pi return Vector3.new(math.cos(t) * R, f * H, math.sin(t) * R) end SHAPES["Heart"] = function(i, n, th, _, R, H) local t = (i - 1) / n * TAU local x = 16 * math.sin(t) ^ 3 local y = 13 * math.cos(t) - 5 * math.cos(2 * t) - 2 * math.cos(3 * t) - math.cos(4 * t) local s = R / 16 return Vector3.new(x * s * math.cos(th), H + y * s, x * s * math.sin(th)) end SHAPES["Wave"] = function(i, n, th, now, R, H) local t = (i - 1) / n * TAU + th local w = math.sin((i - 1) / n * TAU * 4 + now * 3) * R * 0.2 return Vector3.new(math.cos(t) * R, H + w, math.sin(t) * R) end SHAPES["Galaxy"] = function(i, n, th, _, R, H) local f = (i - 0.5) / n local arm = i % 3 local r = R * math.sqrt(f) local a = f * TAU * 1.3 + arm * TAU / 3 + th return Vector3.new(math.cos(a) * r, H + math.sin(f * TAU * 6) * 1.5, math.sin(a) * r) end SHAPES["Torus"] = function(i, n, th, _, R, H) local f = (i - 1) / n local u = f * TAU + th local v = f * TAU * 14 + th * 2 local r = R * 0.3 local rad = R + r * math.cos(v) return Vector3.new(math.cos(u) * rad, H + r * math.sin(v), math.sin(u) * rad) end SHAPES["Infinity"] = function(i, n, th, _, R, H) local t = (i - 1) / n * TAU local d = 1 + math.sin(t) ^ 2 local p = Vector3.new(math.cos(t) * R / d, 0, math.sin(t) * math.cos(t) * R / d) return CFrame.Angles(0, th * 0.35, 0) * p + Vector3.new(0, H + math.sin(t * 2) * R * 0.1, 0) end SHAPES["Atom"] = function(i, n, th, _, R, H) local k = i % 3 local per = math.ceil(n / 3) local slot = math.floor((i - 1) / 3) local t = slot / per * TAU + th * (1 + k * 0.35) local p = Vector3.new(math.cos(t) * R, 0, math.sin(t) * R) local cf = CFrame.Angles(0, k * TAU / 3, 0) * CFrame.Angles(math.rad(62), 0, 0) return cf * p + Vector3.new(0, H, 0) end -- Swarm ------------------------------------------------------------------- -- A group of parts steered to FIXED SLOTS by AlignPosition / AlignOrientation. -- A part keeps its slot for as long as it exists, so nothing reshuffles. -- opts: getCenter(now) -> Vector3|nil, slot(k,n,theta,now) -> Vector3, -- spin() deg/s, speed() studs/s, snap() stiffness, tidy() bool, reclaim() bool local Swarm = {} Swarm.__index = Swarm function Swarm.new(tag, opts) local self = setmetatable({}, Swarm) self.tag = tag self.opts = opts self.slots = {} -- [k] = part self.slotOf = {} -- [part] = k self.fx = {} self.layoutN = 0 -- size of the layout; only grows while the swarm is not frozen self.count = 0 self.frozen = false -- frozen = new parts may only fill empty slots self.lent = {} -- [k] = time until which this empty slot is reserved (parts are away on a launch) self.theta = 0 self.dirty = false self.tidyState = nil self.cool = {} self.deadline = {} self.stuck = {} self.grace = {} -- [part] = time before which we do not judge it (it is still flying in) self.uncontrolled = {} -- [part] = seconds spent far from its slot self.ctrlT = 0 self.lost = 0 self.lastCenter = nil return self end function Swarm:canGrab(part, now) local c = self.cool[part] return not c or c < now end function Swarm:freeSlots() local out = {} for k = 1, self.layoutN do if not self.slots[k] and not self.lent[k] then out[#out + 1] = k end end return out end -- add(part) -> first empty slot (or a new one while not frozen) -- add(part, slot) -> that exact slot function Swarm:add(part, slot) if not PM.claim(part, self.tag) then return false end if slot then if self.slots[slot] then PM.release(part) return false end if slot > self.layoutN then self.layoutN = slot end self.lent[slot] = nil else for k = 1, self.layoutN do if not self.slots[k] and not self.lent[k] then slot = k break end end if not slot then if self.frozen then PM.release(part) return false end self.layoutN += 1 slot = self.layoutN end end part.CanCollide = false self.slots[slot] = part self.slotOf[part] = slot self.count += 1 self.fx[part] = { placed = false, vel = Vector3.zero, miss = 0, setPos = nil, prevTarget = nil } local dist = self.lastCenter and (part.Position - self.lastCenter).Magnitude or 0 self.deadline[part] = os.clock() + dist / math.max(self.opts.speed(), 10) * 1.3 + 2.5 self.grace[part] = os.clock() + 3.5 + dist / math.max(self.opts.speed(), 10) return true end -- removes the constraints and frees the slot; returns the slot the part had function Swarm:detach(part) local fx = self.fx[part] if fx then self.fx[part] = nil if part.Parent then part.AssemblyLinearVelocity = Vector3.zero end end local k = self.slotOf[part] if k then self.slots[k] = nil self.slotOf[part] = nil self.count -= 1 end self.stuck[part] = nil self.deadline[part] = nil self.grace[part] = nil self.uncontrolled[part] = nil PM.release(part) return k end function Swarm:list() local out = {} for k = 1, self.layoutN do if self.slots[k] then out[#out + 1] = self.slots[k] end end return out end function Swarm:releaseAll() for _, p in ipairs(self:list()) do self:detach(p) end table.clear(self.lent) end -- full reset: lets everything go and forgets the layout function Swarm:reset() self:releaseAll() self.layoutN = 0 self.frozen = false self.count = 0 table.clear(self.cool) end -- the launcher borrows parts: they are released but their slots stay reserved -- returns { {part=, slot=}, ... } function Swarm:lend(untilTime) local out = {} for _, p in ipairs(self:list()) do local k = self:detach(p) self.lent[k] = untilTime out[#out + 1] = { part = p, slot = k } end return out end -- how many parts this client really controls (ReceiveAge == 0 means locally simulated) function Swarm:controlled() -- a part is "yours" when it stays exactly where we put it local owned = 0 for _, p in ipairs(self:list()) do local fx = self.fx[p] if fx and fx.miss == 0 then owned = owned + 1 end end return owned, true end -- returns (parts near their slot, total parts) function Swarm:step(dt, now) local o = self.opts local center = o.getCenter(now) if not center or self.count == 0 then return 0, self.count end self.theta = self.theta + math.rad(o.spin()) * dt for k, t in pairs(self.lent) do if now > t then self.lent[k] = nil end end self.lastCenter = center local tidy = o.tidy() local flySpeed = o.speed() local invDt = dt > 0 and 1 / dt or 0 local near, n = 0, self.layoutN for k = 1, n do local part = self.slots[k] if part then local fx = self.fx[part] if not part.Parent or part.Anchored or not fx then self:detach(part) -- it disappeared: only THIS slot opens up else -- ownership test: we put it at fx.setPos last frame. If it is somewhere else now, -- another player's simulation is winning and we can never hold this part. local lostIt = false if fx.setPos and now > (self.grace[part] or 0) then local expect = fx.vel.Magnitude * dt * 1.5 + 3 if (part.Position - fx.setPos).Magnitude > expect then fx.miss = fx.miss + 1 if fx.miss > 45 and o.reclaim and o.reclaim() then lostIt = true end else fx.miss = 0 end end if lostIt then self:detach(part) self.cool[part] = now + 12 self.lost = self.lost + 1 else local off = o.slot(k, n, self.theta, now) local goal = center + off local cur = part.Position local pos if not fx.placed then -- fly in fast, then lock local d = goal - cur local dist = d.Magnitude if dist < 1.5 then fx.placed = true pos = goal fx.vel = Vector3.zero else pos = cur + d.Unit * math.min(dist, flySpeed * dt) fx.vel = d.Unit * flySpeed end else -- locked: sits EXACTLY on its slot every frame pos = goal local v = fx.prevTarget and (goal - fx.prevTarget) * invDt or Vector3.zero if v.Magnitude > 150 then v = v.Unit * 150 end fx.vel = v near = near + 1 end fx.prevTarget = goal local rot = part.CFrame.Rotation if tidy then local flat = Vector3.new(off.X, 0, off.Z) if flat.Magnitude > 0.1 then local want = CFrame.lookAt(Vector3.zero, flat) rot = fx.placed and want or rot:Lerp(want, 0.3) end end part.CFrame = CFrame.new(pos) * rot part.AssemblyLinearVelocity = fx.vel part.AssemblyAngularVelocity = Vector3.zero fx.setPos = pos end end end end return near, self.count end -- Ring -------------------------------------------------------------------- local ringOn = false local lastScan = 0 local ringStats = { n = 0, owned = 0, known = true, lost = 0 } local seenLost, lostToastAt = 0, -10 local returns = {} -- parts flying back to the ring after a launch: [part] = {slot, at, expire} local ringRebuildAt = nil local function ringCenter() local c = config.center local root = getRoot() if c == "Cursor" then if not root then return nil end local p = mouse.Hit.Position local d = p - root.Position if d.Magnitude > 200 then p = root.Position + d.Unit * 200 end return p elseif c ~= "Me" then local pl = Players:FindFirstChild(c) local ch = pl and pl.Character local r = ch and ch:FindFirstChild("HumanoidRootPart") return r and r.Position end return root and root.Position end -- slots are filled in a spread-out order (golden-ratio step), so even a half-filled ring -- already looks like the full shape instead of a clump on one side local permCache = {} local function permStep(n) local s = permCache[n] if not s then local function gcd(a, b) while b ~= 0 do a, b = b, a % b end return a end s = math.max(1, math.floor(n * 0.6180339887 + 0.5)) while gcd(s, n) ~= 1 do s += 1 end permCache[n] = s end return s end local function ringSlotFn(k, n, th, now) local p = ((k - 1) * permStep(n)) % n + 1 return (SHAPES[config.shape] or SHAPES["Ring"])(p, n, th, now, config.radius, config.height) end local ringSwarm = Swarm.new("ring", { getCenter = ringCenter, slot = ringSlotFn, spin = function() return config.spin end, speed = function() return config.strength end, snap = function() return config.snap end, tidy = function() return config.tidy end, reclaim = function() return config.reclaim end, light = function() return config.featherweight end, antisleep = function() return config.antiSleep end, }) local function ringInit() ringSwarm:reset() ringSwarm.layoutN = config.density -- fixed number of slots: the shape is always complete ringSwarm.frozen = true -- parts can only ever fill empty slots table.clear(returns) lastScan = 0 end local function ringScan(now) local c = ringCenter() if not c then return end for part, t in pairs(ringSwarm.cool) do if t < now or not part.Parent then ringSwarm.cool[part] = nil end end local free = ringSwarm:freeSlots() if #free > 0 then local batch = math.min(#free, 80) -- fill up to 80 slots per scan local cands = collectLoose(c, config.range, config.maxSize, batch, function(p) return ringSwarm:canGrab(p, now) end) local used = {} for fi = 1, batch do if #cands == 0 then break end local k = free[fi] local target = c + ringSlotFn(k, ringSwarm.layoutN, ringSwarm.theta, now) local best, bi, bd for i, p in ipairs(cands) do if not used[i] then local d = (p.Position - target).Magnitude if not bd or d < bd then best, bi, bd = p, i, d end end end if best then used[bi] = true ringSwarm:add(best, k) end end end ringStats.lost = ringSwarm.lost if ringSwarm.lost - seenLost >= 3 and now - lostToastAt > 8 then toast((ringSwarm.lost - seenLost) .. " parts were taken by someone else - swapping in new ones") seenLost = ringSwarm.lost lostToastAt = now end ringStats.n = ringSwarm.count ringStats.owned, ringStats.known = ringSwarm:controlled() end local function ringStep(dt, now) if ringRebuildAt and now >= ringRebuildAt then ringRebuildAt = nil ringInit() toast("Ring rebuilt", "good") end -- parts coming home after a launch go back to the exact slot they left for part, r in pairs(returns) do if not part.Parent or now > r.expire then returns[part] = nil ringSwarm.lent[r.slot] = nil elseif now >= r.at and not PM.owner[part] and not part.Anchored then if ringSwarm:add(part, r.slot) then returns[part] = nil end end end if now - lastScan >= config.scanRate then lastScan = now ringScan(now) end ringSwarm:step(dt, now) end function setRing(on) ringOn = on if on then ringInit() else ringSwarm:reset() -- the ONLY time the ring lets go of everything table.clear(returns) ringRebuildAt = nil ringStats.n, ringStats.owned = 0, 0 end syncToggle("ring", on) refreshStatus() end -- Part Launcher ----------------------------------------------------------- local launch = { mode = "Strike", speed = 300, range = 600, hold = 0.5, maxParts = 400, useRing = true, returnToRing = true, returnDelay = 1.5, } local LAUNCH_MODES = { "Strike", "Gather", "Vortex", "Slam" } local LAUNCH_INFO = { Strike = "Parts fly straight at the spot, hit it and carry on with their momentum. Nothing is parked there.", Gather = "Parts swarm to the spot, pause for the hold time, then get released with a small burst.", Vortex = "Parts spin up into a tornado at the spot, then get flung outward when the hold ends.", Slam = "Parts gather high above the spot, then crash down onto it.", } local sessions = {} local launcherWanted = false local aimParams = RaycastParams.new() aimParams.FilterType = Enum.RaycastFilterType.Exclude local function clusterSlot(i) local a = i * 2.39996 local r = 0.7 * math.sqrt(i) return Vector3.new(math.cos(a) * r, (i % 4) * 0.5, math.sin(a) * r) end -- a part from the ring flies home after the launch local function scheduleReturn(part, slot, now) if slot and launch.returnToRing and ringOn and part.Parent then local at = now + launch.returnDelay returns[part] = { slot = slot, at = at, expire = at + 12 } ringSwarm.lent[slot] = at + 13 end end -- parts home in on the target and are let go the moment they reach it (momentum is kept) local function startStrike(list, target, now, mul) local entries = {} for i, e in ipairs(list) do if PM.claim(e.part, "launch") then e.part.CanCollide = false entries[#entries + 1] = { part = e.part, slot = e.slot, off = clusterSlot(i) * 1.6 } end end if #entries > 0 then sessions[#sessions + 1] = { kind = "strike", entries = entries, target = target, t0 = now, mul = mul or 1 } end end local function finishSession(s) if s.swarm then s.swarm:releaseAll() else for _, e in ipairs(s.entries) do PM.release(e.part) end end end local function finishSwarm(s, now) local parts = s.swarm:list() s.swarm:releaseAll() if s.mode == "Slam" then local entries = {} for _, p in ipairs(parts) do entries[#entries + 1] = { part = p, slot = s.slots[p] } end startStrike(entries, s.target, now, 1.6) return end for _, p in ipairs(parts) do if p.Parent then local radial = p.Position - s.center radial = Vector3.new(radial.X, 0, radial.Z) local r = radial.Magnitude > 0.1 and radial.Unit or Vector3.xAxis if s.mode == "Vortex" then p.AssemblyLinearVelocity = Vector3.yAxis:Cross(r) * launch.speed * 0.35 + Vector3.new(0, 25, 0) else p.AssemblyLinearVelocity = r * 30 + Vector3.new(0, 20, 0) end scheduleReturn(p, s.slots[p], now) end end end local function updateSessions(dt, now) for i = #sessions, 1, -1 do local s = sessions[i] local done = false if s.kind == "swarm" then local near, n = s.swarm:step(dt, now) if n == 0 then done = true elseif s.stage == "gather" then if near >= n * 0.85 or now - s.t0 > 5 then s.stage = "hold" s.stageAt = now end elseif now - s.stageAt >= s.holdTime then finishSwarm(s, now) done = true end else for j = #s.entries, 1, -1 do local e = s.entries[j] local p = e.part if not p.Parent or p.Anchored then PM.release(p) table.remove(s.entries, j) else local d = s.target + e.off - p.Position if d.Magnitude < 8 or now - s.t0 > 2.5 then -- impact: let go, the part keeps flying / bouncing on its own PM.release(p) scheduleReturn(p, e.slot, now) table.remove(s.entries, j) else p.AssemblyLinearVelocity = d.Unit * launch.speed * s.mul end end end done = #s.entries == 0 end if done then table.remove(sessions, i) end end end local function launchAt() local root = getRoot() if not root then return end local now = os.clock() local pool = {} -- { part = , slot = (ring slot or nil) } if launch.useRing and ringOn then for _, e in ipairs(ringSwarm:lend(now + 15)) do if #pool < launch.maxParts then pool[#pool + 1] = e else ringSwarm:add(e.part, e.slot) -- over the cap: put it straight back end end end local need = launch.maxParts - #pool if need > 0 then for _, p in ipairs(collectLoose(root.Position, launch.range, config.maxSize, need)) do pool[#pool + 1] = { part = p } end end if #pool == 0 then toast("No loose parts in range", "bad") return end -- aim at the exact spot under the cursor, ignoring the parts we are about to move local filter = { LocalPlayer.Character } for _, e in ipairs(pool) do filter[#filter + 1] = e.part end aimParams.FilterDescendantsInstances = filter local ray = workspace.CurrentCamera:ScreenPointToRay(mouse.X, mouse.Y) local hit = workspace:Raycast(ray.Origin, ray.Direction * 4000, aimParams) local target = hit and (hit.Position + hit.Normal * 0.5) or (ray.Origin + ray.Direction * 300) local mode = launch.mode if mode == "Strike" then startStrike(pool, target, now, 1) else local s = { kind = "swarm", mode = mode, target = target, t0 = now, stage = "gather", stageAt = now, slots = {} } s.center = mode == "Slam" and (target + Vector3.new(0, 70, 0)) or target s.holdTime = mode == "Slam" and 0.4 or (mode == "Vortex" and math.max(launch.hold, 1.5) or launch.hold) s.swarm = Swarm.new("launch", { getCenter = function() return s.center end, slot = function(k, n, th, t) if mode == "Vortex" then return SHAPES["Tornado"](k, n, th, t, 22, 45) end return clusterSlot(k) end, spin = function() return mode == "Vortex" and 360 or 0 end, speed = function() return launch.speed end, snap = function() return 90 end, tidy = function() return false end, }) for _, e in ipairs(pool) do if s.swarm:add(e.part) then s.slots[e.part] = e.slot end end sessions[#sessions + 1] = s end toast(mode .. " - " .. #pool .. " parts", "good") end local function hasLauncher() local backpack = LocalPlayer:FindFirstChildOfClass("Backpack") return (backpack and backpack:FindFirstChild("Part Launcher")) or (LocalPlayer.Character and LocalPlayer.Character:FindFirstChild("Part Launcher")) end local function giveLauncher() launcherWanted = true syncToggle("launcher", true) local backpack = LocalPlayer:FindFirstChildOfClass("Backpack") if not backpack then return end if hasLauncher() then toast("You already have the launcher") return end local tool = new("Tool", { Name = "Part Launcher", ToolTip = "Click anywhere to send loose parts to that exact spot", RequiresHandle = false, CanBeDropped = false, }) tool.Activated:Connect(launchAt) tool.Parent = backpack toast("Part Launcher added to your hotbar", "good") end local function removeLauncher() launcherWanted = false syncToggle("launcher", false) local tool = hasLauncher() if tool then tool:Destroy() toast("Part Launcher removed") end end local function stopSessions() for _, s in ipairs(sessions) do finishSession(s) end table.clear(sessions) end -- Carpet state (declared early so Fly can see it) ------------------------- local CARPET_MODES = { "Hover", "Fly", "Air Walk" } local MODE_TEXT = { ["Hover"] = "The carpet stays put. Walk and jump on it freely. E raises it and Q lowers it, and you ride along.", ["Fly"] = "The carpet carries you. WASD flies where the camera points, E / Space up, Q / Ctrl down, Shift boosts. Walking is paused.", ["Air Walk"] = "The carpet follows your feet so you can walk on thin air. E and Q change the height.", } local carpet = { on = false, mode = "Hover", rider = "Me", visible = true, width = 14, length = 14, speed = 70, lift = 20, boost = 2, grab = 160, center = Vector3.zero, yaw = 0, vel = Vector3.zero, pad = nil, parts = {}, usingParts = false, template = nil, saved = nil, locked = false, filter = {}, } -- Fly (Superman) ------------------------------------------------------------ local fly = { on = false, speed = 70, smooth = 8, boost = 2.2, superman = true, cape = true, trailFx = true, fovFx = true, att = nil, lv = nil, ao = nil, vel = Vector3.zero, w = 0, bank = 0, lastYaw = nil, boosting = false, rig = nil, animate = nil, capeFolder = nil, capeParts = {}, trail = nil, trailAtts = {}, fov = nil, } local CAPE_SEGS = 7 local function setScriptEnabled(s, enabled) local ok = pcall(function() s.Enabled = enabled end) if not ok then pcall(function() s.Disabled = not enabled end) end end -- rotates a Motor6D around the character's own left/right axis, whatever the rig type function fly.setJoint(j, ang) if j and j.Parent then j.Transform = CFrame.fromAxisAngle(j.C0:VectorToObjectSpace(Vector3.xAxis), ang) end end function fly.cleanFx() if fly.capeFolder then fly.capeFolder:Destroy() fly.capeFolder = nil end table.clear(fly.capeParts) if fly.trail then fly.trail:Destroy() fly.trail = nil end for _, a in ipairs(fly.trailAtts) do a:Destroy() end table.clear(fly.trailAtts) local cam = workspace.CurrentCamera if cam and fly.fov then tween(cam, { FieldOfView = fly.fov }, 0.4) fly.fov = nil end if fly.rig then for _, j in pairs(fly.rig) do if j.Parent then j.Transform = CFrame.new() end end fly.rig = nil end if fly.animate then if fly.animate.Parent then setScriptEnabled(fly.animate, true) end fly.animate = nil end end function fly.buildCape() local folder = new("Folder", { Name = "MoonCape", Parent = workspace }) for i = 1, CAPE_SEGS do fly.capeParts[i] = new("Part", { Name = "Seg" .. i, Anchored = true, CanCollide = false, CanQuery = false, CanTouch = false, Material = Enum.Material.Fabric, Color = Color3.fromRGB(206, 30, 52), Size = Vector3.new(1.7 + i * 0.28, 0.1, 0.95), TopSurface = Enum.SurfaceType.Smooth, BottomSurface = Enum.SurfaceType.Smooth, Parent = folder, }) end fly.capeFolder = folder end function fly.updateCape(t, sp) local char = LocalPlayer.Character local torso = char and (char:FindFirstChild("UpperTorso") or char:FindFirstChild("Torso")) if not torso then return end local tcf = torso.CFrame local air = math.clamp(sp / 60, 0, 1) local hang = -tcf.UpVector -- towards the feet local dir = hang * 0.8 + Vector3.new(0, -0.25, 0) if sp > 2 then dir = dir + (-fly.vel.Unit) * air * 1.2 end dir = dir.Unit local base = (tcf * CFrame.new(0, torso.Size.Y * 0.42, torso.Size.Z / 2 + 0.05)).Position local away = -tcf.LookVector -- from the back, away from the body local upRef = tcf.LookVector if math.abs(dir:Dot(upRef)) > 0.97 then upRef = tcf.RightVector end for i, seg in ipairs(fly.capeParts) do local f = i / CAPE_SEGS local flutter = math.sin(t * 11 - i * 0.9) * (0.1 + 0.35 * air) * f local lift = away * (0.12 + air * 0.5 * f * f) local pos = base + dir * ((i - 0.5) * 0.85) + lift + tcf.RightVector * flutter seg.CFrame = CFrame.lookAt(pos, pos + dir, upRef) * CFrame.Angles(0, 0, flutter * 0.4) end end function fly.stop() fly.on = false for _, k in ipairs({ "lv", "ao", "att" }) do if fly[k] then fly[k]:Destroy() fly[k] = nil end end fly.vel = Vector3.zero fly.w, fly.bank, fly.lastYaw, fly.boosting = 0, 0, nil, false fly.cleanFx() local root, hum = getRoot(), getHumanoid() if root then local look = root.CFrame.LookVector local up = Vector3.new(look.X, 0, look.Z) if up.Magnitude < 0.1 then up = workspace.CurrentCamera.CFrame.LookVector * Vector3.new(1, 0, 1) end root.CFrame = CFrame.lookAt(root.Position, root.Position + up.Unit) -- stand back up root.AssemblyAngularVelocity = Vector3.zero end if hum then hum.PlatformStand = false hum:ChangeState(Enum.HumanoidStateType.GettingUp) end end function fly.start() local root, hum, char = getRoot(), getHumanoid(), LocalPlayer.Character if not (root and hum and char) then return false end fly.att = new("Attachment", { Name = "MoonFlyAtt", Parent = root }) fly.lv = new("LinearVelocity", { Name = "MoonFlyLV", Attachment0 = fly.att, MaxForce = math.huge, VelocityConstraintMode = Enum.VelocityConstraintMode.Vector, RelativeTo = Enum.ActuatorRelativeTo.World, VectorVelocity = Vector3.zero, Parent = root, }) fly.ao = new("AlignOrientation", { Name = "MoonFlyAO", Mode = Enum.OrientationAlignmentMode.OneAttachment, Attachment0 = fly.att, RigidityEnabled = false, MaxTorque = math.huge, MaxAngularVelocity = math.huge, Responsiveness = 26, CFrame = root.CFrame.Rotation, Parent = root, }) hum.PlatformStand = true fly.vel = Vector3.new(0, 26, 0) -- little take-off hop fly.w, fly.bank, fly.lastYaw = 0, 0, nil local function motor(...) for _, n in ipairs({ ... }) do local j = char:FindFirstChild(n, true) if j and j:IsA("Motor6D") then return j end end end fly.rig = { rs = motor("RightShoulder", "Right Shoulder"), ls = motor("LeftShoulder", "Left Shoulder"), rh = motor("RightHip", "Right Hip"), lh = motor("LeftHip", "Left Hip"), neck = motor("Neck"), } -- stop the default animations so our pose is not fought over local anim = char:FindFirstChild("Animate") if anim then fly.animate = anim setScriptEnabled(anim, false) end local animator = hum:FindFirstChildOfClass("Animator") if animator then for _, tr in ipairs(animator:GetPlayingAnimationTracks()) do tr:Stop(0.15) end end if fly.cape then fly.buildCape() end if fly.trailFx then local a0 = new("Attachment", { Name = "MoonTrail0", Position = Vector3.new(-1.1, 0, 0.4), Parent = root }) local a1 = new("Attachment", { Name = "MoonTrail1", Position = Vector3.new(1.1, 0, 0.4), Parent = root }) fly.trailAtts = { a0, a1 } fly.trail = new("Trail", { Attachment0 = a0, Attachment1 = a1, Lifetime = 0.5, MinLength = 0.1, LightEmission = 0.9, FaceCamera = true, Enabled = false, Color = ColorSequence.new(accent, accent2()), Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 0.2), NumberSequenceKeypoint.new(1, 1) }), WidthScale = NumberSequence.new({ NumberSequenceKeypoint.new(0, 1), NumberSequenceKeypoint.new(1, 0.15) }), Parent = root, }) end local cam = workspace.CurrentCamera fly.fov = cam and cam.FieldOfView or nil fly.on = true return true end -- physics: velocity + Superman orientation (head first, belly down, banking along the long axis) function fly.step(dt) local root, hum = getRoot(), getHumanoid() if not (fly.lv and fly.ao and root and hum) then return end local dir = cameraMoveDir(hum) + Vector3.new(0, verticalInput(true), 0) if dir.Magnitude > 1 then dir = dir.Unit end fly.boosting = UIS:IsKeyDown(Enum.KeyCode.LeftShift) and dir.Magnitude > 0.05 local top = fly.speed * (fly.boosting and fly.boost or 1) fly.vel = fly.vel:Lerp(dir * top, 1 - math.exp(-dt * fly.smooth)) fly.lv.VectorVelocity = fly.vel local sp = fly.vel.Magnitude local look = workspace.CurrentCamera.CFrame.LookVector local upright = CFrame.Angles(0, math.atan2(-look.X, -look.Z), 0) local cf = upright local wT = (fly.superman and sp > 6) and math.clamp((sp - 6) / 30, 0, 1) or 0 fly.w = fly.w + (wT - fly.w) * (1 - math.exp(-dt * 7)) if fly.w > 0.01 and sp > 1 then local U = fly.vel.Unit local L = Vector3.new(0, -1, 0) + U * U.Y -- belly faces down (or forward when going straight up) if L.Magnitude < 0.15 then L = upright.LookVector end L = L.Unit local flight = CFrame.fromMatrix(Vector3.zero, U:Cross(-L), U, -L) local heading = math.atan2(-fly.vel.X, -fly.vel.Z) if fly.lastYaw then local d = (heading - fly.lastYaw + math.pi) % TAU - math.pi local want = math.clamp(-(d / math.max(dt, 1e-3)) * 0.22, -0.6, 0.6) * math.clamp(sp / top, 0, 1) fly.bank = fly.bank + (want - fly.bank) * (1 - math.exp(-dt * 5)) end fly.lastYaw = heading cf = upright:Lerp(flight * CFrame.Angles(0, fly.bank, 0), fly.w) else fly.bank = fly.bank * 0.9 fly.lastYaw = nil end fly.ao.CFrame = cf end -- visuals: pose, cape, trail, camera (runs every rendered frame) function fly.visuals(dt) if not fly.on then return end local t = os.clock() local w = fly.w local sp = fly.vel.Magnitude local rig = fly.rig if rig then local idle = math.sin(t * 1.7) * 0.1 * (1 - w) fly.setJoint(rig.rs, math.rad(175) * w + idle) -- right arm stretched out front fly.setJoint(rig.ls, -math.rad(18) * w - idle) -- left fist back at the hip local kick = math.sin(t * 10) * 0.09 * w fly.setJoint(rig.rh, 0.12 * w + kick) fly.setJoint(rig.lh, 0.12 * w - kick) fly.setJoint(rig.neck, math.rad(52) * w) -- look forward, not at the ground end if fly.capeFolder then fly.updateCape(t, sp) end if fly.trail then fly.trail.Enabled = sp > 35 fly.trail.Color = ColorSequence.new(accent, accent2()) end local cam = workspace.CurrentCamera if fly.fovFx and fly.fov and cam then local target = fly.fov + math.clamp(sp / (fly.speed * fly.boost), 0, 1) * 12 + (fly.boosting and 8 or 0) cam.FieldOfView = cam.FieldOfView + (target - cam.FieldOfView) * (1 - math.exp(-dt * 5)) end end function setFly(on) if on then if carpet.on and carpet.mode == "Fly" and carpet.rider == "Me" then toast("Carpet Fly is active", "bad") syncToggle("fly", false) return end if not fly.start() then toast("No character yet", "bad") syncToggle("fly", false) return end else fly.stop() end syncToggle("fly", on) refreshStatus() end -- Carpet ------------------------------------------------------------------ local rayParams = RaycastParams.new() rayParams.FilterType = Enum.RaycastFilterType.Exclude rayParams.RespectCanCollide = true local function hipTotal(hum, root) local hip = hum.RigType == Enum.HumanoidRigType.R6 and 2 or hum.HipHeight return hip + root.Size.Y / 2 end local function lockHumanoid(on) local hum = getHumanoid() if not hum then return end if on then if not carpet.saved then carpet.saved = { ws = hum.WalkSpeed, jp = hum.JumpPower, jh = hum.JumpHeight, ar = hum.AutoRotate } end hum.WalkSpeed = 0 hum.JumpPower = 0 hum.JumpHeight = 0 hum.AutoRotate = false carpet.locked = true elseif carpet.saved then hum.WalkSpeed = carpet.saved.ws hum.JumpPower = carpet.saved.jp hum.JumpHeight = carpet.saved.jh hum.AutoRotate = carpet.saved.ar carpet.saved = nil carpet.locked = false end end local function makeLocalPad() local p if carpet.template then p = carpet.template:Clone() if p then for _, d in ipairs(p:GetDescendants()) do if d:IsA("BaseScript") or d:IsA("BodyMover") or d:IsA("Constraint") or d:IsA("JointInstance") or d:IsA("Sound") or d:IsA("ClickDetector") or d:IsA("ProximityPrompt") then d:Destroy() end end end end if not p then p = Instance.new("Part") p.Material = Enum.Material.Fabric p.Color = accent p.TopSurface = Enum.SurfaceType.Smooth p.BottomSurface = Enum.SurfaceType.Smooth p.Size = Vector3.new(carpet.width, 0.8, carpet.length) end if p:IsA("Seat") or p:IsA("VehicleSeat") then p.Disabled = true end if not (p:IsA("MeshPart") or p:IsA("UnionOperation")) then p.Size = Vector3.new(carpet.width, math.clamp(p.Size.Y, 0.4, 2), carpet.length) end if p.Transparency >= 0.95 then p.Transparency = 0.4 end p.Name = "MoonCarpet" p.Anchored = true p.CanCollide = true p.CanTouch = false p.CanQuery = true p.Locked = true p.Parent = workspace return p end -- loose parts laid flat in a grid act as a carpet that everyone can see local function borrowParts(root, selfRider) local cands = collectLoose(root.Position, selfRider and carpet.grab or carpet.grab * 1.6, math.huge, 600, function(p) return p.CanCollide and p.Size.X * p.Size.Z >= 4 end) if #cands == 0 then return false end table.sort(cands, function(a, b) return a.Size.X * a.Size.Z > b.Size.X * b.Size.Z end) local need = carpet.width * carpet.length * 0.8 local picked, covered = {}, 0 for _, p in ipairs(cands) do if #picked >= 12 or covered >= need then break end picked[#picked + 1] = p covered = covered + p.Size.X * p.Size.Z end local n = #picked local cols = math.ceil(math.sqrt(n)) local rows = math.ceil(n / cols) local spacing = math.clamp(math.sqrt(covered / n) * 1.05, 3, 30) for k, p in ipairs(picked) do if PM.claim(p, "carpet") then local i = k - 1 local col, rw = i % cols, math.floor(i / cols) carpet.parts[#carpet.parts + 1] = { part = p, off = Vector3.new((col - (cols - 1) / 2) * spacing, 0, (rw - (rows - 1) / 2) * spacing), } if not selfRider then p.CanCollide = false -- local only: stops the parts bumping you end end end return #carpet.parts > 0 end local function renderPad(cf) if carpet.usingParts then local base = cf.Position for i = #carpet.parts, 1, -1 do local e = carpet.parts[i] local p = e.part if p.Parent and not p.Anchored then p.CFrame = CFrame.new(base + e.off - Vector3.new(0, p.Size.Y / 2, 0)) p.AssemblyLinearVelocity = carpet.vel p.AssemblyAngularVelocity = Vector3.zero else PM.release(p) table.remove(carpet.parts, i) end end elseif carpet.pad then carpet.pad.CFrame = cf * CFrame.new(0, -carpet.pad.Size.Y / 2, 0) end end local function carpetStop() carpet.on = false for _, e in ipairs(carpet.parts) do PM.release(e.part) end table.clear(carpet.parts) if carpet.pad then carpet.pad:Destroy() carpet.pad = nil end carpet.usingParts = false carpet.vel = Vector3.zero lockHumanoid(false) end local function carpetStart() carpetStop() local target = carpet.rider == "Me" and LocalPlayer or Players:FindFirstChild(carpet.rider) if not target then toast("That player isn't here", "bad") return false end local selfRider = target == LocalPlayer local root, hum = getRoot(), getHumanoid() local tChar = target.Character local tRoot = tChar and tChar:FindFirstChild("HumanoidRootPart") local tHum = tChar and tChar:FindFirstChildOfClass("Humanoid") if not (root and hum and tRoot and tHum) then toast("No character yet", "bad") return false end if carpet.visible or not selfRider then carpet.usingParts = borrowParts(root, selfRider) if not carpet.usingParts then if not selfRider then toast("No loose parts nearby to use as a carpet", "bad") return false end toast("No loose parts in range - local carpet only") end end if not carpet.usingParts then carpet.pad = makeLocalPad() end local feetY = tRoot.Position.Y - hipTotal(tHum, tRoot) carpet.yaw = 0 if selfRider then if not carpet.usingParts then local look = root.CFrame.LookVector carpet.yaw = math.atan2(-look.X, -look.Z) end carpet.center = Vector3.new(tRoot.Position.X, feetY + 1.5, tRoot.Position.Z) root.CFrame = root.CFrame + Vector3.new(0, 1.6, 0) root.AssemblyLinearVelocity = Vector3.zero else carpet.center = Vector3.new(tRoot.Position.X, feetY - 0.05, tRoot.Position.Z) end carpet.vel = Vector3.zero carpet.filter = { LocalPlayer.Character, tChar } if carpet.pad then carpet.filter[#carpet.filter + 1] = carpet.pad end for _, e in ipairs(carpet.parts) do carpet.filter[#carpet.filter + 1] = e.part end carpet.on = true renderPad(CFrame.new(carpet.center) * CFrame.Angles(0, carpet.yaw, 0)) return true end -- keeps the carpet and its rider from being driven through walls and floors local function clampStep(origin, step, extent) local dist = step.Magnitude if dist < 1e-4 then return step end rayParams.FilterDescendantsInstances = carpet.filter local res = workspace:Raycast(origin, step.Unit * (dist + extent), rayParams) if res then local into = step:Dot(res.Normal) if into < 0 then step = step - res.Normal * into end end return step end local function onPad(root, hip) local p = (CFrame.new(carpet.center) * CFrame.Angles(0, carpet.yaw, 0)):PointToObjectSpace(root.Position) return math.abs(p.X) <= carpet.width / 2 + 1.5 and math.abs(p.Z) <= carpet.length / 2 + 1.5 and p.Y > -1 and p.Y < hip + 6 end local function carpetStep(dt) local target = carpet.rider == "Me" and LocalPlayer or Players:FindFirstChild(carpet.rider) local tChar = target and target.Character local tRoot = tChar and tChar:FindFirstChild("HumanoidRootPart") local tHum = tChar and tChar:FindFirstChildOfClass("Humanoid") local myHum = getHumanoid() if not (tRoot and tHum and myHum) or myHum.Health <= 0 then return end local selfRider = target == LocalPlayer local mode = carpet.mode local hip = hipTotal(tHum, tRoot) local vy = verticalInput(mode == "Fly") local oldCF = CFrame.new(carpet.center) * CFrame.Angles(0, carpet.yaw, 0) local rel = oldCF:ToObjectSpace(tRoot.CFrame) -- where the rider sits on the carpet local delta local lift = 0 if mode == "Fly" then if not carpet.locked then lockHumanoid(true) end local want = cameraMoveDir(myHum) + Vector3.new(0, vy, 0) if want.Magnitude > 1 then want = want.Unit end local boost = UIS:IsKeyDown(Enum.KeyCode.LeftShift) and carpet.boost or 1 carpet.vel = carpet.vel:Lerp(want * carpet.speed * boost, 1 - math.exp(-dt * 6)) delta = carpet.vel * dt else if carpet.locked then lockHumanoid(false) end lift = vy * carpet.lift * dt delta = Vector3.new(0, lift, 0) carpet.vel = Vector3.new(0, vy * carpet.lift, 0) if mode == "Air Walk" then carpet.center = Vector3.new(tRoot.Position.X, carpet.center.Y, tRoot.Position.Z) end end if mode == "Fly" and delta.Magnitude > 1e-4 then local hor = Vector3.new(delta.X, 0, delta.Z) if hor.Magnitude > 1e-4 then hor = clampStep(carpet.center, hor, math.max(carpet.width, carpet.length) / 2) end local ver = Vector3.new(0, delta.Y, 0) if math.abs(delta.Y) > 1e-4 then ver = clampStep(tRoot.Position, ver, delta.Y > 0 and 3.4 or (hip + 1.1)) end delta = hor + ver carpet.vel = delta / dt elseif lift ~= 0 then local ext = lift > 0 and 3.4 or (hip + 1.1) delta = clampStep(tRoot.Position, delta, ext) lift = delta.Y end carpet.center = carpet.center + delta if mode == "Fly" and not carpet.usingParts then local flat = Vector3.new(carpet.vel.X, 0, carpet.vel.Z) if flat.Magnitude > 5 then carpet.yaw = lerpAngle(carpet.yaw, math.atan2(-flat.X, -flat.Z), 1 - math.exp(-dt * 3)) end end local newCF = CFrame.new(carpet.center) * CFrame.Angles(0, carpet.yaw, 0) renderPad(newCF) if selfRider then if mode == "Fly" then tRoot.CFrame = newCF * rel tRoot.AssemblyLinearVelocity = Vector3.zero elseif lift ~= 0 and (mode == "Air Walk" or onPad(tRoot, hip)) then tRoot.CFrame = tRoot.CFrame + Vector3.new(0, lift, 0) end end if carpet.usingParts and #carpet.parts == 0 then toast("The carpet's parts are gone", "bad") setCarpet(false) end end function setCarpet(on) if on then if carpet.mode == "Fly" and carpet.rider == "Me" and fly.on then setFly(false) end if not carpetStart() then syncToggle("carpet", false) refreshStatus() return end toast("Carpet on - " .. carpet.mode, "good") else carpetStop() end syncToggle("carpet", on) refreshStatus() end local function setCarpetMode(m) carpet.mode = m carpet.vel = Vector3.zero if m == "Fly" and carpet.rider == "Me" and fly.on then setFly(false) end if UI.modeInfo then UI.modeInfo.Text = MODE_TEXT[m] end end -- Carpet look picker (local carpet only) local pickerHighlight = new("Highlight", { FillTransparency = 0.6, Enabled = false, Parent = workspace }) S(pickerHighlight, { OutlineColor = "accent", FillColor = "accent" }) local picking = false local function refreshLocalPad() if not (carpet.on and carpet.pad) then return end local oldTop = carpet.center.Y carpet.pad:Destroy() carpet.pad = makeLocalPad() carpet.center = Vector3.new(carpet.center.X, oldTop, carpet.center.Z) carpet.filter[3] = carpet.pad renderPad(CFrame.new(carpet.center) * CFrame.Angles(0, carpet.yaw, 0)) end local function resizeLocalPad() local p = carpet.pad if p and not (p:IsA("MeshPart") or p:IsA("UnionOperation")) then p.Size = Vector3.new(carpet.width, p.Size.Y, carpet.length) end end -- the one physics loop -------------------------------------------------------- on(RunService.PreSimulation, function(dt) local now = os.clock() if fly.on and not guard("fly", fly.step, dt) then setFly(false) toast("Fly hit an error and was turned off", "bad") end if carpet.on and not guard("carpet", carpetStep, dt) then setCarpet(false) toast("Carpet hit an error and was turned off", "bad") end if ringOn and not guard("ring", ringStep, dt, now) then setRing(false) toast("Ring hit an error and was turned off", "bad") end if #sessions > 0 then guard("launcher", updateSessions, dt, now) end end) -- pose / cape / camera run after animations, every rendered frame on(RunService.RenderStepped, function(dt) if fly.on then guard("fly visuals", fly.visuals, dt) end end) -- GUI lives in its own function (keeps the main chunk under Luau's 200-local limit) local function buildUI() -- UI ------------------------------------------------------------------------ local W = {} -- widget factory (kept in a table so the script stays under Luau's local limit) W.activeSlider = nil local gui = new("ScreenGui", { Name = "MoonsFERing", ResetOnSpawn = false, ZIndexBehavior = Enum.ZIndexBehavior.Sibling, DisplayOrder = 100, IgnoreGuiInset = true, Parent = playerGui, }) -- toasts, popup and touch buttons live here so they stay visible when the panel is hidden local overlay = new("ScreenGui", { Name = "MoonsFERingOverlay", ResetOnSpawn = false, ZIndexBehavior = Enum.ZIndexBehavior.Sibling, DisplayOrder = 101, IgnoreGuiInset = true, Parent = playerGui, }) function W.T(props, key) local p = { BackgroundTransparency = 1, Font = Enum.Font.GothamMedium, TextSize = 12, TextXAlignment = Enum.TextXAlignment.Left, } for k, v in pairs(props) do p[k] = v end return S(new("TextLabel", p), { TextColor3 = key or "text" }) end -- drawn icons (no font glyphs, so nothing can turn into a box) -------------------- -- kinds: home ring launch fly carpet settings x minus dots chevdown chevup check info bang function W.icon(parent, kind, size) local holder = new("Frame", { Size = UDim2.fromOffset(size, size), AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(0.5, 0.5), BackgroundTransparency = 1, Parent = parent, }) local items = {} local function shape(x, y, w, h, rot, round, outline) local f = new("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(x, y), Size = UDim2.fromScale(w, h), Rotation = rot or 0, BorderSizePixel = 0, BackgroundColor3 = WHITE, BackgroundTransparency = outline and 1 or 0, Parent = holder, }) if round then new("UICorner", { CornerRadius = UDim.new(round, 0), Parent = f }) end local st if outline then st = new("UIStroke", { Thickness = math.max(1.5, size / 9), Color = WHITE, ApplyStrokeMode = Enum.ApplyStrokeMode.Border, Parent = f }) end items[#items + 1] = { f = f, st = st } end if kind == "home" then shape(0.28, 0.28, 0.38, 0.38, 0, 0.25) shape(0.72, 0.28, 0.38, 0.38, 0, 0.25) shape(0.28, 0.72, 0.38, 0.38, 0, 0.25) shape(0.72, 0.72, 0.38, 0.38, 0, 0.5) elseif kind == "ring" then shape(0.5, 0.5, 0.78, 0.78, 0, 0.5, true) shape(0.5, 0.11, 0.24, 0.24, 0, 0.5) shape(0.84, 0.7, 0.24, 0.24, 0, 0.5) shape(0.16, 0.7, 0.24, 0.24, 0, 0.5) elseif kind == "launch" then shape(0.5, 0.5, 0.66, 0.66, 0, 0.5, true) shape(0.5, 0.5, 0.12, 1, 0, 0) shape(0.5, 0.5, 1, 0.12, 0, 0) shape(0.5, 0.5, 0.24, 0.24, 0, 0.5) elseif kind == "fly" then shape(0.5, 0.58, 0.14, 0.7, 0, 0.5) shape(0.32, 0.3, 0.14, 0.5, 45, 0.5) shape(0.68, 0.3, 0.14, 0.5, -45, 0.5) elseif kind == "carpet" then shape(0.5, 0.5, 0.92, 0.62, 0, 0.22, true) shape(0.5, 0.5, 0.5, 0.12, 0, 0.5) elseif kind == "settings" then shape(0.5, 0.22, 0.86, 0.1, 0, 0.5) shape(0.5, 0.5, 0.86, 0.1, 0, 0.5) shape(0.5, 0.78, 0.86, 0.1, 0, 0.5) shape(0.3, 0.22, 0.26, 0.26, 0, 0.5) shape(0.68, 0.5, 0.26, 0.26, 0, 0.5) shape(0.4, 0.78, 0.26, 0.26, 0, 0.5) elseif kind == "x" then shape(0.5, 0.5, 0.14, 0.95, 45, 0.5) shape(0.5, 0.5, 0.14, 0.95, -45, 0.5) elseif kind == "minus" then shape(0.5, 0.5, 0.9, 0.14, 0, 0.5) elseif kind == "dots" then shape(0.15, 0.5, 0.2, 0.2, 0, 0.5) shape(0.5, 0.5, 0.2, 0.2, 0, 0.5) shape(0.85, 0.5, 0.2, 0.2, 0, 0.5) elseif kind == "chevdown" then shape(0.34, 0.5, 0.16, 0.6, -45, 0.5) shape(0.66, 0.5, 0.16, 0.6, 45, 0.5) elseif kind == "chevup" then shape(0.34, 0.5, 0.16, 0.6, 45, 0.5) shape(0.66, 0.5, 0.16, 0.6, -45, 0.5) elseif kind == "check" then shape(0.36, 0.62, 0.16, 0.38, -45, 0.5) shape(0.62, 0.5, 0.16, 0.7, 45, 0.5) elseif kind == "bang" then shape(0.5, 0.36, 0.18, 0.5, 0, 0.5) shape(0.5, 0.82, 0.2, 0.2, 0, 0.5) else -- info shape(0.5, 0.2, 0.2, 0.2, 0, 0.5) shape(0.5, 0.64, 0.18, 0.52, 0, 0.5) end local api = { Frame = holder } function api.Set(color) for _, it in ipairs(items) do if it.st then it.st.Color = color else it.f.BackgroundColor3 = color end end end return api end -- live shape preview: draws the current ring shape so you can see what the ring will look like local PREVIEW_DOTS = 90 W.previews = {} local previewBounds = { key = nil } function W.preview(parent, h, tabName) local f = new("Frame", { Size = UDim2.new(1, 0, 0, h), BorderSizePixel = 0, ClipsDescendants = true, Parent = parent, }, { corner(12), stroke("stroke", 1) }) S(f, { BackgroundColor3 = "bg" }) local dots = {} for i = 1, PREVIEW_DOTS do dots[i] = new("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Size = UDim2.fromOffset(5, 5), BorderSizePixel = 0, BackgroundColor3 = WHITE, Visible = false, Parent = f, }, { corner(4) }) end W.previews[#W.previews + 1] = { frame = f, dots = dots, tab = tabName } return f end function W.updatePreviews(now, activeTab) local n = math.min(config.density, PREVIEW_DOTS) local shapeFn = SHAPES[config.shape] or SHAPES["Ring"] local ct, st = math.cos(0.9), math.sin(0.9) local key = config.shape .. ":" .. config.radius .. ":" .. config.height .. ":" .. n if previewBounds.key ~= key then local b = { key = key, minX = math.huge, maxX = -math.huge, minY = math.huge, maxY = -math.huge } for _, th in ipairs({ 0, 1.57, 3.14, 4.71 }) do for i = 1, n do local o = shapeFn(i, n, th, 0, config.radius, config.height) local px, py = o.X, -(o.Y * ct - o.Z * st) b.minX, b.maxX = math.min(b.minX, px), math.max(b.maxX, px) b.minY, b.maxY = math.min(b.minY, py), math.max(b.maxY, py) end end previewBounds = b end local b = previewBounds local theta = ringOn and ringSwarm.theta or (now * math.rad(config.spin)) local a1, a2 = accent, accent2() local ext = config.radius * 1.2 + 1 for _, rec in ipairs(W.previews) do local size = rec.frame.AbsoluteSize if rec.tab == activeTab and size.X > 20 then local sc = math.min(size.X * 0.8 / (b.maxX - b.minX + 1e-3), size.Y * 0.8 / (b.maxY - b.minY + 1e-3)) local cx, cy = (b.minX + b.maxX) / 2, (b.minY + b.maxY) / 2 for i, d in ipairs(rec.dots) do if i <= n then local o = shapeFn(i, n, theta, now, config.radius, config.height) local depth = math.clamp((o.Z * ct + o.Y * st) / ext * 0.5 + 0.5, 0, 1) local px, py = o.X, -(o.Y * ct - o.Z * st) local s = 3 + depth * 4 d.Visible = true d.Size = UDim2.fromOffset(s, s) d.Position = UDim2.fromOffset(size.X / 2 + (px - cx) * sc, size.Y / 2 + (py - cy) * sc) d.BackgroundColor3 = a1:Lerp(a2, i / n) d.BackgroundTransparency = 0.5 - depth * 0.45 else d.Visible = false end end end end end function W.pageHeader(parent, title, sub) local h = new("Frame", { Size = UDim2.new(1, 0, 0, 46), BackgroundTransparency = 1, Parent = parent }) W.T({ Size = UDim2.new(1, 0, 0, 26), Text = title, Font = Enum.Font.GothamBold, TextSize = 22, Parent = h }) W.T({ Size = UDim2.new(1, 0, 0, 16), Position = UDim2.fromOffset(0, 28), Text = sub or "", Font = Enum.Font.Gotham, TextSize = 12, Parent = h }, "dim") end -- crescent moon logo (drawn, no font glyph) function W.moon(parent, size) local holder = new("Frame", { Size = UDim2.fromOffset(size, size), AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(0.5, 0.5), BackgroundTransparency = 1, Parent = parent, }) new("Frame", { Size = UDim2.fromScale(0.6, 0.6), Position = UDim2.fromScale(0.15, 0.22), BackgroundColor3 = WHITE, BorderSizePixel = 0, Parent = holder, }, { corner(size) }) local bite = new("Frame", { Size = UDim2.fromScale(0.52, 0.52), Position = UDim2.fromScale(0.36, 0.1), BackgroundColor3 = accent, BorderSizePixel = 0, Parent = holder, }, { corner(size) }) new("Frame", { Size = UDim2.fromScale(0.11, 0.11), Position = UDim2.fromScale(0.72, 0.58), BackgroundColor3 = WHITE, BorderSizePixel = 0, Parent = holder, }, { corner(size) }) new("Frame", { Size = UDim2.fromScale(0.07, 0.07), Position = UDim2.fromScale(0.62, 0.2), BackgroundColor3 = WHITE, BackgroundTransparency = 0.2, BorderSizePixel = 0, Parent = holder, }, { corner(size) }) hook(holder, function() bite.BackgroundColor3 = accent:Lerp(accent2(), 0.5) end) return holder end function W.card(parent, title, sub) local f = new("Frame", { Size = UDim2.new(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, BorderSizePixel = 0, Parent = parent, }, { corner(14), pad(14), list(9), stroke("stroke", 1), new("UIGradient", { Rotation = 90, Color = ColorSequence.new(WHITE, Color3.fromRGB(214, 214, 224)) }), }) S(f, { BackgroundColor3 = "surface" }) if title then local head = new("Frame", { Size = UDim2.new(1, 0, 0, 18), BackgroundTransparency = 1, Parent = f }) local bar = new("Frame", { Size = UDim2.fromOffset(3, 12), Position = UDim2.fromOffset(0, 3), BackgroundColor3 = WHITE, BorderSizePixel = 0, Parent = head, }, { corner(2) }) S(new("UIGradient", { Rotation = 90, Parent = bar }), { Color = "accentSeq" }) W.T({ Size = UDim2.new(0.6, -12, 1, 0), Position = UDim2.fromOffset(12, 0), Text = string.upper(title), Font = Enum.Font.GothamBold, TextSize = 11, Parent = head, }, "sub") if sub then W.T({ Size = UDim2.new(0.4, 0, 1, 0), Position = UDim2.new(0.6, 0, 0, 0), Text = sub, Font = Enum.Font.Gotham, TextSize = 11, TextXAlignment = Enum.TextXAlignment.Right, Parent = head, }, "dim") end end return f end function W.row(parent, h) local f = new("Frame", { Size = UDim2.new(1, 0, 0, h), BorderSizePixel = 0, Parent = parent }, { corner(10) }) return S(f, { BackgroundColor3 = "surface2" }) end function W.info(parent, text) local f = new("Frame", { Size = UDim2.new(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, BorderSizePixel = 0, Parent = parent, }, { corner(10), pad(9, 10, 9, 20) }) S(f, { BackgroundColor3 = "bg" }) S(new("Frame", { Size = UDim2.new(0, 3, 1, -16), Position = UDim2.fromOffset(9, 8), BorderSizePixel = 0, Parent = f, }, { corner(2) }), { BackgroundColor3 = "accent" }) return W.T({ Size = UDim2.new(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, Text = text, Font = Enum.Font.Gotham, TextWrapped = true, TextYAlignment = Enum.TextYAlignment.Top, Parent = f, }, "sub") end function W.keyRow(parent, key, desc) local r = new("Frame", { Size = UDim2.new(1, 0, 0, 26), BackgroundTransparency = 1, Parent = parent }) W.T({ Size = UDim2.new(1, -110, 1, 0), Text = desc, Font = Enum.Font.Gotham, Parent = r }, "sub") S(new("TextLabel", { Size = UDim2.fromOffset(0, 22), AutomaticSize = Enum.AutomaticSize.X, AnchorPoint = Vector2.new(1, 0.5), Position = UDim2.new(1, 0, 0.5, 0), Text = key, Font = Enum.Font.GothamBold, TextSize = 11, Parent = r, }, { corner(6), pad(0, 9), stroke("stroke", 1) }), { BackgroundColor3 = "surface3", TextColor3 = "text" }) end -- big number tile for the dashboard; returns the value label function W.statTile(parent, label) local f = new("Frame", { BorderSizePixel = 0, Parent = parent }, { corner(12), stroke("stroke", 1) }) S(f, { BackgroundColor3 = "surface2" }) local v = W.T({ Size = UDim2.new(1, -20, 0, 30), Position = UDim2.fromOffset(12, 10), Text = "0", Font = Enum.Font.GothamBold, TextSize = 24, Parent = f, }, "accent") W.T({ Size = UDim2.new(1, -20, 0, 14), Position = UDim2.fromOffset(12, 44), Text = label, Font = Enum.Font.Gotham, Parent = f }, "dim") return v end function W.statRow(parent, text) local r = W.row(parent, 32) W.T({ Size = UDim2.new(0.6, 0, 1, 0), Position = UDim2.fromOffset(12, 0), Text = text, Parent = r }, "sub") return W.T({ Size = UDim2.new(0.4, -12, 1, 0), Position = UDim2.new(0.6, 0, 0, 0), Text = "0", Font = Enum.Font.GothamBold, TextSize = 13, TextXAlignment = Enum.TextXAlignment.Right, Parent = r, }, "accent") end function W.toggle(parent, text, desc, default, callback, gearFn) local state = default and true or false local r = W.row(parent, desc and 54 or 42) local rightPad = 74 + (gearFn and 34 or 0) W.T({ Size = UDim2.new(1, -rightPad, 0, 18), Position = UDim2.fromOffset(12, desc and 9 or 12), Text = text, TextSize = 13, Parent = r }) if desc then W.T({ Size = UDim2.new(1, -rightPad, 0, 14), Position = UDim2.fromOffset(12, 29), Text = desc, Font = Enum.Font.Gotham, TextSize = 11, TextTruncate = Enum.TextTruncate.AtEnd, Parent = r, }, "dim") end local hit = new("TextButton", { Size = UDim2.fromScale(1, 1), BackgroundTransparency = 1, Text = "", Parent = r }) if gearFn then local gear = new("TextButton", { Size = UDim2.fromOffset(30, 26), AnchorPoint = Vector2.new(1, 0.5), Position = UDim2.new(1, -62, 0.5, 0), Text = "", AutoButtonColor = false, Parent = r, }, { corner(8) }) S(gear, { BackgroundColor3 = "surface3" }) local gearIcon = W.icon(gear, "dots", 16) hook(gear, function() gearIcon.Set(accent) end) gear.MouseEnter:Connect(function() tween(gear, { BackgroundColor3 = theme.off }, 0.1) end) gear.MouseLeave:Connect(function() tween(gear, { BackgroundColor3 = theme.surface3 }, 0.1) end) gear.MouseButton1Click:Connect(function() playClick() gearFn() end) end local track = new("Frame", { Size = UDim2.fromOffset(44, 24), AnchorPoint = Vector2.new(1, 0.5), Position = UDim2.new(1, -12, 0.5, 0), BorderSizePixel = 0, Parent = r, }, { corner(12) }) S(track, { BackgroundColor3 = "off" }) local glow = S(new("UIStroke", { Thickness = 2, Transparency = state and 0.35 or 1, ApplyStrokeMode = Enum.ApplyStrokeMode.Border, }), { Color = "accent" }) glow.Parent = track local fill = new("Frame", { Size = UDim2.fromScale(1, 1), BackgroundColor3 = WHITE, BackgroundTransparency = state and 0 or 1, BorderSizePixel = 0, Parent = track, }, { corner(12) }) S(new("UIGradient", { Parent = fill }), { Color = "accentSeq" }) local knob = new("Frame", { Size = UDim2.fromOffset(18, 18), AnchorPoint = Vector2.new(0, 0.5), Position = UDim2.new(0, state and 23 or 3, 0.5, 0), BackgroundColor3 = WHITE, BorderSizePixel = 0, Parent = track, }, { corner(9) }) local api = {} function api.Set(v, silent) state = v and true or false tween(knob, { Position = UDim2.new(0, state and 23 or 3, 0.5, 0) }, 0.22, Enum.EasingStyle.Back) tween(fill, { BackgroundTransparency = state and 0 or 1 }, 0.18) tween(glow, { Transparency = state and 0.35 or 1 }, 0.2) if not silent then callback(state) end end function api.Get() return state end hit.MouseEnter:Connect(function() tween(r, { BackgroundColor3 = theme.surface3 }, 0.12) end) hit.MouseLeave:Connect(function() tween(r, { BackgroundColor3 = theme.surface2 }, 0.12) end) hit.MouseButton1Click:Connect(function() playClick() api.Set(not state) end) return api end function W.slider(parent, text, min, max, default, step, onChange, suffix) local value = default local decimals = step < 1 local api = {} local r = W.row(parent, 58) W.T({ Size = UDim2.new(0.6, 0, 0, 20), Position = UDim2.fromOffset(12, 8), Text = text, TextSize = 13, Parent = r }) local box = new("TextBox", { Size = UDim2.fromOffset(74, 24), AnchorPoint = Vector2.new(1, 0), Position = UDim2.new(1, -10, 0, 7), Text = "", ClearTextOnFocus = false, Font = Enum.Font.GothamBold, TextSize = 12, BorderSizePixel = 0, Parent = r, }, { corner(7), stroke("stroke", 1) }) S(box, { BackgroundColor3 = "bg", TextColor3 = "accent" }) local track = new("Frame", { Size = UDim2.new(1, -28, 0, 6), Position = UDim2.new(0, 14, 0, 43), BorderSizePixel = 0, Parent = r, }, { corner(3) }) S(track, { BackgroundColor3 = "off" }) local fill = new("Frame", { Size = UDim2.fromScale(0, 1), BackgroundColor3 = WHITE, BorderSizePixel = 0, Parent = track }, { corner(3) }) S(new("UIGradient", { Parent = fill }), { Color = "accentSeq" }) local halo = new("Frame", { Size = UDim2.fromOffset(26, 26), AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(0, 0.5), BackgroundTransparency = 1, BorderSizePixel = 0, Parent = track, }, { corner(13) }) S(halo, { BackgroundColor3 = "accent" }) local knob = new("Frame", { Size = UDim2.fromOffset(14, 14), AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(0, 0.5), BackgroundColor3 = WHITE, BorderSizePixel = 0, ZIndex = 2, Parent = track, }, { corner(7), stroke("accent", 2) }) local function fmt(v) local s = decimals and string.format("%.1f", v) or tostring(math.floor(v + 0.5)) return s .. (suffix or "") end local function render() local a = (value - min) / (max - min) fill.Size = UDim2.fromScale(a, 1) knob.Position = UDim2.fromScale(a, 0.5) halo.Position = UDim2.fromScale(a, 0.5) box.Text = fmt(value) end local function commit(v, silent) v = math.clamp(math.floor(v / step + 0.5) * step, min, max) if decimals then v = math.floor(v * 10 + 0.5) / 10 end value = v render() if not silent then onChange(value) end end box.FocusLost:Connect(function() local n = tonumber((string.gsub(box.Text, "[^%d%.%-]", ""))) if n then commit(n) else render() end end) local hit = new("TextButton", { Size = UDim2.new(1, -12, 0, 30), Position = UDim2.new(0, 6, 0, 29), BackgroundTransparency = 1, Text = "", Parent = r, }) hit.MouseEnter:Connect(function() tween(halo, { BackgroundTransparency = 0.82 }, 0.15) end) hit.MouseLeave:Connect(function() if W.activeSlider == nil then tween(halo, { BackgroundTransparency = 1 }, 0.15) end end) hit.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then local scroller = r:FindFirstAncestorOfClass("ScrollingFrame") if scroller then scroller.ScrollingEnabled = false end tween(knob, { Size = UDim2.fromOffset(19, 19) }, 0.1) tween(halo, { BackgroundTransparency = 0.7 }, 0.1) W.activeSlider = { set = function(x) local a = math.clamp((x - track.AbsolutePosition.X) / track.AbsoluteSize.X, 0, 1) commit(min + a * (max - min)) end, stop = function() if scroller then scroller.ScrollingEnabled = true end tween(knob, { Size = UDim2.fromOffset(14, 14) }, 0.12) tween(halo, { BackgroundTransparency = 1 }, 0.2) end, } W.activeSlider.set(input.Position.X) end end) function api.Set(v, silent) commit(v, silent) end function api.Get() return value end render() return api end on(UIS.InputChanged, function(input) if W.activeSlider and (input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch) then W.activeSlider.set(input.Position.X) end end) on(UIS.InputEnded, function(input) if W.activeSlider and (input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch) then W.activeSlider.stop() W.activeSlider = nil end end) function W.chips(parent, options, default, columns, onChange) local selected = default local gap = 6 local holder = new("Frame", { Size = UDim2.new(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, BackgroundTransparency = 1, Parent = parent, }, { new("UIGridLayout", { CellSize = UDim2.new(1 / columns, -gap * (columns - 1) / columns, 0, 32), CellPadding = UDim2.fromOffset(gap, gap), SortOrder = Enum.SortOrder.LayoutOrder, }), }) local buttons = {} local function paint(animate) for name, b in pairs(buttons) do local sel = name == selected local bg = sel and WHITE or theme.surface2 local fg = sel and WHITE or theme.sub b.UIGradient.Enabled = sel b.UIStroke.Transparency = sel and 1 or 0 if animate then tween(b, { BackgroundColor3 = bg, TextColor3 = fg }, 0.15) else b.BackgroundColor3 = bg b.TextColor3 = fg end end end for i, name in ipairs(options) do local b = new("TextButton", { Text = name, Font = Enum.Font.GothamMedium, TextSize = 12, AutoButtonColor = false, BorderSizePixel = 0, LayoutOrder = i, Parent = holder, }, { corner(9), stroke("stroke", 1) }) S(new("UIGradient", { Rotation = 20, Parent = b }), { Color = "accentSeq" }) buttons[name] = b b.MouseEnter:Connect(function() if name ~= selected then tween(b, { BackgroundColor3 = theme.surface3 }, 0.1) end end) b.MouseLeave:Connect(function() if name ~= selected then tween(b, { BackgroundColor3 = theme.surface2 }, 0.1) end end) b.MouseButton1Click:Connect(function() selected = name paint(true) playClick() onChange(name) end) end hook(holder, paint) return { Set = function(name) selected = name paint(true) end, } end function W.dropdown(parent, text, options, default, onChange) local selected = default local open = false local holder = new("Frame", { Size = UDim2.new(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, BackgroundTransparency = 1, Parent = parent, }, { list(4) }) local header = new("TextButton", { Size = UDim2.new(1, 0, 0, 42), Text = "", AutoButtonColor = false, BorderSizePixel = 0, LayoutOrder = 1, Parent = holder, }, { corner(10) }) S(header, { BackgroundColor3 = "surface2" }) header.MouseEnter:Connect(function() tween(header, { BackgroundColor3 = theme.surface3 }, 0.1) end) header.MouseLeave:Connect(function() tween(header, { BackgroundColor3 = theme.surface2 }, 0.1) end) W.T({ Size = UDim2.new(0.4, 0, 1, 0), Position = UDim2.fromOffset(12, 0), Text = text, TextSize = 13, Parent = header }) local valueLabel = W.T({ Size = UDim2.new(0.6, -38, 1, 0), Position = UDim2.new(0.4, 0, 0, 0), Text = selected, Font = Enum.Font.GothamBold, TextSize = 13, TextXAlignment = Enum.TextXAlignment.Right, TextTruncate = Enum.TextTruncate.AtEnd, Parent = header, }, "accent") local arrow = new("Frame", { Size = UDim2.fromOffset(20, 42), Position = UDim2.new(1, -26, 0, 0), BackgroundTransparency = 1, Parent = header, }) local arrowIcon = W.icon(arrow, "chevdown", 14) hook(arrow, function() arrowIcon.Set(theme.dim) end) local optionList = new("ScrollingFrame", { Size = UDim2.new(1, 0, 0, 40), BorderSizePixel = 0, ScrollBarThickness = 3, CanvasSize = UDim2.new(), AutomaticCanvasSize = Enum.AutomaticSize.Y, Visible = false, LayoutOrder = 2, Parent = holder, }, { corner(10), list(2), pad(4), stroke("stroke", 1) }) S(optionList, { BackgroundColor3 = "bg", ScrollBarImageColor3 = "surface3" }) local optionButtons = {} hook(holder, function() for name, b in pairs(optionButtons) do b.TextColor3 = name == selected and accent or theme.text end end) local function setOpen(v) open = v optionList.Visible = v tween(arrow, { Rotation = v and 180 or 0 }, 0.15) end local function build(newOptions) for _, b in pairs(optionButtons) do b:Destroy() end table.clear(optionButtons) for i, name in ipairs(newOptions) do local b = new("TextButton", { Size = UDim2.new(1, 0, 0, 30), BackgroundTransparency = 1, Text = name, TextColor3 = name == selected and accent or theme.text, Font = Enum.Font.GothamMedium, TextSize = 13, AutoButtonColor = false, BorderSizePixel = 0, LayoutOrder = i, Parent = optionList, }, { corner(7) }) S(b, { BackgroundColor3 = "surface3" }) optionButtons[name] = b b.MouseEnter:Connect(function() tween(b, { BackgroundTransparency = 0 }, 0.1) end) b.MouseLeave:Connect(function() tween(b, { BackgroundTransparency = 1 }, 0.1) end) b.MouseButton1Click:Connect(function() selected = name valueLabel.Text = name setOpen(false) for n2, b2 in pairs(optionButtons) do b2.TextColor3 = n2 == selected and accent or theme.text end playClick() onChange(name) end) end optionList.Size = UDim2.new(1, 0, 0, math.clamp(#newOptions * 32 + 8, 40, 168)) end header.MouseButton1Click:Connect(function() playClick() setOpen(not open) end) build(options) return { Refresh = function(newOptions, newSelected) if newSelected ~= nil then selected = newSelected valueLabel.Text = newSelected end build(newOptions) end, } end function W.button(parent, text, callback, ghost, size) local btn = new("TextButton", { Size = size or UDim2.new(1, 0, 0, 38), BackgroundTransparency = 1, Text = "", AutoButtonColor = false, Parent = parent, }) local bg = new("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(0.5, 0.5), Size = UDim2.fromScale(1, 1), BackgroundColor3 = WHITE, BorderSizePixel = 0, ClipsDescendants = true, Parent = btn, }, { corner(10) }) if ghost then S(bg, { BackgroundColor3 = "surface3" }) bg.Parent = btn new("UIStroke", { Color = theme.stroke, Thickness = 1, ApplyStrokeMode = Enum.ApplyStrokeMode.Border, Parent = bg }) else S(new("UIGradient", { Rotation = 20, Parent = bg }), { Color = "accentSeq" }) end local shine = new("Frame", { Size = UDim2.new(0, 26, 2, 0), Position = UDim2.new(-0.3, 0, -0.5, 0), Rotation = 20, BackgroundColor3 = WHITE, BackgroundTransparency = 0.85, BorderSizePixel = 0, Parent = bg, }) local lbl = new("TextLabel", { Size = UDim2.fromScale(1, 1), BackgroundTransparency = 1, Text = text, TextColor3 = WHITE, Font = Enum.Font.GothamBold, TextSize = 13, Parent = btn, }) if ghost then S(lbl, { TextColor3 = "text" }) -- primary buttons keep white text in every theme end local function rest() tween(bg, { Size = UDim2.fromScale(1, 1) }, 0.1) end btn.MouseEnter:Connect(function() tween(bg, ghost and { BackgroundColor3 = theme.surface3:Lerp(WHITE, 0.1) } or { BackgroundTransparency = 0.12 }, 0.12) shine.Position = UDim2.new(-0.3, 0, -0.5, 0) tween(shine, { Position = UDim2.new(1.2, 0, -0.5, 0) }, 0.55, Enum.EasingStyle.Quad) end) btn.MouseLeave:Connect(function() tween(bg, ghost and { BackgroundColor3 = theme.surface3 } or { BackgroundTransparency = 0 }, 0.12) rest() end) btn.MouseButton1Down:Connect(function() tween(bg, { Size = UDim2.new(1, -6, 1, -4) }, 0.08) end) btn.MouseButton1Up:Connect(rest) btn.MouseButton1Click:Connect(function() playClick() callback() end) return { Button = btn, SetText = function(t) lbl.Text = t end } end function W.buttonRow(parent, defs) local holder = new("Frame", { Size = UDim2.new(1, 0, 0, 38), BackgroundTransparency = 1, Parent = parent }, { new("UIListLayout", { FillDirection = Enum.FillDirection.Horizontal, Padding = UDim.new(0, 8), SortOrder = Enum.SortOrder.LayoutOrder, }), }) local n = #defs local apis = {} for i, d in ipairs(defs) do apis[i] = W.button(holder, d[1], d[2], d[3], UDim2.new(1 / n, -8 * (n - 1) / n, 1, 0)) end return apis end -- quick-action tile for the dashboard: icon, name, state, hotkey function W.tile(parent, icon, title, keyText, callback) local state = false local b = new("TextButton", { Text = "", AutoButtonColor = false, BorderSizePixel = 0, Parent = parent, }, { corner(14) }) local st = stroke("stroke", 1.5) st.Parent = b local bubble = new("Frame", { Size = UDim2.fromOffset(40, 40), Position = UDim2.fromOffset(12, 14), BorderSizePixel = 0, BackgroundColor3 = WHITE, Parent = b, }, { corner(12) }) local bubbleGrad = S(new("UIGradient", { Rotation = 45, Parent = bubble }), { Color = "accentSeq" }) local iconApi = W.icon(bubble, icon, 20) W.T({ Size = UDim2.new(1, -130, 0, 18), Position = UDim2.fromOffset(62, 16), Text = title, Font = Enum.Font.GothamBold, TextSize = 14, Parent = b }) local stateL = W.T({ Size = UDim2.new(1, -130, 0, 14), Position = UDim2.fromOffset(62, 36), Text = "Off", Font = Enum.Font.Gotham, Parent = b }, "dim") S(new("TextLabel", { Size = UDim2.fromOffset(0, 20), AutomaticSize = Enum.AutomaticSize.X, AnchorPoint = Vector2.new(1, 0), Position = UDim2.new(1, -10, 0, 12), Text = keyText, Font = Enum.Font.GothamBold, TextSize = 10, Parent = b, }, { corner(6), pad(0, 7) }), { BackgroundColor3 = "surface3", TextColor3 = "sub" }) local function paint(animate) local bg = state and theme.surface2:Lerp(accent, 0.2) or theme.surface2 bubbleGrad.Enabled = state bubble.BackgroundColor3 = state and WHITE or theme.surface3 iconApi.Set(state and WHITE or theme.dim) stateL.Text = state and "On" or "Off" stateL.TextColor3 = state and GOOD or theme.dim st.Color = state and accent or theme.stroke if animate then tween(b, { BackgroundColor3 = bg }, 0.18) else b.BackgroundColor3 = bg end end hook(b, function() paint(false) end) local api = {} function api.Set(v, silent) state = v and true or false paint(true) if not silent then callback(state) end end b.MouseEnter:Connect(function() tween(b, { BackgroundColor3 = (state and theme.surface2:Lerp(accent, 0.28) or theme.surface3) }, 0.12) end) b.MouseLeave:Connect(function() paint(true) end) b.MouseButton1Click:Connect(function() playClick() api.Set(not state) end) return api end -- Toasts ------------------------------------------------------------------ local toastHolder = new("Frame", { Size = UDim2.fromOffset(300, 360), AnchorPoint = Vector2.new(1, 1), Position = UDim2.new(1, -16, 1, -16), BackgroundTransparency = 1, Parent = overlay, }, { list(6, { VerticalAlignment = Enum.VerticalAlignment.Bottom, HorizontalAlignment = Enum.HorizontalAlignment.Right, }), }) local toastId = 0 toast = function(text, kind) toastId = toastId + 1 local color = (kind == "bad" and BAD) or (kind == "good" and GOOD) or accent local glyph = (kind == "bad" and "bang") or (kind == "good" and "check") or "info" local slot = new("Frame", { Size = UDim2.new(1, 0, 0, 46), BackgroundTransparency = 1, LayoutOrder = toastId, Parent = toastHolder, }) local c = new("Frame", { Size = UDim2.fromScale(1, 1), Position = UDim2.new(1.2, 0, 0, 0), BorderSizePixel = 0, ClipsDescendants = true, Parent = slot, }, { corner(12), stroke("stroke", 1) }) S(c, { BackgroundColor3 = "surface" }) local dot = new("Frame", { Size = UDim2.fromOffset(26, 26), Position = UDim2.fromOffset(11, 10), BackgroundColor3 = color, BackgroundTransparency = 0.8, BorderSizePixel = 0, Parent = c, }, { corner(13) }) W.icon(dot, glyph, 12).Set(color) W.T({ Size = UDim2.new(1, -52, 1, -4), Position = UDim2.fromOffset(44, 0), Text = text, TextWrapped = true, Parent = c, }) local bar = new("Frame", { Size = UDim2.new(1, -24, 0, 2), Position = UDim2.new(0, 12, 1, -4), BackgroundColor3 = color, BackgroundTransparency = 0.45, BorderSizePixel = 0, Parent = c, }, { corner(1) }) tween(c, { Position = UDim2.new(0, 0, 0, 0) }, 0.32, Enum.EasingStyle.Back) tween(bar, { Size = UDim2.new(0, 0, 0, 2) }, 2.8, Enum.EasingStyle.Linear) task.delay(2.8, function() if not c.Parent then return end tween(c, { Position = UDim2.new(1.2, 0, 0, 0) }, 0.22) task.wait(0.25) slot:Destroy() end) end -- Popup (the "..." buttons on toggles) -------------------------------------- local popupOverlay = new("TextButton", { Size = UDim2.fromScale(1, 1), BackgroundColor3 = Color3.new(0, 0, 0), BackgroundTransparency = 1, AutoButtonColor = false, Text = "", Visible = false, ZIndex = 50, Parent = overlay, }) local popupPanel = new("Frame", { Size = UDim2.fromOffset(370, 0), AutomaticSize = Enum.AutomaticSize.Y, AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(0.5, 0.5), BorderSizePixel = 0, ZIndex = 51, Parent = popupOverlay, }, { corner(16), stroke("stroke", 1.5), pad(16), list(10) }) S(popupPanel, { BackgroundColor3 = "bg2" }) local popupHeader = new("Frame", { Size = UDim2.new(1, 0, 0, 26), BackgroundTransparency = 1, LayoutOrder = 1, Parent = popupPanel }) local popupTitle = W.T({ Size = UDim2.new(1, -30, 1, 0), Text = "", Font = Enum.Font.GothamBold, TextSize = 15, Parent = popupHeader, }) local popupClose = new("TextButton", { Size = UDim2.fromOffset(26, 26), AnchorPoint = Vector2.new(1, 0), Position = UDim2.new(1, 0, 0, 0), Text = "", AutoButtonColor = false, Parent = popupHeader, }, { corner(8) }) S(popupClose, { BackgroundColor3 = "surface3" }) local popupCloseIcon = W.icon(popupClose, "x", 12) hook(popupClose, function() popupCloseIcon.Set(theme.text) end) local popupContent = new("Frame", { Size = UDim2.new(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, BackgroundTransparency = 1, LayoutOrder = 2, Parent = popupPanel, }, { list(8) }) local popupScale = new("UIScale", { Scale = 0.9, Parent = popupPanel }) local function closePopup() tween(popupScale, { Scale = 0.9 }, 0.12) tween(popupOverlay, { BackgroundTransparency = 1 }, 0.12) task.delay(0.12, function() popupOverlay.Visible = false end) end local function openPopup(title, buildFn) for _, c in ipairs(popupContent:GetChildren()) do if not c:IsA("UIListLayout") then c:Destroy() end end popupTitle.Text = title buildFn(popupContent) popupOverlay.Visible = true popupOverlay.BackgroundTransparency = 1 popupScale.Scale = 0.9 tween(popupOverlay, { BackgroundTransparency = 0.45 }, 0.15) tween(popupScale, { Scale = 1 }, 0.2, Enum.EasingStyle.Back) end popupOverlay.MouseButton1Click:Connect(closePopup) popupClose.MouseButton1Click:Connect(function() playClick() closePopup() end) -- touch buttons for up / down (fly + carpet on mobile) local touchPad = new("Frame", { Size = UDim2.fromOffset(60, 128), AnchorPoint = Vector2.new(1, 0.5), Position = UDim2.new(1, -18, 0.5, 0), BackgroundTransparency = 1, Visible = false, Parent = overlay, }, { list(8) }) for i, def in ipairs({ { "chevup", 1 }, { "chevdown", -1 } }) do local b = new("TextButton", { Size = UDim2.fromOffset(60, 60), BackgroundTransparency = 0.1, Text = "", AutoButtonColor = false, LayoutOrder = i, Parent = touchPad, }, { corner(30), stroke("accent", 2) }) S(b, { BackgroundColor3 = "surface" }) local touchIcon = W.icon(b, def[1], 24) hook(b, function() touchIcon.Set(theme.text) end) b.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then touchV = def[2] tween(b, { BackgroundColor3 = theme.surface3 }, 0.08) end end) b.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then if touchV == def[2] then touchV = 0 end tween(b, { BackgroundColor3 = theme.surface }, 0.12) end end) end -- Window ------------------------------------------------------------------ local WIN_W, WIN_H, HEADER_H, FOOTER_H, SIDE_W = 780, 540, 68, 30, 180 local scaler = new("Frame", { Name = "Scaler", BackgroundTransparency = 1, Parent = gui }) local uiScale = new("UIScale", { Parent = scaler }) local userScale = 1 local function applyScale() local cam = workspace.CurrentCamera local vp = cam and cam.ViewportSize or Vector2.new(1280, 720) local auto = math.clamp(math.min(vp.X / (WIN_W + 50), vp.Y / (WIN_H + 50)), 0.45, 1) local s = auto * userScale uiScale.Scale = s scaler.Size = UDim2.fromScale(1 / s, 1 / s) end applyScale() if workspace.CurrentCamera then on(workspace.CurrentCamera:GetPropertyChangedSignal("ViewportSize"), applyScale) end -- root moves as one piece (shadow + window) local root = new("Frame", { Name = "Root", AnchorPoint = Vector2.new(0.5, 0), Position = UDim2.new(0.5, 0, 0.5, -WIN_H / 2), Size = UDim2.fromOffset(WIN_W, WIN_H), BackgroundTransparency = 1, Parent = scaler, }) local popScale = new("UIScale", { Scale = 0.9, Parent = root }) new("ImageLabel", { Name = "Shadow", AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(0.5, 0.52), Size = UDim2.new(1, 70, 1, 70), BackgroundTransparency = 1, Image = "rbxassetid://6014261993", ImageColor3 = Color3.new(0, 0, 0), ImageTransparency = 0.45, ScaleType = Enum.ScaleType.Slice, SliceCenter = Rect.new(49, 49, 450, 450), ZIndex = 0, Parent = root, }) local main = new("Frame", { Name = "Main", Size = UDim2.fromScale(1, 1), BorderSizePixel = 0, ClipsDescendants = true, Parent = root, }, { corner(18) }) S(main, { BackgroundColor3 = "bg" }) S(new("UIGradient", { Rotation = 90, Parent = main }), { Color = "bgSeq" }) local mainStroke = new("UIStroke", { Thickness = 1.6, Color = WHITE, ApplyStrokeMode = Enum.ApplyStrokeMode.Border, Parent = main }) local mainStrokeGrad = S(new("UIGradient", { Parent = mainStroke }), { Color = "frameSeq" }) -- soft accent glows in the corners local glowA = new("Frame", { Size = UDim2.fromOffset(340, 340), Position = UDim2.new(1, -200, 0, -190), BackgroundTransparency = 0.9, BorderSizePixel = 0, Parent = main, }, { corner(170) }) S(glowA, { BackgroundColor3 = "accent" }) local glowB = new("Frame", { Size = UDim2.fromOffset(300, 300), Position = UDim2.new(0, -150, 1, -130), BackgroundTransparency = 0.92, BorderSizePixel = 0, Parent = main, }, { corner(150) }) S(glowB, { BackgroundColor3 = "accent2" }) -- ambient particles: snow (Christmas), embers (Halloween), petals (Sakura) local fxLayer = new("Frame", { Size = UDim2.fromScale(1, 1), BackgroundTransparency = 1, ZIndex = 20, Parent = main }) local fxParts = {} for i = 1, 30 do local f = new("Frame", { BorderSizePixel = 0, Visible = false, ZIndex = 20, Parent = fxLayer }, { corner(8) }) fxParts[i] = { f = f, x = math.random(), y = math.random(), s = 2 + math.random() * 3.5, v = 0.05 + math.random() * 0.08, ph = math.random() * 6.28, sw = 0.01 + math.random() * 0.02, tr = 0.25 + math.random() * 0.4, } end local fxMode hook(fxLayer, function() fxMode = theme.fx for i, p in ipairs(fxParts) do p.f.Visible = fxMode ~= nil local s = fxMode == "petals" and p.s * 1.7 or p.s p.f.Size = UDim2.fromOffset(s, s) p.f.BackgroundTransparency = p.tr if fxMode == "snow" then p.f.BackgroundColor3 = WHITE elseif fxMode == "petals" then p.f.BackgroundColor3 = accent:Lerp(WHITE, 0.45) else p.f.BackgroundColor3 = (i % 3 == 0) and accent2() or accent end end end) function W.updateFx(dt, now) if not fxMode then return end for _, p in ipairs(fxParts) do if fxMode == "embers" then p.y = p.y - p.v * dt * 1.4 if p.y < -0.05 then p.y, p.x = 1.05, math.random() end p.f.BackgroundTransparency = 0.2 + 0.55 * math.abs(math.sin(now * 3 + p.ph)) else p.y = p.y + p.v * dt if p.y > 1.05 then p.y, p.x = -0.05, math.random() end end p.f.Position = UDim2.fromScale(p.x + math.sin(now * 0.9 + p.ph) * p.sw, p.y) end end -- header local header = new("Frame", { Name = "Header", Size = UDim2.new(1, 0, 0, HEADER_H), BackgroundTransparency = 1, Parent = main }) local headerLine = new("Frame", { Size = UDim2.new(1, 0, 0, 1), Position = UDim2.new(0, 0, 1, -1), BackgroundColor3 = WHITE, BorderSizePixel = 0, Parent = header, }) S(new("UIGradient", { Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 1), NumberSequenceKeypoint.new(0.5, 0.25), NumberSequenceKeypoint.new(1, 1), }), Parent = headerLine, }), { Color = "accentSeq" }) local logo = new("Frame", { Size = UDim2.fromOffset(40, 40), Position = UDim2.fromOffset(16, 12), BackgroundColor3 = WHITE, BorderSizePixel = 0, Parent = header, }, { corner(13) }) S(new("UIGradient", { Rotation = 45, Parent = logo }), { Color = "accentSeq" }) W.moon(logo, 40) W.T({ Size = UDim2.fromOffset(200, 22), Position = UDim2.fromOffset(66, 11), Text = "Moon's FE Ring", Font = Enum.Font.GothamBold, TextSize = 17, Parent = header }) W.T({ Size = UDim2.fromOffset(240, 14), Position = UDim2.fromOffset(66, 35), Text = "Physics suite", Font = Enum.Font.Gotham, TextSize = 11, Parent = header }, "dim") local verPill = new("TextLabel", { Size = UDim2.fromOffset(0, 16), AutomaticSize = Enum.AutomaticSize.X, Position = UDim2.fromOffset(150, 35), Text = "v5", Font = Enum.Font.GothamBold, TextSize = 10, BorderSizePixel = 0, Parent = header, }, { corner(9), pad(0, 8) }) S(verPill, { BackgroundColor3 = "accent", TextColor3 = "text" }) verPill.TextColor3 = WHITE local statusPill = new("TextLabel", { Size = UDim2.fromOffset(112, 26), Position = UDim2.new(1, -208, 0, 19), Text = "", Font = Enum.Font.GothamMedium, TextSize = 12, Parent = header, }, { corner(13), stroke("stroke", 1), pad(0, 0, 0, 14) }) S(statusPill, { BackgroundColor3 = "surface", TextColor3 = "sub" }) local statusDot = new("Frame", { Size = UDim2.fromOffset(8, 8), Position = UDim2.fromOffset(12, 9), BackgroundColor3 = theme.dim, BorderSizePixel = 0, Parent = statusPill, }, { corner(4) }) local function headerButton(kind, xOffset, hoverColor) local b = new("TextButton", { Size = UDim2.fromOffset(30, 30), Position = UDim2.new(1, xOffset, 0, 19), Text = "", AutoButtonColor = false, BorderSizePixel = 0, Parent = header, }, { corner(9) }) S(b, { BackgroundColor3 = "surface2" }) local ic = W.icon(b, kind, 13) hook(b, function() ic.Set(theme.text) end) b.MouseEnter:Connect(function() tween(b, { BackgroundColor3 = hoverColor }, 0.12) end) b.MouseLeave:Connect(function() tween(b, { BackgroundColor3 = theme.surface2 }, 0.12) end) return b end local minimizeBtn = headerButton("minus", -82, Color3.fromRGB(70, 70, 100)) local closeBtn = headerButton("x", -46, Color3.fromRGB(205, 70, 84)) -- body: sidebar + pages local body = new("Frame", { Name = "Body", Size = UDim2.new(1, 0, 1, -(HEADER_H + FOOTER_H)), Position = UDim2.fromOffset(0, HEADER_H), BackgroundTransparency = 1, Parent = main, }) local sidebar = new("Frame", { Size = UDim2.new(0, SIDE_W, 1, -16), Position = UDim2.fromOffset(12, 8), BorderSizePixel = 0, Parent = body, }, { corner(14), stroke("stroke", 1) }) S(sidebar, { BackgroundColor3 = "surface" }) local tabList = new("Frame", { Size = UDim2.new(1, 0, 1, -66), BackgroundTransparency = 1, Parent = sidebar, }, { pad(8), list(4) }) local userCard = new("Frame", { Size = UDim2.new(1, -16, 0, 50), Position = UDim2.new(0, 8, 1, -58), BorderSizePixel = 0, Parent = sidebar, }, { corner(12), stroke("stroke", 1) }) S(userCard, { BackgroundColor3 = "surface2" }) local avatar = new("ImageLabel", { Size = UDim2.fromOffset(34, 34), Position = UDim2.fromOffset(8, 8), BorderSizePixel = 0, Image = "", Parent = userCard, }, { corner(17) }) S(avatar, { BackgroundColor3 = "surface3" }) W.T({ Size = UDim2.new(1, -52, 0, 16), Position = UDim2.fromOffset(49, 9), Text = LocalPlayer.DisplayName, Font = Enum.Font.GothamBold, TextTruncate = Enum.TextTruncate.AtEnd, Parent = userCard }) W.T({ Size = UDim2.new(1, -52, 0, 14), Position = UDim2.fromOffset(49, 26), Text = #ADMIN_IDS > 0 and "Admin" or "Player", Font = Enum.Font.Gotham, TextSize = 11, Parent = userCard }, "dim") task.spawn(function() local ok, img = pcall(function() return Players:GetUserThumbnailAsync(LocalPlayer.UserId, Enum.ThumbnailType.HeadShot, Enum.ThumbnailSize.Size100x100) end) if ok and img then avatar.Image = img end end) local content = new("Frame", { Size = UDim2.new(1, -(SIDE_W + 36), 1, -16), Position = UDim2.fromOffset(SIDE_W + 24, 8), BackgroundTransparency = 1, ClipsDescendants = true, Parent = body, }) -- footer local footer = new("Frame", { Size = UDim2.new(1, 0, 0, FOOTER_H), Position = UDim2.new(0, 0, 1, -FOOTER_H), BackgroundTransparency = 1, Parent = main, }) S(new("Frame", { Size = UDim2.new(1, -24, 0, 1), Position = UDim2.fromOffset(12, 0), BorderSizePixel = 0, Parent = footer }), { BackgroundColor3 = "stroke" }) local footerStatus = W.T({ Size = UDim2.new(0.62, 0, 1, 0), Position = UDim2.fromOffset(16, 0), Text = "", Font = Enum.Font.Gotham, TextSize = 11, Parent = footer }, "dim") local footerRight = W.T({ Size = UDim2.new(0.38, -16, 1, 0), Position = UDim2.new(0.62, 0, 0, 0), Text = "", Font = Enum.Font.Gotham, TextSize = 11, TextXAlignment = Enum.TextXAlignment.Right, Parent = footer, }, "dim") -- tabs local tabs = {} local currentTab local tabCount = 0 local function styleTabs(animate) for name, t in pairs(tabs) do local active = name == currentTab t.page.Visible = active local goal = { bar = { Size = UDim2.fromOffset(3, active and 20 or 0) }, btn = { BackgroundTransparency = active and 0.1 or 1 }, name = { TextColor3 = active and theme.text or theme.sub }, } if animate then tween(t.button, goal.btn, 0.15) tween(t.bar, goal.bar, 0.18) tween(t.nameL, goal.name, 0.15) else t.button.BackgroundTransparency = goal.btn.BackgroundTransparency t.bar.Size = goal.bar.Size t.nameL.TextColor3 = goal.name.TextColor3 end t.icon.Set(active and WHITE or theme.dim) t.chip.BackgroundColor3 = active and WHITE or theme.surface3 t.chipGrad.Enabled = active end end local function selectTab(name) if currentTab == name then return end currentTab = name styleTabs(true) local page = tabs[name].page page.Position = UDim2.fromOffset(0, 14) tween(page, { Position = UDim2.fromOffset(0, 0) }, 0.26, Enum.EasingStyle.Quint) end local function createTab(name, icon, subtitle) tabCount = tabCount + 1 local button = new("TextButton", { Size = UDim2.new(1, 0, 0, 40), BackgroundTransparency = 1, Text = "", AutoButtonColor = false, BorderSizePixel = 0, LayoutOrder = tabCount, Parent = tabList, }, { corner(11) }) S(button, { BackgroundColor3 = "surface3" }) local bar = new("Frame", { Size = UDim2.fromOffset(3, 0), AnchorPoint = Vector2.new(0, 0.5), Position = UDim2.new(0, 0, 0.5, 0), BackgroundColor3 = WHITE, BorderSizePixel = 0, Parent = button, }, { corner(2) }) S(new("UIGradient", { Rotation = 90, Parent = bar }), { Color = "accentSeq" }) local chip = new("Frame", { Size = UDim2.fromOffset(28, 28), Position = UDim2.fromOffset(10, 6), BackgroundColor3 = WHITE, BorderSizePixel = 0, Parent = button, }, { corner(9) }) local chipGrad = S(new("UIGradient", { Rotation = 45, Parent = chip }), { Color = "accentSeq" }) local iconApi = W.icon(chip, icon, 16) local nameL = new("TextLabel", { Size = UDim2.new(1, -86, 1, 0), Position = UDim2.fromOffset(46, 0), BackgroundTransparency = 1, Text = name, TextXAlignment = Enum.TextXAlignment.Left, Font = Enum.Font.GothamMedium, TextSize = 13, Parent = button, }) local dot = new("Frame", { Size = UDim2.fromOffset(8, 8), AnchorPoint = Vector2.new(1, 0.5), Position = UDim2.new(1, -12, 0.5, 0), BackgroundColor3 = GOOD, BorderSizePixel = 0, Visible = false, Parent = button, }, { corner(4), new("UIStroke", { Color = GOOD, Thickness = 3, Transparency = 0.7 }) }) local page = new("ScrollingFrame", { Size = UDim2.fromScale(1, 1), BackgroundTransparency = 1, BorderSizePixel = 0, ScrollBarThickness = 3, ScrollingDirection = Enum.ScrollingDirection.Y, CanvasSize = UDim2.new(), AutomaticCanvasSize = Enum.AutomaticSize.Y, Visible = false, Parent = content, }, { list(10), pad(0, 8, 10, 0) }) S(page, { ScrollBarImageColor3 = "surface3" }) W.pageHeader(page, name == "Home" and "Dashboard" or name, subtitle) tabs[name] = { button = button, page = page, bar = bar, icon = iconApi, nameL = nameL, dot = dot, chip = chip, chipGrad = chipGrad } button.MouseEnter:Connect(function() if currentTab ~= name then tween(button, { BackgroundTransparency = 0.6 }, 0.12) end end) button.MouseLeave:Connect(function() if currentTab ~= name then tween(button, { BackgroundTransparency = 1 }, 0.12) end end) button.MouseButton1Click:Connect(function() playClick() selectTab(name) end) return page end hook(sidebar, function() styleTabs(false) end) -- status (pill, footer, tab dots, touch buttons) -------------------------------- local fps = 60 refreshStatus = function() local n = 0 if ringOn then n = n + 1 end if fly.on then n = n + 1 end if carpet.on then n = n + 1 end statusPill.Text = n > 0 and (n .. " active") or "Idle" statusDot.BackgroundColor3 = n > 0 and GOOD or theme.dim footerStatus.Text = string.format( "Ring %s - Fly %s - Carpet %s", ringOn and "on" or "off", fly.on and "on" or "off", carpet.on and "on" or "off" ) if tabs["Ring"] and tabs["Fly"] and tabs["Carpet"] then tabs["Ring"].dot.Visible = ringOn tabs["Fly"].dot.Visible = fly.on tabs["Carpet"].dot.Visible = carpet.on end touchPad.Visible = UIS.TouchEnabled and (fly.on or carpet.on) end -- Helpers for the tabs ---------------------------------------------------- local rainbow = false local function playerNames() local names = { "Me" } for _, p in ipairs(Players:GetPlayers()) do if p ~= LocalPlayer then names[#names + 1] = p.Name end end return names end local function centerOptions() local names = playerNames() table.insert(names, 2, "Cursor") return names end local PRESETS = { { "Halo", { shape = "Ring", radius = 28, height = 22, spin = 70, strength = 800, range = 250, density = 60 } }, { "Cyclone", { shape = "Tornado", radius = 45, height = 60, spin = 240, strength = 900, range = 300, density = 120 } }, { "Orb", { shape = "Sphere", radius = 16, height = 12, spin = 120, strength = 800, range = 250, density = 80 } }, { "Galaxy", { shape = "Galaxy", radius = 70, height = 18, spin = 40, strength = 900, range = 400, density = 160 } }, { "Heartbeat", { shape = "Heart", radius = 26, height = 22, spin = 60, strength = 800, range = 250, density = 70 } }, { "Atom", { shape = "Atom", radius = 30, height = 18, spin = 90, strength = 800, range = 250, density = 90 } }, } local PRESET_KEYS = { "shape", "radius", "height", "spin", "strength", "range", "density" } local stopPicking, pickTarget, disableEverything, safetyCard local function grid(parent, cols, cellH, gap) gap = gap or 10 return new("Frame", { Size = UDim2.new(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, BackgroundTransparency = 1, Parent = parent, }, { new("UIGridLayout", { CellSize = UDim2.new(1 / cols, -gap, 0, cellH), CellPadding = UDim2.fromOffset(gap, gap), SortOrder = Enum.SortOrder.LayoutOrder, }), }) end -- Home tab ------------------------------------------------------------------ do local homePage = createTab("Home", "home", "Live overview and quick actions") local hero = new("Frame", { Size = UDim2.new(1, 0, 0, 150), BorderSizePixel = 0, BackgroundColor3 = WHITE, ClipsDescendants = true, Parent = homePage, }, { corner(16) }) S(new("UIGradient", { Rotation = 25, Parent = hero }), { Color = "accentSeq" }) new("TextLabel", { Size = UDim2.new(1, -250, 0, 28), Position = UDim2.fromOffset(20, 22), BackgroundTransparency = 1, Text = "Welcome back, " .. LocalPlayer.DisplayName, TextColor3 = WHITE, Font = Enum.Font.GothamBold, TextSize = 20, TextXAlignment = Enum.TextXAlignment.Left, TextTruncate = Enum.TextTruncate.AtEnd, Parent = hero, }) new("TextLabel", { Size = UDim2.new(1, -250, 0, 60), Position = UDim2.fromOffset(20, 56), BackgroundTransparency = 1, Text = "Your ring has a fixed number of slots and fills them evenly. Nothing is dropped or reshuffled until you switch it off.", TextColor3 = WHITE, TextTransparency = 0.12, Font = Enum.Font.Gotham, TextSize = 12, TextWrapped = true, TextXAlignment = Enum.TextXAlignment.Left, TextYAlignment = Enum.TextYAlignment.Top, Parent = hero, }) local heroPreview = W.preview(hero, 126, "Home") heroPreview.Size = UDim2.fromOffset(190, 126) heroPreview.Position = UDim2.new(1, -206, 0.5, -63) local stats = grid(homePage, 3, 74) UI.hParts = W.statTile(stats, "Captured parts") UI.hCtrl = W.statTile(stats, "Controlled by you") UI.hFps = W.statTile(stats, "Frames per second") local quick = W.card(homePage, "Quick actions", "click a tile or use its hotkey") local tiles = grid(quick, 2, 68) UI.ringTile = W.tile(tiles, "ring", "Ring", "R", function(v) setRing(v) end) UI.flyTile = W.tile(tiles, "fly", "Superman Fly", "F", function(v) setFly(v) end) UI.carpetTile = W.tile(tiles, "carpet", "Carpet", "C", function(v) setCarpet(v) end) UI.launcherTile = W.tile(tiles, "launch", "Part Launcher", "T", function(v) if v then giveLauncher() else removeLauncher() end end) W.buttonRow(quick, { { "Fire at cursor", launchAt }, { "Disable everything", function() disableEverything() toast("Everything disabled", "good") end, true }, }) end -- Ring tab ------------------------------------------------------------------ do local ringSliders = {} local function buildRingAdvanced(parent) W.slider(parent, "Max Part Size", 5, 500, config.maxSize, 5, function(v) config.maxSize = v end) W.slider(parent, "Rescan Delay", 0.1, 2, config.scanRate, 0.1, function(v) config.scanRate = v end, "s") W.toggle(parent, "Tidy orientation", "Parts face outward as they orbit", config.tidy, function(v) config.tidy = v end) W.toggle(parent, "Swap lost parts", "Replaces parts that fell out of the ring for good", config.reclaim, function(v) config.reclaim = v end) end local ringPage = createTab("Ring", "ring", "Orbit loose parts in a shape that never reshuffles") local ringCard = W.card(ringPage, "Ring", "R") UI.ring = W.toggle(ringCard, "Enable Ring", "Orbit nearby loose parts - ... = advanced", false, function(v) setRing(v) end, function() openPopup("Ring - Advanced", buildRingAdvanced) end) UI.statParts = W.statRow(ringCard, "Captured parts") UI.statCtrl = W.statRow(ringCard, "Controlled by you") UI.statLost = W.statRow(ringCard, "Taken by others") W.button(ringCard, "Rebuild ring (re-grab the nearest parts)", function() if ringOn then ringRebuildAt = os.clock() else toast("Turn the ring on first", "bad") end end, true) local pvCard = W.card(ringPage, "Live preview", "what the ring will look like") W.preview(pvCard, 200, "Ring") UI.fillLbl = W.T({ Size = UDim2.new(1, 0, 0, 16), Text = "0 / 0 slots filled", Font = Enum.Font.Gotham, Parent = pvCard }, "sub") local presetCard = W.card(ringPage, "Presets") local shapeChips local function applyPreset(t) for k, v in pairs(t) do config[k] = v end shapeChips.Set(config.shape) for k, s in pairs(ringSliders) do s.Set(config[k], true) end ringSwarm.dirty = true if ringOn then ringRebuildAt = os.clock() + 0.3 -- density may have changed end end W.chips(presetCard, (function() local names = {} for _, p in ipairs(PRESETS) do names[#names + 1] = p[1] end return names end)(), "", 3, function(name) for _, p in ipairs(PRESETS) do if p[1] == name then applyPreset(p[2]) toast("Preset: " .. name, "good") end end end) -- your own presets: save the current setup into a slot and load it any time local myCard = W.card(ringPage, "My presets", "save your own setups") local slotNames = { "Slot 1", "Slot 2", "Slot 3", "Slot 4" } local savedSlots = {} local selSlot = "Slot 1" local function slotSummary() local names = {} for _, n in ipairs(slotNames) do if savedSlots[n] then names[#names + 1] = n .. " (" .. savedSlots[n].shape .. ")" end end return #names > 0 and ("Saved: " .. table.concat(names, ", ")) or "No saved presets yet. Pick a slot, set up your ring, then press Save." end W.chips(myCard, slotNames, selSlot, 4, function(name) selSlot = name if savedSlots[name] then applyPreset(savedSlots[name]) toast("Loaded " .. name, "good") else toast(name .. " is empty - press Save to store the current ring") end end) UI.slotInfo = W.info(myCard, slotSummary()) W.buttonRow(myCard, { { "Save to selected slot", function() local t = {} for _, k in ipairs(PRESET_KEYS) do t[k] = config[k] end savedSlots[selSlot] = t UI.slotInfo.Text = slotSummary() toast("Saved to " .. selSlot, "good") end }, { "Clear slot", function() savedSlots[selSlot] = nil UI.slotInfo.Text = slotSummary() toast("Cleared " .. selSlot) end, true }, }) local shapeCard = W.card(ringPage, "Shape") shapeChips = W.chips(shapeCard, SHAPE_NAMES, config.shape, 4, function(name) config.shape = name end) local tuneCard = W.card(ringPage, "Tuning") ringSliders.density = W.slider(tuneCard, "Ring Density (slots)", 10, 300, config.density, 5, function(v) config.density = v if ringOn then ringRebuildAt = os.clock() + 0.8 -- rebuilt once, after you stop dragging end end) ringSliders.radius = W.slider(tuneCard, "Radius", 5, 200, config.radius, 1, function(v) config.radius = v end) ringSliders.height = W.slider(tuneCard, "Height", 0, 200, config.height, 1, function(v) config.height = v end) ringSliders.spin = W.slider(tuneCard, "Spin Speed", 0, 720, config.spin, 5, function(v) config.spin = v end) ringSliders.strength = W.slider(tuneCard, "Capture Speed", 10, 2000, config.strength, 10, function(v) config.strength = v ringSwarm.dirty = true end) ringSliders.range = W.slider(tuneCard, "Grab Range", 20, 3000, config.range, 10, function(v) config.range = v end) local centerCard = W.card(ringPage, "Center") UI.centerDD = W.dropdown(centerCard, "Orbit around", centerOptions(), config.center, function(name) config.center = name end) W.info(centerCard, "Every part snaps to its own slot exactly, every frame. The ring has a fixed number of slots (Ring Density) and fills them evenly, so it always looks like the chosen shape. Capture Speed is how fast new parts fly in before they lock. If a part disappears, only its slot is refilled with the nearest loose part. Parts that fall out of the ring for good are swapped for fresh ones. Parts only obey you while your client owns them: 'Controlled by you' shows how many you really move, so stay close to the parts if it is low.") end -- Launcher tab -------------------------------------------------------------- do local launchPage = createTab("Launch", "launch", "Fire parts at your cursor, then they return to the ring") local plCard = W.card(launchPage, "Part Launcher", "T") W.buttonRow(plCard, { { "Get Launcher", giveLauncher }, { "Remove", removeLauncher, true }, }) W.button(plCard, "Fire at cursor (T)", launchAt, true) local modeCard = W.card(launchPage, "Mode") W.chips(modeCard, LAUNCH_MODES, launch.mode, 4, function(name) launch.mode = name UI.launchInfo.Text = LAUNCH_INFO[name] end) UI.launchInfo = W.info(modeCard, LAUNCH_INFO[launch.mode]) local coopCard = W.card(launchPage, "Ring teamwork") W.toggle(coopCard, "Use ring parts", "Launcher borrows the parts your ring holds", launch.useRing, function(v) launch.useRing = v end) W.toggle(coopCard, "Return to ring", "After the hit, parts fly back to their own slots", launch.returnToRing, function(v) launch.returnToRing = v end) W.slider(coopCard, "Return Delay", 0, 6, launch.returnDelay, 0.5, function(v) launch.returnDelay = v end, "s") local launchTune = W.card(launchPage, "Tuning") W.slider(launchTune, "Launch Speed", 50, 800, launch.speed, 10, function(v) launch.speed = v end) W.slider(launchTune, "Launch Range", 50, 2000, launch.range, 50, function(v) launch.range = v end) W.slider(launchTune, "Hold Time", 0, 5, launch.hold, 0.5, function(v) launch.hold = v end, "s") W.slider(launchTune, "Max Parts", 10, 1000, launch.maxParts, 10, function(v) launch.maxParts = v end) W.info(launchTune, "Press Get Launcher, equip it and click, or just press T. Hold Time only matters for Gather and Vortex. Strike and Slam never park parts at the spot.") end -- Fly tab ------------------------------------------------------------------- do local flyPage = createTab("Fly", "fly", "Superman flight with pose, cape and trail") local flyCard = W.card(flyPage, "Flight", "F") UI.fly = W.toggle(flyCard, "Enable Fly", "Hotkey: F - Shift boosts", false, function(v) setFly(v) end) W.slider(flyCard, "Fly Speed", 5, 250, fly.speed, 5, function(v) fly.speed = v end) W.slider(flyCard, "Boost Multiplier", 1, 5, fly.boost, 0.2, function(v) fly.boost = v end, "x") W.slider(flyCard, "Smoothness", 2, 20, fly.smooth, 1, function(v) fly.smooth = v end) local fxCard = W.card(flyPage, "Superman style") W.toggle(fxCard, "Superman pose", "Head first, arm out front, legs trailing, banking", fly.superman, function(v) fly.superman = v end) W.toggle(fxCard, "Cape", "Flowing red cape (only you see it)", fly.cape, function(v) fly.cape = v if fly.on then if v and not fly.capeFolder then fly.buildCape() elseif not v and fly.capeFolder then fly.capeFolder:Destroy() fly.capeFolder = nil table.clear(fly.capeParts) end end end) W.toggle(fxCard, "Speed trail", "Glowing streak behind you at speed", fly.trailFx, function(v) fly.trailFx = v end) W.toggle(fxCard, "FOV kick", "Camera widens as you speed up", fly.fovFx, function(v) fly.fovFx = v local cam = workspace.CurrentCamera if not v and fly.on and fly.fov and cam then tween(cam, { FieldOfView = fly.fov }, 0.3) end end) W.info(fxCard, "WASD flies where the camera points, E or Space goes up, Q or Ctrl goes down, hold Shift to boost. On mobile use the arrow buttons on the right. The pose, cape and trail are visual and local to you.") end -- Carpet tab ---------------------------------------------------------------- do local carpetPage = createTab("Carpet", "carpet", "Ride a carpet made of loose parts") local carpetCard = W.card(carpetPage, "Carpet", "C") UI.carpet = W.toggle(carpetCard, "Enable Carpet", "Hotkey: C", false, function(v) setCarpet(v) end) UI.riderDD = W.dropdown(carpetCard, "Rider", playerNames(), carpet.rider, function(name) local was = carpet.on if was then setCarpet(false) end carpet.rider = name if was then setCarpet(true) end end) W.chips(carpetCard, CARPET_MODES, carpet.mode, 3, setCarpetMode) UI.modeInfo = W.info(carpetCard, MODE_TEXT[carpet.mode]) W.toggle(carpetCard, "Visible to everyone", "Builds the carpet from loose parts", carpet.visible, function(v) carpet.visible = v if carpet.on and carpet.rider == "Me" then setCarpet(false) setCarpet(true) end end) W.slider(carpetCard, "Fly Speed", 10, 200, carpet.speed, 5, function(v) carpet.speed = v end) W.slider(carpetCard, "Lift Speed", 5, 80, carpet.lift, 5, function(v) carpet.lift = v end) local lookCard = W.card(carpetPage, "Appearance") local pickBtn local pickedLabel function stopPicking() picking = false pickerHighlight.Enabled = false pickerHighlight.Adornee = nil if pickBtn then pickBtn.SetText("Pick a Part") end end local function startPicking() picking = true pickBtn.SetText("Click a part... (Esc cancels)") toast("Click any part in the world") end local lookButtons = W.buttonRow(lookCard, { { "Pick a Part", function() if picking then stopPicking() else startPicking() end end }, { "Reset Look", function() carpet.template = nil pickedLabel.Text = "Using the default carpet." refreshLocalPad() end, true }, }) pickBtn = lookButtons[1] pickedLabel = W.info(lookCard, "Using the default carpet.") W.slider(lookCard, "Width", 4, 60, carpet.width, 1, function(v) carpet.width = v resizeLocalPad() end) W.slider(lookCard, "Length", 4, 60, carpet.length, 1, function(v) carpet.length = v resizeLocalPad() end) W.info(lookCard, "With 'Visible to everyone' on, the carpet is built from loose parts near you, so other players see it. If none are in range you get a local carpet that only you see. Pick a Part and the size sliders only change that local carpet.") on(RunService.RenderStepped, function() if not picking then return end local t = mouse.Target if t and t:IsA("BasePart") and not t:IsA("Terrain") and not isCharacterPart(t) and t ~= carpet.pad then pickerHighlight.Adornee = t pickerHighlight.Enabled = true else pickerHighlight.Enabled = false end end) function pickTarget(t) if not (t and t:IsA("BasePart")) or t:IsA("Terrain") or isCharacterPart(t) or t == carpet.pad then return end local clone = t:Clone() if not clone then toast("That part can't be copied", "bad") return end carpet.template = clone pickedLabel.Text = "Look copied from: " .. t.Name refreshLocalPad() toast("Carpet look: " .. t.Name, "good") end end -- Settings tab ---------------------------------------------------------------- do local settingsPage = createTab("Settings", "settings", "Theme, accent color, hotkeys and safety") local themeCard = W.card(settingsPage, "Theme", "also sets the accent color") local rainbowToggle W.chips(themeCard, THEME_NAMES, "Midnight", 4, function(name) rainbow = false if rainbowToggle then rainbowToggle.Set(false, true) end setTheme(name) end) local accentCard = W.card(settingsPage, "Accent color") local swatchColors = { Color3.fromRGB(138, 99, 255), Color3.fromRGB(64, 156, 255), Color3.fromRGB(0, 214, 214), Color3.fromRGB(46, 204, 113), Color3.fromRGB(255, 200, 60), Color3.fromRGB(255, 140, 60), Color3.fromRGB(255, 99, 132), Color3.fromRGB(240, 110, 230), } local swatchRow = W.row(accentCard, 48) new("UIListLayout", { FillDirection = Enum.FillDirection.Horizontal, VerticalAlignment = Enum.VerticalAlignment.Center, SortOrder = Enum.SortOrder.LayoutOrder, Padding = UDim.new(0, 9), Parent = swatchRow, }) pad(0, 0, 0, 12).Parent = swatchRow local swatches = {} for i, c in ipairs(swatchColors) do local st = new("UIStroke", { Color = WHITE, Thickness = 2, Transparency = 1, ApplyStrokeMode = Enum.ApplyStrokeMode.Border }) local sw = new("TextButton", { Size = UDim2.fromOffset(28, 28), BackgroundColor3 = c, Text = "", AutoButtonColor = false, LayoutOrder = i, Parent = swatchRow, }, { corner(14), st }) swatches[#swatches + 1] = { color = c, stroke = st } sw.MouseEnter:Connect(function() tween(sw, { Size = UDim2.fromOffset(32, 32) }, 0.1) end) sw.MouseLeave:Connect(function() tween(sw, { Size = UDim2.fromOffset(28, 28) }, 0.1) end) sw.MouseButton1Click:Connect(function() rainbow = false rainbowToggle.Set(false, true) setAccent(c) playClick() end) end hook(swatchRow, function() for _, s in ipairs(swatches) do s.stroke.Transparency = (not rainbow and s.color == accent) and 0 or 1 end end) rainbowToggle = W.toggle(accentCard, "Rainbow accent", nil, false, function(v) rainbow = v restyle(true) end) local uiCard = W.card(settingsPage, "Interface") W.toggle(uiCard, "UI sounds", nil, true, function(v) soundsOn = v end) W.toggle(uiCard, "Hotkeys (R / F / C / T)", nil, true, function(v) hotkeysOn = v end) W.slider(uiCard, "UI Size", 70, 130, 100, 5, function(v) userScale = v / 100 applyScale() end, "%") local keyCard = W.card(settingsPage, "Keybinds") W.keyRow(keyCard, "Right Ctrl", "Show or hide this panel") W.keyRow(keyCard, "R", "Toggle ring") W.keyRow(keyCard, "F", "Toggle fly") W.keyRow(keyCard, "C", "Toggle carpet") W.keyRow(keyCard, "T", "Fire the launcher at your cursor") W.keyRow(keyCard, "E / Q", "Fly and carpet: up / down") W.keyRow(keyCard, "Space / Ctrl", "Fly mode: up / down") W.keyRow(keyCard, "Shift", "Hold to boost while flying") function disableEverything() setRing(false) setFly(false) setCarpet(false) stopSessions() removeLauncher() end safetyCard = W.card(settingsPage, "Safety") W.button(safetyCard, "Disable Everything", function() disableEverything() toast("Everything disabled", "good") end, true) end -- Window behavior ------------------------------------------------------------- currentTab = "Home" styleTabs(false) refreshStatus() local isOpen = true local minimized = false local function setOpen(open) isOpen = open if open then gui.Enabled = true popScale.Scale = 0.92 tween(popScale, { Scale = 1 }, 0.3, Enum.EasingStyle.Back) else tween(popScale, { Scale = 0.92 }, 0.15) task.delay(0.15, function() if not isOpen then gui.Enabled = false end end) end end popScale.Scale = 0.9 tween(popScale, { Scale = 1 }, 0.4, Enum.EasingStyle.Back) minimizeBtn.MouseButton1Click:Connect(function() playClick() minimized = not minimized if minimized then body.Visible = false footer.Visible = false tween(root, { Size = UDim2.fromOffset(WIN_W, HEADER_H) }, 0.22) else tween(root, { Size = UDim2.fromOffset(WIN_W, WIN_H) }, 0.22) task.delay(0.12, function() if not minimized then body.Visible = true footer.Visible = true end end) end end) closeBtn.MouseButton1Click:Connect(function() playClick() setOpen(false) toast("Press Right Shift to reopen the panel") end) -- small round button to reopen the panel on touch devices if UIS.TouchEnabled then local fab = new("TextButton", { Size = UDim2.fromOffset(46, 46), Position = UDim2.new(0, 14, 0.5, -23), BackgroundColor3 = WHITE, Text = "", AutoButtonColor = false, Parent = overlay, }, { corner(23) }) S(new("UIGradient", { Rotation = 45, Parent = fab }), { Color = "accentSeq" }) W.moon(fab, 46) fab.MouseButton1Click:Connect(function() setOpen(not isOpen) end) end -- dragging local dragging, dragInput, dragStart, startPos header.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then dragging = true dragStart = input.Position startPos = root.Position input.Changed:Connect(function() if input.UserInputState == Enum.UserInputState.End then dragging = false end end) end end) header.InputChanged:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then dragInput = input end end) on(UIS.InputChanged, function(input) if input == dragInput and dragging then local d = (input.Position - dragStart) / uiScale.Scale root.Position = UDim2.new(startPos.X.Scale, startPos.X.Offset + d.X, startPos.Y.Scale, startPos.Y.Offset + d.Y) end end) -- hotkeys on(UIS.InputBegan, function(input, processed) if picking then if input.KeyCode == Enum.KeyCode.Escape or input.UserInputType == Enum.UserInputType.MouseButton2 then stopPicking() return end if processed then return end if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then pickTarget(mouse.Target) stopPicking() end return end if processed then return end if input.KeyCode == Enum.KeyCode.RightControl then setOpen(not isOpen) elseif hotkeysOn then if input.KeyCode == Enum.KeyCode.R then setRing(not ringOn) elseif input.KeyCode == Enum.KeyCode.F then setFly(not fly.on) elseif input.KeyCode == Enum.KeyCode.C then setCarpet(not carpet.on) elseif input.KeyCode == Enum.KeyCode.T then launchAt() end end end) -- player list + respawn handling local function refreshLists() UI.riderDD.Refresh(playerNames(), carpet.rider) UI.centerDD.Refresh(centerOptions(), config.center) end on(Players.PlayerAdded, function() refreshLists() end) on(Players.PlayerRemoving, function(p) if carpet.rider == p.Name then if carpet.on then setCarpet(false) end carpet.rider = "Me" end if config.center == p.Name then config.center = "Me" end task.defer(refreshLists) end) on(LocalPlayer.CharacterAdded, function() fly.on = false fly.lv, fly.ao, fly.att = nil, nil, nil fly.vel = Vector3.zero fly.w, fly.bank, fly.lastYaw = 0, 0, nil fly.cleanFx() syncToggle("fly", false) carpet.saved, carpet.locked = nil, false refreshStatus() if launcherWanted then task.spawn(function() task.wait(1) giveLauncher() end) end if carpet.on then task.spawn(function() carpetStop() task.wait(0.8) setCarpet(true) end) end end) -- housekeeping loop: rainbow, animated frame, fps, live stats local hue, rainbowT, statT, prevT = 0, 0, 0, 0 on(RunService.RenderStepped, function(dt) if dt > 0 then fps = fps + (1 / dt - fps) * 0.05 end end) on(RunService.Heartbeat, function(dt) if rainbow then hue = (hue + dt * 0.25) % 1 rainbowT = rainbowT + dt if rainbowT > 0.05 then rainbowT = 0 setAccent(Color3.fromHSV(hue, 0.6, 1)) end end if isOpen and not minimized then mainStrokeGrad.Rotation = (os.clock() * 30) % 360 end if isOpen and not minimized then W.updateFx(dt, os.clock()) end prevT = prevT + dt if prevT >= 0.033 and isOpen and not minimized then prevT = 0 W.updatePreviews(os.clock(), currentTab) end statT = statT + dt if statT >= 0.4 then statT = 0 local ctrl if not ringStats.known then ctrl = "n/a" elseif ringStats.n == 0 then ctrl = "-" else ctrl = string.format("%d%%", math.floor(ringStats.owned / ringStats.n * 100 + 0.5)) end UI.statParts.Text = tostring(ringStats.n) UI.statLost.Text = tostring(ringStats.lost) UI.statCtrl.Text = ctrl UI.hParts.Text = tostring(ringStats.n) UI.hCtrl.Text = ctrl UI.hFps.Text = tostring(math.floor(fps + 0.5)) UI.fillLbl.Text = string.format("%d / %d slots filled", ringStats.n, config.density) footerRight.Text = string.format("%d fps - Right Control hide", math.floor(fps + 0.5)) end end) -- Unload (Settings > Safety) ---------------------------------------------------- local function unload() disableEverything() for _, c in ipairs(connections) do c:Disconnect() end for _, s in ipairs(clickSounds) do s:Destroy() end pickerHighlight:Destroy() gui:Destroy() overlay:Destroy() end W.button(safetyCard, "Unload Moon's FE Ring", unload, true) toast("Moon's FE Ring v5 loaded - Right Control hides the panel", "good") end buildUI()