--!nonstrict --[[ Boxing Battles Hub ------------------ One file, five tools for Boxing Battles: 1. Training - grows your strength with the game's free auto train while you stand in the gym 1.1 Weights - buys and equips the dumbbells you prefer, as soon as you are allowed to use them 2. Money - parks you on the AFK pad so cash keeps coming in 3. Battles - queues you into a ring, fights for you and starts the next one - Kill aura : picks the closest opponent and keeps you on top of them - Fly above : hovers you over the target while you hit - Auto hits : presses the punch over and over - Wait timer : how long to wait for a match before trying another ring 4. Rotation - flips between money, battles and training on a timer Press RightControl to show or hide the window. Every switch starts off - nothing happens until you turn it on. Settings are saved in the BoxingBattlesHub folder. ]] ---------------------------------------------------------------------- -- UI library ---------------------------------------------------------------------- local UI_SOURCES = { "https://raw.githubusercontent.com/bobloscript/Robly_Airflow/main/Source.luau", "https://raw.githubusercontent.com/PookiePepelsss/Airflow-UI/refs/heads/main/Source.luau", } local Airflow for _, url in ipairs(UI_SOURCES) do local ok, lib = pcall(function() return loadstring(game:HttpGet(url))() end) if ok and type(lib) == "table" and type(lib.CreateWindow) == "function" then Airflow = lib break end end if not Airflow then error("[Boxing Battles Hub] The UI library could not be loaded. Check your connection and retry.") end ---------------------------------------------------------------------- -- Game access ---------------------------------------------------------------------- local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local LocalPlayer = Players.LocalPlayer local Remotes = ReplicatedStorage:WaitForChild("Remotes", 30) if not Remotes then error("[Boxing Battles Hub] This script only works in Boxing Battles.") end local SourceFolder = ReplicatedStorage:WaitForChild("src", 30) local WeightModule = SourceFolder and require(SourceFolder:WaitForChild("Collections", 30):WaitForChild("WeightCollection", 30)) local leaderstats = LocalPlayer:WaitForChild("leaderstats", 30) ---------------------------------------------------------------------- -- Small helpers ---------------------------------------------------------------------- local function money() return leaderstats and leaderstats.Money and leaderstats.Money.Value or 0 end local function strength() return leaderstats and leaderstats.Strength and leaderstats.Strength.Value or 0 end local function streak() return leaderstats and leaderstats.Streak and leaderstats.Streak.Value or 0 end local function rebirths() return leaderstats and leaderstats.Rebirths and leaderstats.Rebirths.Value or 0 end local function short(value) if type(value) ~= "number" then return "-" end local rounded = math.floor(value) local text = tostring(rounded) local grouped = text:reverse():gsub("(%d%d%d)", "%1 "):reverse() return (grouped:gsub("^%s+", "")) end local function character() local char = LocalPlayer.Character if char and char.Parent then return char end return nil end local function humanoid() local char = character() return char and char:FindFirstChildOfClass("Humanoid") or nil end local function rootPosition() local char = character() if not char then return nil end local root = char:FindFirstChild("HumanoidRootPart") return root and root.Position or nil end local dataCache, dataCacheAt = nil, 0 local function playerData(force) local now = os.clock() if not force and dataCache and (now - dataCacheAt) < 2 then return dataCache end local ok, data = pcall(function() return Remotes.ReadPlayerData:InvokeServer() end) if ok and type(data) == "table" then dataCache, dataCacheAt = data, now return data end return dataCache end --- Puts the character on a spot. Used for the gym, the AFK pad and the ring pads. local function goTo(position) local char = character() if not char then return false end return pcall(function() char:PivotTo(CFrame.new(position)) end) end local function distanceTo(position) local current = rootPosition() if not current then return math.huge end return (current - position).Magnitude end ---------------------------------------------------------------------- -- Where things are on the map ---------------------------------------------------------------------- local Map = workspace:WaitForChild("Boxing Map", 30) local StandsFolder = Map and Map:FindFirstChild("Dumbbell Stands") local Important = Map and Map:FindFirstChild("Important Stuff") local RingArea = Map and Map:FindFirstChild("Ring Area") local function allWeights() local list = {} if not WeightModule then return list end for name, info in pairs(WeightModule) do table.insert(list, { name = name, price = tonumber(info.Price) or 0, gain = tonumber(info.Strength) or 1, req = tonumber(info.StrengthReq) or 0, robux = info.Price == "Robux", }) end table.sort(list, function(a, b) if a.robux ~= b.robux then return not a.robux end if a.req == b.req then return a.gain < b.gain end return a.req < b.req end) return list end --- Spot next to the rack of a given weight, where training actually counts. local function gymSpot(weightName) local stand = StandsFolder and StandsFolder:FindFirstChild(weightName) local red = stand and stand:FindFirstChild("RedPart", true) if red then return red.Position + Vector3.new(0, 0.8, 3.2) end return Vector3.new(38.6, 5.3, 42) end local WEIGH_LIST = allWeights() local function weights() return WEIGH_LIST end local function afkSpot() local pad = Important and Important:FindFirstChild("AFK Pad") local main = pad and (pad:FindFirstChild("Main") or pad:FindFirstChildWhichIsA("BasePart", true)) if main then return main.Position + Vector3.new(0, 3.4, 0) end return Vector3.new(-12.5, 3.4, 19) end --- All ring pads of a match type, with how many players are standing around them. local function ringPads(matchType) local result = {} if not RingArea then return result end for _, ring in ipairs(RingArea:GetChildren()) do if ring:GetAttribute("MatchType") == matchType then local pads = ring:FindFirstChild("Pads") if pads then for _, padModel in ipairs(pads:GetChildren()) do local padPart = padModel:FindFirstChildWhichIsA("BasePart", true) if padPart and padModel.Name:find("Pad") then local nearby = 0 for _, p in ipairs(Players:GetPlayers()) do if p ~= LocalPlayer and p.Character then local pos = p.Character:GetPivot().Position if (pos - padPart.Position).Magnitude < 16 then nearby += 1 end end end table.insert(result, { ring = ring.Name, name = padModel.Name, position = padPart.Position + Vector3.new(0, 3.2, 0), nearby = nearby, }) end end end end end table.sort(result, function(a, b) return a.nearby < b.nearby end) return result end ---------------------------------------------------------------------- -- Feature state ---------------------------------------------------------------------- local Settings = { -- training trainOn = false, trainAutoBuy = true, trainEquip = true, preferredWeight = "Auto", cashReserve = 0, -- money moneyOn = false, -- battles battleOn = false, ringType = "1v1", killAura = true, auraRadius = 60, flyAbove = true, flyHeight = 6, autoHits = true, hitDelay = 0.2, waitForMatch = 45, maxFightTime = 90, requeueDelay = 3, -- rotation rotationOn = false, rotateMoney = true, rotateBattle = true, rotateTraining = false, moneyMinutes = 5, battleMinutes = 10, trainingMinutes = 5, } local Window, Log local function log(message, color) if Log then Log:Push(tostring(message), color) end end local function notify(title, content, kind) pcall(function() Window:Notify({ Title = title, Content = content, Type = kind or "Info", Duration = 4 }) end) end getgenv().__BoxingHubGen = (getgenv().__BoxingHubGen or 0) + 1 local SCRIPT_GENERATION = getgenv().__BoxingHubGen local function scriptAlive() return getgenv().__BoxingHubGen == SCRIPT_GENERATION end --- A background loop that stops when its generation changes. local loops = {} local function startLoop(name, step) loops[name] = (loops[name] or 0) + 1 local myGeneration = loops[name] task.spawn(function() while loops[name] == myGeneration and scriptAlive() do local ok, err = pcall(step) if not ok then log("" .. name .. ": " .. tostring(err) .. "") task.wait(1) end if loops[name] ~= myGeneration then break end task.wait(0.1) end end) end local function stopLoop(name) loops[name] = (loops[name] or 0) + 1 end ---------------------------------------------------------------------- -- 1. TRAINING: strength at the gym ---------------------------------------------------------------------- local trainingStats = { started = 0, gained = 0, rate = 0, lastSample = 0, lastStrength = 0, equipped = "-" } local UiRefs = {} local function autoTrainEnabled() local data = playerData() return data ~= nil and data.Autoclicking == true end local function setAutoTrain(on) if autoTrainEnabled() == on then return true end Remotes.AutoTrain:FireServer() task.wait(0.35) local data = playerData(true) return data ~= nil and data.Autoclicking == on end local function ownedWeights() local data = playerData() return (data and data.Weights) or {} end local function ownsWeight(name) for _, owned in ipairs(ownedWeights()) do if owned == name then return true end end return false end --- Best weight we already own. local function bestOwnedWeight() local best, bestGain for _, info in ipairs(WEIGH_LIST) do if not info.robux and ownsWeight(info.name) then if not bestGain or info.gain > bestGain then best, bestGain = info.name, info.gain end end end return best end --- Weight we want to be using right now. local function desiredWeight() if Settings.preferredWeight ~= "Auto" then return Settings.preferredWeight end return bestOwnedWeight() end --- Best weight we could buy right now (strength requirement met, cash available). local function nextWeightToBuy() local best for _, info in ipairs(WEIGH_LIST) do if not info.robux and not ownsWeight(info.name) and info.req <= strength() and info.price > 0 and info.price <= money() - Settings.cashReserve then if not best or info.gain > best.gain then best = info end end end return best end local function equipWeight(name) local char = character() local hum = char and char:FindFirstChildOfClass("Humanoid") if not (char and hum) then return false end Remotes.EquipWeights:FireServer(name) task.wait(0.6) local backpack = LocalPlayer:FindFirstChildOfClass("Backpack") local tool = (backpack and backpack:FindFirstChild(name)) or char:FindFirstChild(name) if tool then pcall(function() hum:EquipTool(tool) end) trainingStats.equipped = name return true end return false end local trainingLoopRunning = false local function trainingStep() if not character() then return end -- Stand where training counts: next to the rack of the weight we want to use. local want = desiredWeight() or "Weights [1Kg]" local spot = gymSpot(want) if distanceTo(spot) > 4 then goTo(spot) task.wait(0.35) end -- Buy the next weight when it is allowed and affordable. if Settings.trainAutoBuy and os.clock() - (trainingStats.lastBuyCheck or 0) > 4 then trainingStats.lastBuyCheck = os.clock() local candidate = nextWeightToBuy() if candidate and not ownsWeight(candidate.name) then local target = Settings.preferredWeight ~= "Auto" and Settings.preferredWeight or candidate.name goTo(gymSpot(target)) task.wait(0.35) Remotes.BuyWeights:FireServer(target) task.wait(0.9) playerData(true) if ownsWeight(target) then log("Bought " .. target .. "") end end end -- Put the wanted weight in hand. if Settings.trainEquip then local worn = trainingStats.equipped if desiredWeight() and worn ~= desiredWeight() and ownsWeight(desiredWeight()) then equipWeight(desiredWeight()) end end -- Keep the game's free auto train switched on. if not autoTrainEnabled() then setAutoTrain(true) end -- Strength per minute, for the status row. local now = os.clock() if now - trainingStats.lastSample >= 10 then local delta = strength() - trainingStats.lastStrength if trainingStats.lastSample > 0 then trainingStats.rate = delta * (60 / (now - trainingStats.lastSample)) end trainingStats.lastStrength = strength() trainingStats.lastSample = now end end local function trainingSet(on) Settings.trainOn = on if on then trainingStats.lastStrength = strength() trainingStats.lastSample = os.clock() trainingLoopRunning = true startLoop("Training", trainingStep) log("Training on - gym, " .. tostring(desiredWeight() or "default weights")) else trainingLoopRunning = false stopLoop("Training") log("Training off") end if UiRefs.trainToggle then UiRefs.trainToggle:Set(on, true) end end ---------------------------------------------------------------------- -- 2. MONEY: AFK pad ---------------------------------------------------------------------- local moneyStats = { rate = 0, lastSample = 0, lastMoney = 0 } local function moneyStep() if not character() then return end local spot = afkSpot() if distanceTo(spot) > 6 then goTo(spot) task.wait(0.4) end local now = os.clock() if now - moneyStats.lastSample >= 10 then local delta = money() - moneyStats.lastMoney if moneyStats.lastSample > 0 then moneyStats.rate = delta * (60 / (now - moneyStats.lastSample)) end moneyStats.lastMoney = money() moneyStats.lastSample = now end end local function moneySet(on) Settings.moneyOn = on if on then moneyStats.lastMoney = money() moneyStats.lastSample = os.clock() startLoop("Money", moneyStep) log("AFK money on - standing on the free cash pad") else stopLoop("Money") log("AFK money off") end if UiRefs.moneyToggle then UiRefs.moneyToggle:Set(on, true) end end ---------------------------------------------------------------------- -- 3. BATTLES ---------------------------------------------------------------------- local BoxingGui = LocalPlayer:WaitForChild("PlayerGui"):WaitForChild("Boxing", 30) local battleStats = { phase = "idle", -- idle | queueing | fighting since = os.clock(), wins = 0, losses = 0, opponent = nil, pad = nil, lastHit = 0, lastFly = 0, lastHp = 100, } local function inMatch() return BoxingGui ~= nil and BoxingGui.Enabled == true end local function myHealth() local hum = humanoid() return hum and hum.Health or 0 end --- Closest living player that is not us. local function closestOpponent() local myPos = rootPosition() if not myPos then return nil end local best, bestDistance for _, player in ipairs(Players:GetPlayers()) do if player ~= LocalPlayer and player.Character then local hum = player.Character:FindFirstChildOfClass("Humanoid") if hum and hum.Health > 0 then local pos = player.Character:GetPivot().Position local d = (pos - myPos).Magnitude if d <= Settings.auraRadius and (not bestDistance or d < bestDistance) then best, bestDistance = player, d end end end end return best end --- Kill aura, hover and punches. local function combatStep() local target = Settings.killAura and closestOpponent() or battleStats.opponent local char = character() if not char then return end if target and target.Character then battleStats.opponent = target local targetPos = target.Character:GetPivot().Position local now = os.clock() if now - battleStats.lastFly >= 0.1 then battleStats.lastFly = now if Settings.flyAbove then local hover = targetPos + Vector3.new(0, Settings.flyHeight, 0) pcall(function() char:PivotTo(CFrame.new(hover, targetPos)) end) elseif (char:GetPivot().Position - targetPos).Magnitude > 5 then pcall(function() char:PivotTo(CFrame.new(targetPos + Vector3.new(0, 3, 0), targetPos)) end) end end end if Settings.autoHits then local now = os.clock() if now - battleStats.lastHit >= Settings.hitDelay then battleStats.lastHit = now Remotes.Click:FireServer() end end end local function pickPad() local pads = ringPads(Settings.ringType) if #pads == 0 then return nil end local chosen = pads[math.random(1, math.min(3, #pads))] return chosen end local function queueUp() local pad = pickPad() if not pad then log("No " .. Settings.ringType .. " ring found") return false end battleStats.pad = pad goTo(pad.position) battleStats.phase = "queueing" battleStats.since = os.clock() log("Standing on " .. pad.ring .. " (" .. pad.name .. ") and waiting for an opponent") return true end local function battleStep() local now = os.clock() if inMatch() then if battleStats.phase ~= "fighting" then battleStats.phase = "fighting" battleStats.since = now battleStats.opponent = closestOpponent() log("Match started" .. (battleStats.opponent and (" vs " .. battleStats.opponent.Name) or "") .. "") end combatStep() if now - battleStats.since > Settings.maxFightTime then log("Fight took too long, leaving the ring") goTo(Vector3.new(6.9, 5.4, 18.4)) battleStats.phase = "idle" battleStats.since = now end return end -- Not in a match any more. if battleStats.phase == "fighting" then if myHealth() <= 0 then battleStats.losses += 1 log("Lost the match") else battleStats.wins += 1 log("Won the match") end battleStats.phase = "idle" battleStats.since = now battleStats.opponent = nil return end if battleStats.phase == "queueing" then if now - battleStats.since > Settings.waitForMatch then log("Nobody came, moving to another ring") battleStats.phase = "idle" battleStats.since = now end return end if now - battleStats.since >= Settings.requeueDelay then queueUp() end end local function battleSet(on) Settings.battleOn = on if on then battleStats.phase = "idle" battleStats.since = os.clock() startLoop("Battle", battleStep) log("Auto battle on (" .. Settings.ringType .. ")") else stopLoop("Battle") log("Auto battle off") end if UiRefs.battleToggle then UiRefs.battleToggle:Set(on, true) end end ---------------------------------------------------------------------- -- 4. ROTATION ---------------------------------------------------------------------- local ROTATION_PHASES = { "Money", "Battle", "Training" } local rotationStats = { index = 0, phase = "-", untilAt = 0, length = 0 } local function phaseEnabled(name) if name == "Money" then return Settings.rotateMoney elseif name == "Battle" then return Settings.rotateBattle end return Settings.rotateTraining end local function phaseMinutes(name) if name == "Money" then return Settings.moneyMinutes elseif name == "Battle" then return Settings.battleMinutes end return Settings.trainingMinutes end local function applyPhase(name) moneySet(name == "Money") battleSet(name == "Battle") trainingSet(name == "Training") rotationStats.phase = name rotationStats.length = phaseMinutes(name) * 60 rotationStats.untilAt = os.clock() + rotationStats.length log("Rotation: " .. name .. " for " .. phaseMinutes(name) .. " min") end local function nextPhase() for step = 1, #ROTATION_PHASES do rotationStats.index = rotationStats.index % #ROTATION_PHASES + 1 local name = ROTATION_PHASES[rotationStats.index] if phaseEnabled(name) then return name end end return nil end local function rotationStep() if not Settings.rotationOn then return end if os.clock() >= rotationStats.untilAt then local name = nextPhase() if name then applyPhase(name) else log("Rotation has no phases selected") Settings.rotationOn = false stopLoop("Rotation") if UiRefs.rotationToggle then UiRefs.rotationToggle:Set(false, true) end end end end local function rotationSet(on) Settings.rotationOn = on if on then rotationStats.index = 0 rotationStats.untilAt = 0 startLoop("Rotation", rotationStep) log("Rotation on") else stopLoop("Rotation") rotationStats.phase = "-" -- Leaving rotation off should not leave a phase running behind your back. trainingSet(false) moneySet(false) battleSet(false) log("Rotation off") end if UiRefs.rotationToggle then UiRefs.rotationToggle:Set(on, true) end end ---------------------------------------------------------------------- -- WINDOW ---------------------------------------------------------------------- local THEME = { Background = Color3.fromHex("#0A0A0C"), Surface = Color3.fromHex("#141418"), Surface2 = Color3.fromHex("#1A1A20"), Surface3 = Color3.fromHex("#2A2A33"), Stroke = Color3.fromHex("#26262E"), StrokeHover = Color3.fromHex("#3C3C49"), Accent = Color3.fromHex("#FF3B7F"), AccentDark = Color3.fromHex("#1A0710"), Text = Color3.fromHex("#F5F5F7"), Muted = Color3.fromHex("#8D8D99"), } Airflow.ThemePresets.Robscript = THEME Airflow:SetDefaultTheme(THEME) Window = Airflow:CreateWindow({ Name = "Boxing Battles Hub", LoadingSubtitle = "training, money, battles", ToggleUIKeybind = "RightControl", Size = UDim2.fromOffset(780, 540), Backdrop = { Weather = "Fireflies", Tint = 0.35 }, Theme = THEME, ConfigurationSaving = { Enabled = true, FolderName = "BoxingBattlesHub" }, Home = { Title = "Boxing Battles Hub", Welcome = "Welcome back,", Tier = "v1.0", Stats = { "Players", "Session", "FPS", "Ping", "Game", "Executor" }, }, }) local LogTab = Window:CreateTab({ Name = "Logs", Icon = "scroll-text" }) local LogBox = LogTab:CreateGroupbox({ Name = "What the script is doing", Side = "Left" }) Log = LogBox:CreateLog({ Name = "Activity", MaxEntries = 300, MaxRows = 14, Timestamps = true, EmptyText = "Nothing yet", }) ---------------------------------------------------------------------- -- TRAINING TAB ---------------------------------------------------------------------- local TrainTab = Window:CreateTab({ Name = "Training", Icon = "dumbbell" }) local TrainMain = TrainTab:CreateGroupbox({ Name = "Strength", Side = "Left" }) local TrainGear = TrainTab:CreateGroupbox({ Name = "Weights", Side = "Right" }) local TrainInfo = TrainTab:CreateGroupbox({ Name = "Status", Side = "Right" }) UiRefs.trainToggle = TrainMain:CreateToggle({ Name = "Auto train", Desc = "Stands at the dumbbell rack and keeps the game's free auto train running", Flag = "TrainOn", CurrentValue = false, Callback = function(on) trainingSet(on) end, }) TrainMain:CreateButton({ Name = "Go to the gym now", Icon = "map-pin", Callback = function() goTo(gymSpot(desiredWeight() or "Weights [1Kg]")) log("Moved to the gym") end, }) TrainMain:CreateSlider({ Name = "Keep this much cash", Desc = "Weights are not bought below this balance", Range = { 0, 500000 }, Increment = 100, Suffix = " $", CurrentValue = 0, Flag = "CashReserve", Callback = function(value) Settings.cashReserve = value end, }) local weightOptions = {} for _, info in ipairs(WEIGH_LIST) do if not info.robux then table.insert(weightOptions, string.format("%s (%s $, needs %s str)", info.name, short(info.price), short(info.req))) end end table.insert(weightOptions, 1, "Auto (best I own)") TrainGear:CreateDropdown({ Name = "Preferred weight", Desc = "What to wear, and what to buy when auto buy is on", Options = weightOptions, CurrentOption = "Auto (best I own)", Flag = "PreferredWeight", Callback = function(value) local chosen = type(value) == "table" and value[1] or value Settings.preferredWeight = (chosen == "Auto (best I own)") and "Auto" or tostring(chosen):match("^([^%(]+)") or "Auto" Settings.preferredWeight = (Settings.preferredWeight or ""):gsub("%s+$", "") end, }) TrainGear:CreateToggle({ Name = "Auto buy weights", Desc = "Buys the next weight once your strength allows it", Flag = "TrainAutoBuy", CurrentValue = true, Callback = function(on) Settings.trainAutoBuy = on end, }) TrainGear:CreateToggle({ Name = "Keep the weight in hand", Desc = "Re-equips it if the tool disappears", Flag = "TrainEquip", CurrentValue = true, Callback = function(on) Settings.trainEquip = on end, }) TrainInfo:CreateStatus({ Name = "Strength", Style = "Row", Pin = true, Update = function() return "" .. short(strength()) .. "" end, }) TrainInfo:CreateStatus({ Name = "Gain", Style = "Row", Update = function() return short(trainingStats.rate) .. " per minute" end, }) TrainInfo:CreateStatus({ Name = "Wearing", Style = "Row", Update = function() return tostring(trainingStats.equipped or "-") end, }) TrainInfo:CreateStatus({ Name = "Auto train", Style = "Row", Update = function() return autoTrainEnabled() and "running" or "off" end, }) TrainInfo:CreateParagraph({ Title = "How training works", Content = "Stand next to a dumbbell rack and the game trains for you - the script just keeps that " .. "auto train switched on. Heavier weights give more strength, but each one needs a minimum " .. "strength before you may buy it, so the script only buys what you can already use.", }) ---------------------------------------------------------------------- -- MONEY TAB ---------------------------------------------------------------------- local MoneyTab = Window:CreateTab({ Name = "Money", Icon = "coins" }) local MoneyMain = MoneyTab:CreateGroupbox({ Name = "AFK money", Side = "Left" }) local MoneyInfo = MoneyTab:CreateGroupbox({ Name = "Status", Side = "Right" }) UiRefs.moneyToggle = MoneyMain:CreateToggle({ Name = "Farm money on the AFK pad", Desc = "Stands on the free cash pad and stays there", Flag = "MoneyOn", CurrentValue = false, Callback = function(on) moneySet(on) end, }) MoneyMain:CreateButton({ Name = "Go to the pad now", Icon = "map-pin", Callback = function() goTo(afkSpot()) log("Moved to the AFK pad") end, }) MoneyInfo:CreateStatus({ Name = "Cash", Style = "Row", Pin = true, Update = function() return "" .. short(money()) .. "" end, }) MoneyInfo:CreateStatus({ Name = "Per minute", Style = "Row", Update = function() return short(moneyStats.rate) end, }) MoneyInfo:CreateStatus({ Name = "On the pad", Style = "Row", Update = function() local spot = afkSpot() return distanceTo(spot) < 6 and "yes" or "no" end, }) MoneyInfo:CreateParagraph({ Title = "How the AFK pad works", Content = "The glowing pad in the middle of the map pays cash every second while you stand on it. " .. "The script keeps you there and puts you back if something knocks you off.", }) ---------------------------------------------------------------------- -- BATTLE TAB ---------------------------------------------------------------------- local BattleTab = Window:CreateTab({ Name = "Battles", Icon = "swords" }) local BattleMain = BattleTab:CreateGroupbox({ Name = "Auto battle", Side = "Left" }) local CombatBox = BattleTab:CreateGroupbox({ Name = "Fighting", Side = "Right" }) local BattleInfo = BattleTab:CreateGroupbox({ Name = "Status", Side = "Right" }) UiRefs.battleToggle = BattleMain:CreateToggle({ Name = "Auto battle", Desc = "Queues into a ring, fights and starts the next match", Flag = "BattleOn", CurrentValue = false, Callback = function(on) battleSet(on) end, }) BattleMain:CreateDropdown({ Name = "Ring", Options = { "1v1", "2v2" }, CurrentOption = "1v1", Flag = "RingType", Callback = function(value) Settings.ringType = type(value) == "table" and (value[1] or "1v1") or value or "1v1" end, }) BattleMain:CreateSlider({ Name = "Wait for an opponent", Desc = "If nobody shows up in this time, another ring is tried", Range = { 10, 180 }, Increment = 5, Suffix = " s", CurrentValue = 45, Flag = "WaitForMatch", Callback = function(value) Settings.waitForMatch = value end, }) BattleMain:CreateSlider({ Name = "Longest fight", Desc = "Safety limit so a stuck match cannot hold you", Range = { 20, 300 }, Increment = 10, Suffix = " s", CurrentValue = 90, Flag = "MaxFightTime", Callback = function(value) Settings.maxFightTime = value end, }) CombatBox:CreateToggle({ Name = "Kill aura", Desc = "Always picks the closest player and keeps you on them", Flag = "KillAura", CurrentValue = true, Callback = function(on) Settings.killAura = on end, }) CombatBox:CreateSlider({ Name = "Aura range", Range = { 10, 150 }, Increment = 5, Suffix = " studs", CurrentValue = 60, Flag = "AuraRadius", Callback = function(value) Settings.auraRadius = value end, }) CombatBox:CreateToggle({ Name = "Fly above the target", Desc = "Hovers over the opponent while hitting", Flag = "FlyAbove", CurrentValue = true, Callback = function(on) Settings.flyAbove = on end, }) CombatBox:CreateSlider({ Name = "Hover height", Range = { 2, 20 }, Increment = 1, Suffix = " studs", CurrentValue = 6, Flag = "FlyHeight", Callback = function(value) Settings.flyHeight = value end, }) CombatBox:CreateToggle({ Name = "Auto punches", Flag = "AutoHits", CurrentValue = true, Callback = function(on) Settings.autoHits = on end, }) CombatBox:CreateSlider({ Name = "Punch delay", Range = { 0.05, 1 }, Increment = 0.05, Suffix = " s", CurrentValue = 0.2, Flag = "HitDelay", Callback = function(value) Settings.hitDelay = value end, }) BattleInfo:CreateStatus({ Name = "State", Style = "Row", Pin = true, Update = function() local phase = battleStats.phase if phase == "fighting" then return "fighting " .. tostring(battleStats.opponent and battleStats.opponent.Name or "") elseif phase == "queueing" then return "waiting " .. math.max(0, math.floor(Settings.waitForMatch - (os.clock() - battleStats.since))) .. "s" end return "idle" end, }) BattleInfo:CreateStatus({ Name = "Result", Style = "Row", Update = function() return string.format("%d won, %d lost", battleStats.wins, battleStats.losses) end, }) BattleInfo:CreateStatus({ Name = "Health", Style = "Row", Update = function() return math.floor(myHealth()) .. " / 100" end, }) BattleInfo:CreateParagraph({ Title = "How battles work", Content = "Touching a coloured pad joins that ring. When both sides fill up the game moves you in and " .. "you punch with the mouse. The script does the queueing, keeps you above your opponent and " .. "punches for you, then starts the next match.", }) ---------------------------------------------------------------------- -- ROTATION TAB ---------------------------------------------------------------------- local RotationTab = Window:CreateTab({ Name = "Rotation", Icon = "refresh-cw" }) local RotationMain = RotationTab:CreateGroupbox({ Name = "Timer", Side = "Left" }) local RotationPhases = RotationTab:CreateGroupbox({ Name = "Phases", Side = "Right" }) local RotationInfo = RotationTab:CreateGroupbox({ Name = "Status", Side = "Right" }) UiRefs.rotationToggle = RotationMain:CreateToggle({ Name = "Rotation", Desc = "Switches between the phases below on a timer", Flag = "RotationOn", CurrentValue = false, Callback = function(on) rotationSet(on) end, }) RotationMain:CreateSlider({ Name = "Money minutes", Range = { 1, 60 }, Increment = 1, Suffix = " min", CurrentValue = 5, Flag = "MoneyMinutes", Callback = function(value) Settings.moneyMinutes = value end, }) RotationMain:CreateSlider({ Name = "Battle minutes", Range = { 1, 60 }, Increment = 1, Suffix = " min", CurrentValue = 10, Flag = "BattleMinutes", Callback = function(value) Settings.battleMinutes = value end, }) RotationMain:CreateSlider({ Name = "Training minutes", Range = { 1, 60 }, Increment = 1, Suffix = " min", CurrentValue = 5, Flag = "TrainingMinutes", Callback = function(value) Settings.trainingMinutes = value end, }) RotationPhases:CreateToggle({ Name = "Include money", Flag = "RotateMoney", CurrentValue = true, Callback = function(on) Settings.rotateMoney = on end, }) RotationPhases:CreateToggle({ Name = "Include battles", Flag = "RotateBattle", CurrentValue = true, Callback = function(on) Settings.rotateBattle = on end, }) RotationPhases:CreateToggle({ Name = "Include training", Flag = "RotateTraining", CurrentValue = false, Callback = function(on) Settings.rotateTraining = on end, }) RotationInfo:CreateStatus({ Name = "Phase", Style = "Row", Pin = true, Update = function() if not Settings.rotationOn then return "off" end local left = math.max(0, math.floor(rotationStats.untilAt - os.clock())) return string.format("%s %d:%02d", rotationStats.phase, math.floor(left / 60), left % 60) end, }) RotationInfo:CreateParagraph({ Title = "How the rotation works", Content = "Pick how long each phase should last and which phases take part. The script then spends that " .. "time on the AFK pad, in rings or at the gym, and switches by itself. Turning rotation off leaves " .. "whatever phase was running switched off too.", }) ---------------------------------------------------------------------- -- SETTINGS TAB ---------------------------------------------------------------------- local SettingsTab = Window:CreateTab({ Name = "Settings", Icon = "settings" }) SettingsTab:CreateConfigManager({ Name = "Configs", Side = "Left" }) SettingsTab:CreateThemeManager({ Name = "Theme", Side = "Right" }) local HelpBox = SettingsTab:CreateGroupbox({ Name = "Quick guide", Side = "Right" }) HelpBox:CreateParagraph({ Title = "Boxing Battles Hub", Content = "Training: keeps the free auto train running at the dumbbell racks and buys better weights.\n" .. "Money: sits on the AFK pad for free cash.\n" .. "Battles: queues, flies over your opponent and punches for you.\n" .. "Rotation: alternates money, battles and training on a timer.\n\n" .. "Everything starts off. RightControl hides the window.", }) ---------------------------------------------------------------------- -- Start ---------------------------------------------------------------------- task.spawn(function() pcall(function() Window:LoadAutoload() end) log("Loaded - " .. short(money()) .. " cash, " .. short(strength()) .. " strength") end) print("[Boxing Battles Hub] loaded")