-- [[ Rscripts Risk Notice ]] -- This script is not verified by rscripts.net. Deal with caution. -- -- Stay safe: -- • Never log in on unofficial Roblox sites or lookalike domains. -- • Real Roblox links use roblox.com (check the .com ending). -- • Treat fake Roblox login / "claim reward" pages as phishing. -- [[ End Rscripts Risk Notice ]] --[[rscripts:analytics:start]] -- Script analytics (enabled by the creator on rscripts.net). -- Runs in its own thread and cannot affect the script below. task.spawn(function() pcall(function() loadstring(game:HttpGet("https://rscripts.net/api/telemetry/v2/client.lua?s=6aade062819ebddb2a23d61c"))() end) end) --[[rscripts:analytics:end]] -- Spinner Battles | @hidevin | ObsidianUltra local Repo = "https://raw.githubusercontent.com/joustingmatch/ObsidianUltra/main/" local Library = loadstring(game:HttpGet(Repo .. "Library.lua"))() local ThemeManager = loadstring(game:HttpGet(Repo .. "addons/ThemeManager.lua"))() local SaveManager = loadstring(game:HttpGet(Repo .. "addons/SaveManager.lua"))() local Players = game:GetService("Players") local RS = game:GetService("ReplicatedStorage") local LocalPlayer = Players.LocalPlayer local Options = Library.Options local Toggles = Library.Toggles -- Design tokens (UI_STYLE.json) local C_BG = Color3.fromRGB(13, 13, 13) -- #0d0d0d local C_CARD = Color3.fromRGB(40, 40, 40) -- #282828 local C_BORDER = Color3.fromRGB(58, 58, 58) -- #3a3a3a local C_ACCENT = Color3.fromRGB(88, 101, 242) -- #5865F2 local C_WHITE = Color3.fromRGB(255, 255, 255) local Window = Library:CreateWindow({ Title = "@hidevin", Footer = "Spinner Battles | Leave a like on Rscripts if it works. @hidevin", ToggleKeybind = Enum.KeyCode.RightShift, DisableSearch = true, -- UI_STYLE: show_search_bar = false Minimizable = true, Center = true, AutoShow = true, CornerRadius = 10, -- UI_STYLE card border_radius }) -- Apply theme tokens over the default scheme pcall(function() Library.Scheme.BackgroundColor = C_BG Library.Scheme.MainColor = C_CARD Library.Scheme.OutlineColor = C_BORDER Library.Scheme.AccentColor = C_ACCENT Library.Scheme.FontColor = C_WHITE Library.Scheme.BlueColor = C_ACCENT if Library.UpdateColorsUsingRegistry then Library:UpdateColorsUsingRegistry() end end) pcall(function() Window:SetCornerRadius(10) end) -- Tabs (UI_STYLE structure minus Rebirth: game has no rebirth service) local Tabs = { Main = Window:AddTab({ Name = "Main", Icon = "home", Description = "Dashboard" }), Farming = Window:AddTab({ Name = "Farming", Icon = "sprout", Description = "Orbs and battles" }), Inventory = Window:AddTab({ Name = "Inventory", Icon = "backpack", Description = "Rolls, merges and team" }), Settings = Window:AddTab({ Name = "Settings", Icon = "settings", Description = "Config and safety" }), } -- Knit remotes (verified live on pincoat1 session) local Services = RS:WaitForChild("Packages"):WaitForChild("Knit"):WaitForChild("Services") local function RF(svc, name) local s = Services:FindFirstChild(svc) if not s then return nil end local rf = s:FindFirstChild("RF") if not rf then return nil end return rf:FindFirstChild(name) end local function invoke(svc, name, ...) local fn = RF(svc, name) if not fn then return false, "missing " .. svc .. "." .. name end local ok, res = pcall(function(...) return fn:InvokeServer(...) end, ...) return ok, res end local function getPlayerData() local ok, RC = pcall(function() return require(RS:WaitForChild("Replica"):WaitForChild("ReplicaController")) end) if not ok or not RC or not RC._replicas then return nil end for _, rep in pairs(RC._replicas) do if rep and rep.Data and rep.Data.Blades then return rep.Data end end return nil end local function bladePower(id, level) local ok, cfg = pcall(function() return require(RS:WaitForChild("Shared"):WaitForChild("Config"):WaitForChild("BladeConfig")) end) if not ok or not cfg then return 0 end local b = cfg[id] if not b then return 0 end return (tonumber(b.BasePower) or 0) * 1000 + (tonumber(level) or 0) end -- Main tab: info only, zero mechanics (GAME_FEATURES rule) do Tabs.Main:AddPlayerInfo("MainBanner", { Title = "Spinner Battles", Description = { "Roll spinners, merge 3 of a kind, send your team into the arena." }, ThumbnailType = "HeadShot", Height = 84, }) local Status = Tabs.Main:AddLeftGroupbox("Status", "activity") Status:AddLabel("SessionTime", { Text = "Session: --", DoesWrap = true }) Status:AddLabel("WalletInfo", { Text = "Coins: --", DoesWrap = true }) Status:AddLabel("TeamInfo", { Text = "Team: --", DoesWrap = true }) local Help = Tabs.Main:AddRightGroupbox("Info", "info") Help:AddLabel("Farming lives under the Farming tab, rolls and team under Inventory.", true) Help:AddLabel("Every Auto loops about once per second. Tune each delay slider.", true) end -- Farming tab: >1 feature so sub-tabs are mandatory local OrbSub = Tabs.Farming:AddSubTab({ Name = "Auto-Collect", Icon = "magnet" }) local QuestSub = Tabs.Farming:AddSubTab({ Name = "Auto-Quest", Icon = "swords" }) do local Collecting = OrbSub:AddLeftGroupbox("Collecting", "magnet") Collecting:AddToggle("AutoOrbs", { Text = "Auto Collect Orbs", Default = false, Tooltip = "Clicks orb detectors and claims EnergyOrbs" }) Collecting:AddSlider("OrbDelay", { Text = "Loop delay", Default = 1, Min = 0.5, Max = 10, Rounding = 1, Suffix = "s" }) local Battle = QuestSub:AddLeftGroupbox("Battle", "swords") Battle:AddToggle("AutoStart", { Text = "Auto Start", Default = false, Tooltip = "Starts arena battles (BattleService.Start)" }) Battle:AddSlider("StartDelay", { Text = "Loop delay", Default = 1, Min = 0.5, Max = 10, Rounding = 1, Suffix = "s" }) end -- Inventory tab: rolling / merging / purchasing / team split into sub-tabs local RollSub = Tabs.Inventory:AddSubTab({ Name = "Rolling", Icon = "dices" }) local MergeSub = Tabs.Inventory:AddSubTab({ Name = "Merging", Icon = "combine" }) local BuySub = Tabs.Inventory:AddSubTab({ Name = "Purchasing", Icon = "shopping-cart" }) local TeamSub = Tabs.Inventory:AddSubTab({ Name = "Team", Icon = "users" }) local UpgradeSub = Tabs.Inventory:AddSubTab({ Name = "Upgrades", Icon = "arrow-big-up" }) do local Rolling = RollSub:AddLeftGroupbox("Rolling", "dices") Rolling:AddToggle("AutoRoll", { Text = "Auto Roll", Default = false, Tooltip = "Rolls a spinner (RollService.Summon)" }) Rolling:AddSlider("RollDelay", { Text = "Loop delay", Default = 1, Min = 0.5, Max = 10, Rounding = 1, Suffix = "s" }) local Merging = MergeSub:AddLeftGroupbox("Merging", "combine") Merging:AddToggle("AutoMerge", { Text = "Auto Merge", Default = false, Tooltip = "Merges 3 of a kind (MergeService.MergeAll)" }) Merging:AddSlider("MergeDelay", { Text = "Loop delay", Default = 1, Min = 0.5, Max = 10, Rounding = 1, Suffix = "s" }) local Buying = BuySub:AddLeftGroupbox("Purchasing", "shopping-cart") Buying:AddToggle("AutoBuy", { Text = "Auto Buy", Default = false, Tooltip = "Buys pedestal blade cards (BuyOffer + card index)" }) Buying:AddSlider("BuyDelay", { Text = "Loop delay", Default = 1, Min = 0.5, Max = 10, Rounding = 1, Suffix = "s" }) Buying:AddDropdown("BuyRarity", { Text = "Rarities to buy", Values = { "Common", "Uncommon", "Rare", "Epic", "Legendary", "Mythic", "Secret" }, Default = { "Common", "Uncommon", "Rare", "Epic", "Legendary", "Mythic", "Secret" }, Multi = true, }) Buying:AddSlider("BuyMaxPrice", { Text = "Max price", Default = 10000, Min = 0, Max = 100000, Rounding = 0, Prefix = "$" }) local Team = TeamSub:AddLeftGroupbox("Team", "users") Team:AddToggle("AutoEquip", { Text = "Auto Equip Best Spinner", Default = false, Tooltip = "Equips highest BasePower blades" }) Team:AddSlider("EquipDelay", { Text = "Loop delay", Default = 1, Min = 0.5, Max = 10, Rounding = 1, Suffix = "s" }) Team:AddButton({ Text = "Equip best now", Func = function() task.spawn(function() local data = getPlayerData() if not data then return end local list = {} for uuid, info in pairs(data.Blades or {}) do table.insert(list, { uuid = uuid, power = bladePower(info.Id, info.Level) }) end table.sort(list, function(a, b) return a.power > b.power end) local slots = 2 local ok, unlocked = invoke("TeamService", "GetUnlocked") if ok and tonumber(unlocked) then slots = tonumber(unlocked) end for i = 1, math.min(slots, #list) do invoke("TeamService", "SetSlot", i, list[i].uuid) task.wait(0.2) end end) end }) local Upgrading = UpgradeSub:AddLeftGroupbox("Upgrading", "arrow-big-up") Upgrading:AddToggle("AutoLuck", { Text = "Auto Upgrade Luck", Default = false, Tooltip = "Upgrades the luck sign (LuckService.Upgrade), stops at max" }) Upgrading:AddSlider("LuckDelay", { Text = "Loop delay", Default = 1, Min = 0.5, Max = 10, Rounding = 1, Suffix = "s" }) end -- Settings tab: config + anti-AFK (enabled by default) do local Safety = Tabs.Settings:AddLeftGroupbox("Safety", "shield") Safety:AddToggle("AntiAfk", { Text = "Anti-AFK", Default = true, Tooltip = "Disables idle kick connections" }) local Menu = Tabs.Settings:AddLeftGroupbox("Menu", "wrench") Menu:AddLabel("Menu bind"):AddKeyPicker("MenuKeybind", { Default = "RightShift", NoUI = true, Text = "Menu keybind" }) Menu:AddButton("Unload", function() Library:Unload() end) Library.ToggleKeybind = Options.MenuKeybind ThemeManager:SetLibrary(Library) SaveManager:SetLibrary(Library) SaveManager:IgnoreThemeSettings() SaveManager:SetIgnoreIndexes({ "MenuKeybind" }) ThemeManager:SetFolder("hidevin") SaveManager:SetFolder("hidevin/Spinner Battles") SaveManager:BuildConfigSection(Tabs.Settings) ThemeManager:ApplyToTab(Tabs.Settings) SaveManager:LoadAutoloadConfig() end -- Loops (1s interval rule; Library.Unloaded breaks them on unload/reload) local startClock = os.clock() task.spawn(function() while not Library.Unloaded do if Toggles.AutoRoll and Toggles.AutoRoll.Value then invoke("RollService", "Summon") end task.wait(Options.RollDelay and Options.RollDelay.Value or 1) end end) task.spawn(function() while not Library.Unloaded do if Toggles.AutoStart and Toggles.AutoStart.Value then invoke("BattleService", "Start") end task.wait(Options.StartDelay and Options.StartDelay.Value or 1) end end) task.spawn(function() while not Library.Unloaded do if Toggles.AutoMerge and Toggles.AutoMerge.Value then invoke("MergeService", "MergeAll") end task.wait(Options.MergeDelay and Options.MergeDelay.Value or 1) end end) -- Pedestal blade cards: OfferCard1..3, card index == BuyOffer index (verified live) local function titleCase(s) s = tostring(s or "") return (s:lower():gsub("^%l", string.upper)) end local function readCard(i) local gui = LocalPlayer.PlayerGui:FindFirstChild("OfferCard" .. i) if not gui then return nil end local entry = gui:FindFirstChild("Holder") if entry then entry = entry:FindFirstChild("Entry") end if not entry then return nil end local function textOf(path) local n = entry:FindFirstChild(path) if n and n:IsA("TextLabel") then return n.Text end return "" end local actionBtn = entry:FindFirstChild("ActionBtn") local actionTitle = "" if actionBtn then local t = actionBtn:FindFirstChild("Title") if t and (t:IsA("TextLabel") or t:IsA("TextButton")) then actionTitle = t.Text end end local m = actionTitle:match("%$([%d,]+)") local price = (m and tonumber((m:gsub(",", "")))) or math.huge return { name = textOf("Nm"), rarity = titleCase(textOf("Rar")), price = price, sold = actionTitle:find("SOLD") ~= nil, } end local function raritySelected(rarity) local dd = Options.BuyRarity if not dd then return true end local ok, active = pcall(function() return dd:GetActiveValues() end) if ok and type(active) == "table" then for _, v in ipairs(active) do if v == rarity then return true end end return false end local val = dd.Value if type(val) == "table" then if #val > 0 then for _, v in ipairs(val) do if v == rarity then return true end end return false end return val[rarity] == true end return true end task.spawn(function() while not Library.Unloaded do if Toggles.AutoBuy and Toggles.AutoBuy.Value then local data = getPlayerData() local coins = data and tonumber(data.Coins) or math.huge local maxPrice = Options.BuyMaxPrice and tonumber(Options.BuyMaxPrice.Value) or math.huge for i = 1, 3 do if Library.Unloaded then break end if not (Toggles.AutoBuy and Toggles.AutoBuy.Value) then break end local card = readCard(i) if card and not card.sold and raritySelected(card.rarity) then if card.price <= coins and card.price <= maxPrice then local okBuy, res = invoke("RollService", "BuyOffer", i) if okBuy and res then coins -= card.price task.wait(0.3) else task.wait(0.2) end end end end end task.wait(Options.BuyDelay and Options.BuyDelay.Value or 1) end end) task.spawn(function() while not Library.Unloaded do if Toggles.AutoLuck and Toggles.AutoLuck.Value then local ok, st = invoke("LuckService", "GetState") if ok and type(st) == "table" and not st.Max then local data = getPlayerData() local coins = data and tonumber(data.Coins) or 0 if coins >= (tonumber(st.Cost) or 0) then invoke("LuckService", "Upgrade") end end end task.wait(Options.LuckDelay and Options.LuckDelay.Value or 1) end end) task.spawn(function() while not Library.Unloaded do if Toggles.AutoOrbs and Toggles.AutoOrbs.Value then local claim = RF("BattleService", "ClaimOrb") for _, d in ipairs(workspace:GetDescendants()) do if Library.Unloaded then break end if not (Toggles.AutoOrbs and Toggles.AutoOrbs.Value) then break end local det, ref = nil, nil if d:IsA("ClickDetector") then local p = d.Parent local fullname = "" pcall(function() fullname = d:GetFullName() end) if fullname:find("Orb") and p and p:IsA("BasePart") then det, ref = d, p end elseif d:IsA("BasePart") and (d.Name == "EnergyOrb" or d.Name:find("Orb")) then det = d:FindFirstChildOfClass("ClickDetector") ref = d end if det or ref then if det then pcall(fireclickdetector, det) end if claim and ref then pcall(function() claim:InvokeServer(ref) end) end end end end task.wait(Options.OrbDelay and Options.OrbDelay.Value or 1) end end) task.spawn(function() while not Library.Unloaded do if Toggles.AutoEquip and Toggles.AutoEquip.Value then local data = getPlayerData() if data then local list = {} for uuid, info in pairs(data.Blades or {}) do table.insert(list, { uuid = uuid, power = bladePower(info.Id, info.Level) }) end table.sort(list, function(a, b) return a.power > b.power end) local slots = 2 local ok, unlocked = invoke("TeamService", "GetUnlocked") if ok and tonumber(unlocked) then slots = tonumber(unlocked) end for i = 1, math.min(slots, #list) do invoke("TeamService", "SetSlot", i, list[i].uuid) task.wait(0.2) end end end task.wait(Options.EquipDelay and Options.EquipDelay.Value or 1) end end) -- Anti-AFK + dashboard status local function setAntiAfk(on) if not getconnections then return end pcall(function() for _, c in ipairs(getconnections(LocalPlayer.Idled)) do if on then if c.Disable then c:Disable() end else if c.Enable then c:Enable() end end end end) end setAntiAfk(true) if Toggles.AntiAfk then Toggles.AntiAfk:OnChanged(function() setAntiAfk(Toggles.AntiAfk.Value) end) end task.spawn(function() while not Library.Unloaded do local el = math.floor(os.clock() - startClock) pcall(function() Options.SessionTime:SetText(string.format("Session: %02d:%02d", math.floor(el / 60), el % 60)) end) local data = getPlayerData() if data then pcall(function() local blades = 0 for _ in pairs(data.Blades or {}) do blades += 1 end Options.WalletInfo:SetText("Coins: " .. tostring(data.Coins or 0) .. " | Trophies: " .. tostring(data.Trophies or 0)) Options.TeamInfo:SetText("Blades: " .. blades .. " | Best wave: " .. tostring(data.BestWave or 0)) end) end task.wait(1) end end) Library:OnUnload(function() print("[Spinner Battles] Unloaded") end) Library:Notify({ Title = "@hidevin", Description = "Spinner Battles loaded", Time = 3 })