local MY_PLACE_ID = 1549137893 -- Place ID (Pirate Wars) local LOADED_GREETING = "Welcome aboard! Pirate Wars Admin Utility loaded. By Obama-Chan" if game.PlaceId ~= MY_PLACE_ID then return end local Players = game:GetService("Players") local UserInputService = game:GetService("UserInputService") local RunService = game:GetService("RunService") local Lighting = game:GetService("Lighting") local Teams = game:GetService("Teams") local GroupService = game:GetService("GroupService") local StarterGui = game:GetService("StarterGui") local HttpService = game:GetService("HttpService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local VirtualUser = game:GetService("VirtualUser") local TweenService = game:GetService("TweenService") local player = Players.LocalPlayer if not RunService:IsClient() or not player then return end local mouse = player:GetMouse() local ShipsFolder = workspace:FindFirstChild("Ships") or workspace:WaitForChild("Ships", 5) local function findWaterHeight() for _, name in ipairs({"Water", "Sea", "Ocean", "WaterPlane", "Waterplane"}) do local part = workspace:FindFirstChild(name) if part and part:IsA("BasePart") then return part.Position.Y + (part.Size.Y / 2) end end for _, part in ipairs(workspace:GetChildren()) do if part:IsA("BasePart") and (part.Material == Enum.Material.Water or string.lower(part.Name):find("water")) then return part.Position.Y + (part.Size.Y / 2) end end return 10 end --// CENTRALIZED CONFIG TABLE local Config = { BOOST_SPEED = 25, FLY_SPEED = 20, MIN_SINK_HEIGHT = 10, PLAYER_WALKSPEED = 50, PLAYER_JUMPPOWER = 100, PLAYER_FLY_SPEED = 50, AUTO_FIRE_DIST = 300, MAP_ACTIVATOR_RANGE = 250, ANTI_VOID_HEIGHT = -15, TREASURE_HITBOX_SIZE = 15, CHEST_PICKUP_WAIT = 0.15, CHEST_RETRY_EXTRA_WAIT = 0.75, CHEST_MAX_PICKUP_WAIT = 8, CHEST_FAILURE_LIMIT = 5, CHEST_MIN_GOLD_GAIN = 25, CHEST_GOLD_CONFIRM_WAIT = 1, HITBOX_SIZE = 10, HITBOX_TARGET_PART = "Torso", KILL_AURA_RANGE = 15, LOOP_KILL_ATTACKS_PER_SECOND = 10, LOOP_KILL_BEHIND_DISTANCE = 3, LOOP_KILL_TARGET_SECONDS = 8, LOOP_KILL_SPAWN_WAIT = 0.35, CTF_PICKUP_ATTEMPTS = 2, CTF_CAPTURE_WAIT = 0.2, CTF_SCORE_CONFIRM_WAIT = 2, CTF_CAPTURE_LIMIT = 0, CTF_LOCK_HOLD_DISTANCE = 80, CTF_HOLD_REACQUIRE_SECONDS = 183, BRING_SHIPS_DESTINATION = "Point", BRING_SHIPS_PLAYER = "", BRING_SHIPS_MODE = "Auto", BRING_SHIPS_SPEED = 150, BRING_SHIPS_TIMEOUT = 15, BRING_SHIPS_ARRIVAL_DISTANCE = 15, BRING_SHIPS_PASS_DELAY = 1, BRING_SHIPS_MAX_DISTANCE = 6000, BRING_SHIPS_MAX_WATER_OFFSET = 200, MELEE_REACH_SIZE = 10, FLING_AURA_RANGE = 20, WATER_WALK_HEIGHT = findWaterHeight(), FLING_POWER = 2500, FLING_MODE_INDEX = 1, FREECAM_SPEED = 50, AUTO_RETURN_HP_PCT = 25, -- Quick Turn (driven by the ShipTurn controller further down). QUICK_TURN_DEGREES = 45, QUICK_TURN_TIMEOUT = 6, -- give up after this many seconds QUICK_TURN_LEAD = 0.35, -- seconds of coast to anticipate when stopping QUICK_TURN_TOLERANCE = 3, -- degrees of slop accepted as on-target QUICK_TURN_INVERT_STEER = false, -- auto-detected on the first steered turn -- Click-detector pacing, in clicks per SECOND rather than per frame. -- The server accepts cannon clicks at a ceiling of roughly 6.7 per second no -- matter how fast they are sent; anything above that is silently discarded. -- 8.3/sec measured best in practice: slightly over the ceiling, so a few -- clicks are lost, but coverage completes sooner and the held/auto modes -- re-queue anything that was refused. Per-frame pacing was the wrong unit -- entirely - the old 3-per-frame default was ~180/sec, about 27x over. FIRE_PER_SECOND = 8.3, FIRE_QUEUE_MAX = 400, -- A single press of Fire ALL sends this many passes over the cannon list, so -- a cannon that refuses the first click still gets another chance. The held -- toggle does not need this; it re-queues continuously on its own. FIRE_ONE_SHOT_PASSES = 2, -- Ship status HUD bar. SHIP_HUD_X = 0, -- pixel offset from the horizontal centre of the screen SHIP_HUD_Y = 64, -- pixels down from the top; clears the default match bar SHIP_HUD_TRANSPARENCY = 0.35, -- Auto CTF flag mode: "Auto", "TwoFlag" or "Neutral". CTF_MODE_OVERRIDE = "Auto" } local StaffSettings = { -- Add known staff user IDs here as [123456] = true. UserIds = {}, -- Zero uses the experience's owning group, when it has one. GroupId = 0, -- Zero disables rank matching. Role words remain a heuristic. MinimumRank = 0, RoleWords = { mod = true, moderator = true, moderators = true, admin = true, administrator = true, dev = true, developer = true, creator = true, owner = true, staff = true, }, } local HITBOX_PARTS = {"Torso", "Head", "Right Arm", "Left Arm", "Right Leg", "Left Leg", "HumanoidRootPart"} local flingModes = { "Vehicle", "Character", "All", "Limbs Safe", "Held Tool", "Unanchored", "Spin Fling", "Aim Direction", "Up Launch", "Bounce Wave", "Orbit Pulser", "Spiral Vortex", "Black Hole", "Seizure Shake", "Tremor Slam", "Chaos Jitter", "Hyper Drive", "Helicopter", "Quantum Shift", "Super Combo" } --// CONSOLIDATED STATE local State = { isFarming = false, isSpammingLeft = false, isSpammingRight = false, isSpammingAll = false, isFireAllBroad = true, isShipHUDActive = false, isKeybindToastActive = true, isActionPingActive = true, isCTFChaseCarrier = false, isSpeedBoosting = false, isFlying = false, isAntiSinkActive = false, isESPActive = false, isAllyESPActive = false, isPlayerESPActive = false, isAllyPlayerESPActive = false, isModDetectorActive = true, isModPanicActive = false, isClickTPActive = false, isShipNoClipActive = false, isPlayerNoClipActive = false, isPlayerFlyActive = false, isInfJumpActive = false, isFullbrightActive = false, isCharSpeedActive = false, isCharJumpActive = false, isAutoFireActive = false, isWaterWalkActive = false, isStealEnemyActive = false, isStealFriendlyActive = false, isStealPaused = false, isAntiVoidActive = false, isAutoMapFireActive = false, isMapFireVisualizerActive = false, isAutoCTFActive = false, isCTFHogFlagActive = false, isCTFLockAtLimitActive = false, isAutoFortActive = false, isKillAuraActive = false, isLoopKillEnemiesActive = false, isLoopKillTargetActive = false, isBringShipsActive = false, isReachActive = false, isHitboxExpanderActive = false, isHitboxVisualsActive = false, isTreasureHitboxActive = false, isAntiRagdollActive = false, isAntiSitActive = false, isFlingActive = false, isFlingAttachActive = false, isFlingAuraActive = false, isFlingAuraTeamActive = false, isFlingAuraVisActive = false, isAutoReturnActive = false, isSpectating = false, isCustomChatActive = false, isFreecamActive = false, isZoomUncapActive = false, lastBetrayTime = 0, flingTargetStr = "", flingStartData = nil, mapFireVisualizer = nil, flingAuraVisualizer = nil, isAutoHPReturnActive = false, isAntiAfkActive = false } --// CONSOLIDATED CACHE local Cache = { shipAffiliation = {}, shipOwner = {}, espHighlights = {}, espLabels = {}, playerEspHighlights = {}, playerEspLabels = {}, originalPlayerCollisions = {}, originalShipCollisions = {}, shipInitialParts = {}, originalToolProps = {}, originalHitboxProps = {}, hitboxAdornments = {}, originalTreasureProps = {}, originalFlingStates = {}, LeftCannons = {}, RightCannons = {}, AllCannons = {}, ShipActivators = {}, FrontCannons = {}, RearCannons = {}, flagSpawns = {}, knownMods = {}, -- Performance Caches shipSeats = {}, lastShipSeatScan = {}, shipPartCount = {}, lastPartCountUpdate = {}, charNoClipParts = {}, lastCharNoClipUpdate = 0, shipNoClipParts = {}, lastShipNoClipUpdate = 0 } local CurrentShip = nil local lockedAltitude = nil local activeFlyBrake = nil local playerFlyBrake = nil local playerFlyGyro = nil local waterPlatform = nil local freecamCF = nil local freecamRot = Vector2.new() local lastLeftFire = 0 local lastRightFire = 0 local Connections = {} local Runtime = {closed = false, notificationSerial = 0} local Keybindings = {} local LoopKillEnemies = {syntheticInputUntil = 0} local SavedPositions = {} local FlagCapture = {} local ShipBring = {} local ChestFarm = {} local ColaDelivery = {active = false, cancelled = false} local guiVisible = true local CONFIG_FILENAME = "PirateWarsConfig.json" local BETRAY_COOLDOWN = 1800 -- 30 minutes in seconds do local previousGui = player:WaitForChild("PlayerGui"):FindFirstChild("DebugUtilityGui") if previousGui then local shutdownFunction = previousGui:FindFirstChild("ShutdownUtility") if shutdownFunction and shutdownFunction:IsA("BindableFunction") then pcall(function() shutdownFunction:Invoke() end) end if previousGui.Parent then previousGui:Destroy() end end local previousOverlay = player.PlayerGui:FindFirstChild("DebugUtilityOverlay") if previousOverlay then previousOverlay:Destroy() end end local origLighting = { Ambient = Lighting.Ambient, ColorShift_Bottom = Lighting.ColorShift_Bottom, ColorShift_Top = Lighting.ColorShift_Top, FogEnd = Lighting.FogEnd, GlobalShadows = Lighting.GlobalShadows, ClockTime = Lighting.ClockTime } local originalMaxZoomDistance = player.CameraMaxZoomDistance local espContainer = Instance.new("Folder") espContainer.Name = "PirateWarsESPContainer" local success = pcall(function() espContainer.Parent = game:GetService("CoreGui") end) if not success then espContainer.Parent = player:WaitForChild("PlayerGui") end --// DUAL-SIDED VISUALIZER CONSTRUCTOR local function createDualSidedSphere(name, color3) local folder = Instance.new("Folder") folder.Name = name folder.Parent = espContainer local outer = Instance.new("SphereHandleAdornment") outer.Name = "Outer" outer.Color3 = color3 outer.Transparency = 0.55 outer.AlwaysOnTop = true outer.ZIndex = 5 outer.Parent = folder local innerPart = Instance.new("Part") innerPart.Name = "Inner" innerPart.Size = Vector3.new(1, 1, 1) innerPart.Transparency = 0.75 innerPart.Color = color3 innerPart.Material = Enum.Material.Neon innerPart.CanCollide = false innerPart.CanQuery = false innerPart.CanTouch = false innerPart.CastShadow = false innerPart.Anchored = true innerPart.Parent = folder local mesh = Instance.new("SpecialMesh") mesh.MeshType = Enum.MeshType.Sphere mesh.Parent = innerPart return { Folder = folder, Outer = outer, InnerPart = innerPart, Mesh = mesh, Update = function(self, centerPos, radius, adornee) self.Outer.Radius = radius self.Outer.Adornee = adornee self.InnerPart.CFrame = CFrame.new(centerPos) self.Mesh.Scale = Vector3.new(-radius * 2, -radius * 2, -radius * 2) end, Destroy = function(self) self.Folder:Destroy() end } end local function cleanupESP(ship) if Cache.espHighlights[ship] then Cache.espHighlights[ship]:Destroy(); Cache.espHighlights[ship] = nil end if Cache.espLabels[ship] then Cache.espLabels[ship]:Destroy(); Cache.espLabels[ship] = nil end end local VALID_CANNONS = {NormalCannon = true, GildedCannon = true, SnowCannon = true, FireCannon = true} local function touchSeat(characterPart, targetSeat) if typeof(firetouchinterest) ~= "function" or not characterPart or not targetSeat then return end local ok = pcall(function() firetouchinterest(characterPart, targetSeat, true) firetouchinterest(characterPart, targetSeat, false) end) if not ok then pcall(function() firetouchinterest(characterPart, targetSeat, 0); firetouchinterest(characterPart, targetSeat, 1) end) end end local function isLifeboatName(name) if not name then return false end local n = string.lower(name) return string.find(n, "rowboat") or string.find(n, "lifeboat") or string.find(n, "dinghy") or string.find(n, "cutter") end local function isPartOfLifeboat(part, rootShip) local curr = part while curr and curr ~= rootShip and curr ~= workspace do if isLifeboatName(curr.Name) then return true end curr = curr.Parent end return false end local function getShipFromSeat(seatPart) if not seatPart then return nil end local curr = seatPart while curr and curr ~= workspace do if curr.Parent == ShipsFolder then return curr end curr = curr.Parent end return seatPart:FindFirstAncestorOfClass("Model") end local function getMainDriverSeat(ship) if not ship or not ship:IsA("Model") or isLifeboatName(ship.Name) then return nil end local validSeats = {} for _, desc in ipairs(ship:GetDescendants()) do if desc:IsA("VehicleSeat") and not isPartOfLifeboat(desc, ship) then table.insert(validSeats, desc) end end if #validSeats > 0 then table.sort(validSeats, function(a, b) return a.Position.Y > b.Position.Y end) return validSeats[1] end return nil end local function isFlagHeld(flag) if not flag or not flag:IsA("BasePart") or not flag.Parent then return false end local ancestor = flag.Parent while ancestor and ancestor ~= workspace do if ancestor:IsA("Model") and ancestor:FindFirstChildOfClass("Humanoid") then return true end ancestor = ancestor.Parent end local succeeded, connectedParts = pcall(function() return flag:GetConnectedParts(true) end) if not succeeded then return false end for _, connectedPart in ipairs(connectedParts) do local model = connectedPart:FindFirstAncestorOfClass("Model") if model and model:FindFirstChildOfClass("Humanoid") then return true end end return false end local function isFlagHeldByMe(flag, character) if not flag or not character or not flag:IsA("BasePart") or not flag.Parent then return false end if flag:IsDescendantOf(character) then return true end local succeeded, connectedParts = pcall(function() return flag:GetConnectedParts(true) end) if not succeeded then return false end for _, connectedPart in ipairs(connectedParts) do if connectedPart:IsDescendantOf(character) then return true end end return false end local function getSafeSpawnLocation() local myTeamColor = player.Team and player.Team.TeamColor local neutralSpawn = nil local firstSpawn = nil for _, obj in ipairs(workspace:GetDescendants()) do if obj:IsA("SpawnLocation") then if not firstSpawn then firstSpawn = obj end if obj.Neutral then neutralSpawn = obj end if myTeamColor and obj.TeamColor == myTeamColor then return obj.CFrame + Vector3.new(0, 5, 0) end end end if neutralSpawn then return neutralSpawn.CFrame + Vector3.new(0, 5, 0) end if firstSpawn then return firstSpawn.CFrame + Vector3.new(0, 5, 0) end local safePart = workspace:FindFirstChild("FlingSafePlatform") if not safePart then safePart = Instance.new("Part") safePart.Name = "FlingSafePlatform" safePart.Size = Vector3.new(30, 1, 30) safePart.Position = Vector3.new(0, 5000, 0) safePart.Anchored = true safePart.Transparency = 0.5 safePart.BrickColor = BrickColor.new("Bright yellow") safePart.Parent = workspace end return CFrame.new(0, 5005, 0) end --// FORWARD DECLARED UTILITIES & STATE HELPERS local restoreHitboxes local updateHitboxes local restoreTreasureHitboxes local updateTreasureHitboxes local restoreFlingStates local farmChests local ensureWaterPlatform local updateModWarning local setFreecam local toggleFreecam local teleportToFreecam local teleportToLocation local refreshQuickTurnUI local refreshCTFModeUI -- Declared here rather than at its definition so the panic handler, which lives -- further up the file, can flush pending clicks immediately. local Fire --// TELEPORT TO LOCATION HELPER local Teleport = {} function Teleport.shipFromPart(part) if not ShipsFolder then return nil end local current = part while current and current ~= workspace do if current.Parent == ShipsFolder and current:IsA("Model") then return current end current = current.Parent end return nil end function Teleport.shipUnderCharacter(character, rootPart, humanoid) if humanoid and humanoid.SeatPart then local ship = Teleport.shipFromPart(humanoid.SeatPart) if ship then return ship end end local ignored = {} for _, candidate in ipairs(Players:GetPlayers()) do if candidate.Character then table.insert(ignored, candidate.Character) end end table.insert(ignored, character) local parameters = RaycastParams.new() parameters.FilterType = Enum.RaycastFilterType.Exclude parameters.FilterDescendantsInstances = ignored local ground = workspace:Raycast(rootPart.Position, Vector3.new(0, -12, 0), parameters) if ground then return Teleport.shipFromPart(ground.Instance) end return nil end function Teleport.isAloneOnShip(ship) local boundsCFrame, boundsSize = ship:GetBoundingBox() local halfSize = boundsSize / 2 + Vector3.new(4, 8, 4) for _, candidate in ipairs(Players:GetPlayers()) do if candidate ~= player and candidate.Character then local character = candidate.Character local humanoid = character:FindFirstChildOfClass("Humanoid") if humanoid and humanoid.SeatPart and humanoid.SeatPart:IsDescendantOf(ship) then return false end local rootPart = character:FindFirstChild("HumanoidRootPart") if rootPart then local position = boundsCFrame:PointToObjectSpace(rootPart.Position) if math.abs(position.X) <= halfSize.X and math.abs(position.Y) <= halfSize.Y and math.abs(position.Z) <= halfSize.Z then return false end if Teleport.shipUnderCharacter(character, rootPart, humanoid) == ship then return false end end end end return true end function Teleport.captainedShip(humanoid) local seat = humanoid and humanoid.SeatPart if not seat or seat.Occupant ~= humanoid then return nil end local ship = Teleport.shipFromPart(seat) if not ship or ShipBring.captainSeat(ship) ~= seat then return nil end return ship end function Teleport.tryBringShip(targetCFrame, character, rootPart, humanoid) local ship = Teleport.captainedShip(humanoid) if not ship or not Teleport.isAloneOnShip(ship) then return false end local parts = {} for _, instance in ipairs(ship:GetDescendants()) do if instance:IsA("BasePart") then if instance.Anchored then return false end table.insert(parts, instance) end end if #parts == 0 then return false end -- Move both pivots from their original positions so seat welds do not move you twice. local offset = targetCFrame.Position - rootPart.Position local shipCFrame = ship:GetPivot() local characterCFrame = character:GetPivot() ship:PivotTo(shipCFrame + offset) character:PivotTo(characterCFrame + offset) for _, part in ipairs(parts) do part.AssemblyLinearVelocity = Vector3.zero part.AssemblyAngularVelocity = Vector3.zero end rootPart.AssemblyLinearVelocity = Vector3.zero rootPart.AssemblyAngularVelocity = Vector3.zero return true end local function teleportToLocationImpl(targetCFrame) if not targetCFrame then return end local character = player.Character local rootPart = character and character:FindFirstChild("HumanoidRootPart") local humanoid = character and character:FindFirstChildOfClass("Humanoid") if not rootPart then return end local succeeded, broughtShip = pcall(Teleport.tryBringShip, targetCFrame, character, rootPart, humanoid) if succeeded and broughtShip then return end if humanoid and (humanoid.Sit or humanoid.SeatPart) then for attempt = 1, 10 do humanoid.Sit = false humanoid.Jump = true task.wait(0.05) if player.Character ~= character or not rootPart.Parent then return end if not humanoid.SeatPart then break end end if humanoid.SeatPart then return end end rootPart.CFrame = targetCFrame rootPart.AssemblyLinearVelocity = Vector3.zero rootPart.AssemblyAngularVelocity = Vector3.zero end teleportToLocation = teleportToLocationImpl local function getTeamSpawnLocation(teamKeyword) local kw = string.lower(teamKeyword) for _, obj in ipairs(workspace:GetDescendants()) do if obj:IsA("SpawnLocation") then local brickName = string.lower(tostring(obj.BrickColor)) local teamName = obj.TeamColor and string.lower(tostring(obj.TeamColor)) or "" local objName = string.lower(obj.Name) if string.find(brickName, kw) or string.find(teamName, kw) or string.find(objName, kw) then return obj.CFrame + Vector3.new(0, 4, 0) end end end for _, obj in ipairs(workspace:GetDescendants()) do if obj:IsA("BasePart") then local name = string.lower(obj.Name) local parentName = obj.Parent and string.lower(obj.Parent.Name) or "" if (string.find(name, "spawn") or string.find(name, "flag")) and (string.find(name, kw) or string.find(parentName, kw)) then return obj.CFrame + Vector3.new(0, 4, 0) end end end return nil end --// CORE GAMEPLAY FUNCTION DEFINITIONS local function restoreFlingStatesImpl() for part, data in pairs(Cache.originalFlingStates) do if part and part.Parent then part.CanCollide = data.CanCollide; part.Massless = data.Massless end end Cache.originalFlingStates = {} end restoreFlingStates = restoreFlingStatesImpl local function getTargetBodyPart(char, partName) if not char then return nil end if partName == "HumanoidRootPart" then partName = "Torso" end if partName == "Torso" then return char:FindFirstChild("Torso") or char:FindFirstChild("UpperTorso") end return char:FindFirstChild(partName) end local zeroPhysics = PhysicalProperties.new(0.0001, 0, 0, 0, 0) local function restoreHitboxesImpl() for part, data in pairs(Cache.originalHitboxProps) do if part and part.Parent then pcall(function() part.Size = data.Size part.Transparency = data.Transparency part.CanCollide = data.CanCollide part.Massless = data.Massless part.CustomPhysicalProperties = data.CustomPhysicalProperties for att, origPos in pairs(data.Attachments) do if att and att.Parent == part then att.Position = origPos end end local visualClone = part:FindFirstChild("HitboxVisualClone") if visualClone then visualClone:Destroy() end end) end end table.clear(Cache.originalHitboxProps) for part, adornment in pairs(Cache.hitboxAdornments) do if adornment then adornment:Destroy() end end table.clear(Cache.hitboxAdornments) end restoreHitboxes = restoreHitboxesImpl local function updateHitboxesImpl() if not State.isHitboxExpanderActive then return end for part, _ in pairs(Cache.originalHitboxProps) do if not part or not part.Parent then Cache.originalHitboxProps[part] = nil end end for _, enemy in ipairs(Players:GetPlayers()) do if enemy ~= player and enemy.Character then local isEnemy = (player.Team == nil) or (enemy.Team == nil) or (enemy.Team ~= player.Team) if isEnemy then local char = enemy.Character local hum = char:FindFirstChildOfClass("Humanoid") local targetPart = getTargetBodyPart(char, Config.HITBOX_TARGET_PART) if targetPart and targetPart:IsA("BasePart") and hum and hum.Health > 0 then if not Cache.originalHitboxProps[targetPart] then Cache.originalHitboxProps[targetPart] = { Size = targetPart.Size, Transparency = targetPart.Transparency, CanCollide = targetPart.CanCollide, Massless = targetPart.Massless, CustomPhysicalProperties = targetPart.CustomPhysicalProperties, Attachments = {} } for _, child in ipairs(targetPart:GetChildren()) do if child:IsA("Attachment") then Cache.originalHitboxProps[targetPart].Attachments[child] = child.Position end end local clone = targetPart:Clone() clone.Name = "HitboxVisualClone" clone.Size = Cache.originalHitboxProps[targetPart].Size clone.CFrame = targetPart.CFrame clone.CanCollide = false clone.Massless = true for _, child in ipairs(clone:GetChildren()) do if child:IsA("Script") or child:IsA("LocalScript") or child:IsA("Weld") or child:IsA("WeldConstraint") or child:IsA("Motor6D") or child:IsA("Attachment") then child:Destroy() end end local weld = Instance.new("WeldConstraint") weld.Part0 = targetPart; weld.Part1 = clone; weld.Parent = clone; clone.Parent = targetPart end local desiredSize = Vector3.new(Config.HITBOX_SIZE, Config.HITBOX_SIZE, Config.HITBOX_SIZE) if targetPart.Size ~= desiredSize then targetPart.Size = desiredSize local scaleFactor = Cache.originalHitboxProps[targetPart].Size / desiredSize for att, origPos in pairs(Cache.originalHitboxProps[targetPart].Attachments) do if att and att.Parent == targetPart then att.Position = origPos * scaleFactor end end end targetPart.Transparency = 1; targetPart.Massless = true; targetPart.CustomPhysicalProperties = zeroPhysics if State.isHitboxVisualsActive then local adornment = Cache.hitboxAdornments[targetPart] if not adornment or not adornment.Parent then adornment = Instance.new("BoxHandleAdornment") adornment.Name = "HitboxVisual" adornment.Adornee = targetPart; adornment.AlwaysOnTop = true; adornment.ZIndex = 5 adornment.Color3 = Color3.fromRGB(255, 30, 30); adornment.Transparency = 0.65 adornment.Parent = espContainer Cache.hitboxAdornments[targetPart] = adornment end adornment.Size = targetPart.Size else if Cache.hitboxAdornments[targetPart] then Cache.hitboxAdornments[targetPart]:Destroy(); Cache.hitboxAdornments[targetPart] = nil end end end end end end end updateHitboxes = updateHitboxesImpl local function updateTreasureHitboxesImpl() if not State.isTreasureHitboxActive then return end local chests = workspace:FindFirstChild("Chests") if not chests then return end for _, chest in ipairs(chests:GetChildren()) do local treasure = chest:FindFirstChild("Treasure") if treasure and treasure:IsA("BasePart") then if not Cache.originalTreasureProps[treasure] then Cache.originalTreasureProps[treasure] = { Size = treasure.Size, Transparency = treasure.Transparency, CanCollide = treasure.CanCollide } end local desiredSize = Vector3.new(Config.TREASURE_HITBOX_SIZE, Config.TREASURE_HITBOX_SIZE, Config.TREASURE_HITBOX_SIZE) if treasure.Size ~= desiredSize then treasure.Size = desiredSize treasure.Transparency = 0.5 treasure.CanCollide = false end end end end updateTreasureHitboxes = updateTreasureHitboxesImpl local function restoreTreasureHitboxesImpl() for part, data in pairs(Cache.originalTreasureProps) do if part and part.Parent then pcall(function() part.Size = data.Size part.Transparency = data.Transparency part.CanCollide = data.CanCollide end) end end table.clear(Cache.originalTreasureProps) end restoreTreasureHitboxes = restoreTreasureHitboxesImpl ChestFarm.records = {} ChestFarm.recordsByInstance = setmetatable({}, {__mode = "k"}) ChestFarm.movementBinding = "PirateWarsChestPickupMovement" function ChestFarm.isAvailable(treasure, chests) if not chests or not treasure or not treasure.Parent or not treasure:IsDescendantOf(chests) then return false end return treasure:GetAttribute("Collected") ~= true end function ChestFarm.recordFor(treasure) local record = ChestFarm.recordsByInstance[treasure] if record then return record end local nearestDistance = 2 local chests = workspace:FindFirstChild("Chests") for _, candidate in ipairs(ChestFarm.records) do local previous = candidate.treasure local stillLoaded = previous and previous.Parent and chests and previous:IsDescendantOf(chests) local distance = (candidate.position - treasure.Position).Magnitude if not stillLoaded and distance <= nearestDistance then record = candidate nearestDistance = distance end end if not record then record = { id = #ChestFarm.records + 1, position = treasure.Position, failures = 0, ignored = false, } table.insert(ChestFarm.records, record) end record.treasure = treasure record.confirmedTreasure = nil ChestFarm.recordsByInstance[treasure] = record return record end function ChestFarm.refreshIgnored(chests) local count = 0 local worst = nil for _, record in ipairs(ChestFarm.records) do if record.ignored then count = count + 1 end if record.failures > 0 and (not worst or record.failures > worst.failures) then worst = record end if record.confirmedTreasure and not ChestFarm.isAvailable(record.confirmedTreasure, chests) then record.confirmedTreasure = nil end end if ChestFarm.setIgnoredStatus then ChestFarm.setIgnoredStatus(count, worst) end return count end function ChestFarm.ignore(treasure, chests) local record = ChestFarm.recordFor(treasure) record.ignored = true ChestFarm.refreshIgnored(chests) if ChestFarm.setStatus then ChestFarm.setStatus(string.format("Ignoring chest #%d at %.0f, %.0f, %.0f after %d failed pickups.", record.id, record.position.X, record.position.Y, record.position.Z, record.failures)) end end function ChestFarm.shouldAttempt(treasure, chests) if not ChestFarm.isAvailable(treasure, chests) then return false end local record = ChestFarm.recordFor(treasure) if record.ignored or record.confirmedTreasure == treasure then return false end if record.failures >= Config.CHEST_FAILURE_LIMIT then ChestFarm.ignore(treasure, chests) return false end return true end function ChestFarm.resetIgnored() for _, record in ipairs(ChestFarm.records) do if record.ignored then record.ignored = false record.failures = 0 end end ChestFarm.refreshIgnored(workspace:FindFirstChild("Chests")) if ChestFarm.setStatus then ChestFarm.setStatus("Ignored chest locations cleared. They will be tried again on the next farming pass.") end end function ChestFarm.readGold() local stats = player:FindFirstChild("leaderstats") local counter = stats and stats:FindFirstChild("Treasure") if not counter and stats then for _, child in ipairs(stats:GetChildren()) do if string.lower(child.Name) == "treasure" then counter = child break end end end if counter and (counter:IsA("IntValue") or counter:IsA("NumberValue")) then local value = counter.Value if type(value) == "number" and value == value and value >= 0 and value < math.huge then return value, counter end end return nil, nil end function ChestFarm.pollReward(attempt) local balance, counter = ChestFarm.readGold() if ChestFarm.setGoldStatus then ChestFarm.setGoldStatus(balance) end if attempt.counter and counter == attempt.counter and balance ~= nil and attempt.balanceBefore ~= nil then attempt.goldChange = balance - attempt.balanceBefore if attempt.goldChange >= Config.CHEST_MIN_GOLD_GAIN then attempt.sawReward = true attempt.rewardGain = math.max(attempt.rewardGain or 0, attempt.goldChange) end end if attempt.sawReward and not ChestFarm.isAvailable(attempt.treasure, attempt.chests) then attempt.confirmed = true end return attempt.confirmed == true end function ChestFarm.wasCancelled(character, humanoid) return not State.isFarming or Runtime.closed or player.Character ~= character or not humanoid.Parent or humanoid.Health <= 0 end function ChestFarm.waitTime(treasure) local failures = ChestFarm.recordFor(treasure).failures local initial = Config.CHEST_PICKUP_WAIT if failures > 0 then initial = math.max(initial, 1.25) end local maximum = math.max(Config.CHEST_PICKUP_WAIT, Config.CHEST_MAX_PICKUP_WAIT) return math.min(initial + failures * Config.CHEST_RETRY_EXTRA_WAIT, maximum) end function ChestFarm.canCollect(treasure, chests, character, rootPart, humanoid) return not ChestFarm.wasCancelled(character, humanoid) and rootPart.Parent == character and not rootPart.Anchored and not humanoid.SeatPart and ChestFarm.shouldAttempt(treasure, chests) end function ChestFarm.stopMovement() local movement = ChestFarm.movement if not movement then return end ChestFarm.movement = nil RunService:UnbindFromRenderStep(ChestFarm.movementBinding) if movement.humanoid.Parent then movement.humanoid:Move(Vector3.zero, false) end if movement.rootPart.Parent and player.Character == movement.character then movement.rootPart.AssemblyLinearVelocity = Vector3.zero movement.rootPart.AssemblyAngularVelocity = Vector3.zero end end function ChestFarm.stepMovement(movement) if ChestFarm.movement ~= movement then return end local now = os.clock() if now >= movement.deadline or not ChestFarm.canCollect(movement.treasure, movement.chests, movement.character, movement.rootPart, movement.humanoid) then ChestFarm.stopMovement() return end local center = movement.treasure.Position local rootPart = movement.rootPart if (rootPart.Position - center).Magnitude > movement.radius + 10 then movement.interrupted = true ChestFarm.stopMovement() return end local offset = movement.waypoints[movement.waypoint] local target = center + offset local difference = target - rootPart.Position local horizontal = Vector3.new(difference.X, 0, difference.Z) local direction = Vector3.zero local speed = 0 if horizontal.Magnitude <= 0.4 then if not movement.holdUntil then local holdTime = 0.12 if offset.Magnitude == 0 then holdTime = 0.4 end movement.holdUntil = now + holdTime end if now >= movement.holdUntil then movement.waypoint = movement.waypoint + 1 if movement.waypoint > #movement.waypoints then movement.waypoint = 1 end movement.holdUntil = nil movement.waypointStarted = now end else movement.holdUntil = nil direction = horizontal.Unit speed = math.min(6, horizontal.Magnitude * 5) if now - movement.waypointStarted >= 1.25 then movement.waypoint = movement.waypoint + 1 if movement.waypoint > #movement.waypoints then movement.waypoint = 1 end movement.waypointStarted = now end end local inputStrength = math.min(1, speed / math.max(movement.humanoid.WalkSpeed, 1)) movement.humanoid:Move(direction * inputStrength, false) local verticalSpeed = rootPart.AssemblyLinearVelocity.Y rootPart.AssemblyLinearVelocity = Vector3.new(direction.X * speed, verticalSpeed, direction.Z * speed) rootPart.AssemblyAngularVelocity = Vector3.zero end function ChestFarm.startMovement(treasure, chests, character, rootPart, humanoid, failures, deadline) ChestFarm.stopMovement() local size = treasure.Size local original = Cache.originalTreasureProps[treasure] if original then size = original.Size end local radius = math.clamp(math.max(size.X, size.Z) / 2 + rootPart.Size.X / 2 + 0.75, 2.5, 12) local approach = Vector3.new(1, 0, 0) local sideways = Vector3.new(0, 0, 1) if failures % 2 == 0 then approach = Vector3.new(0, 0, 1) sideways = Vector3.new(1, 0, 0) end if math.floor((failures - 1) / 2) % 2 == 1 then approach = approach * -1 sideways = sideways * -1 end local movement = { treasure = treasure, chests = chests, character = character, rootPart = rootPart, humanoid = humanoid, radius = radius, deadline = deadline, waypoint = 1, waypointStarted = os.clock(), waypoints = { Vector3.zero, approach * radius, Vector3.zero, approach * -radius, Vector3.zero, sideways * radius, Vector3.zero, sideways * -radius, }, } rootPart.CFrame = CFrame.new(treasure.Position - approach * radius) * rootPart.CFrame.Rotation rootPart.AssemblyLinearVelocity = Vector3.zero rootPart.AssemblyAngularVelocity = Vector3.zero ChestFarm.movement = movement RunService:BindToRenderStep(ChestFarm.movementBinding, Enum.RenderPriority.Input.Value + 1, function() local succeeded, failure = pcall(ChestFarm.stepMovement, movement) if not succeeded then movement.failure = failure ChestFarm.stopMovement() end end) return movement end function ChestFarm.collect(treasure, chests, character, rootPart, humanoid) if not ChestFarm.canCollect(treasure, chests, character, rootPart, humanoid) then return end local record = ChestFarm.recordFor(treasure) local balance, counter = ChestFarm.readGold() local attempt = { treasure = treasure, chests = chests, balanceBefore = balance, counter = counter, started = false, } ChestFarm.lastAttempt = attempt local waitTime = ChestFarm.waitTime(treasure) if ChestFarm.setStatus then ChestFarm.setStatus(string.format("Chest #%d: %.2fs pickup visit; failed attempts %d/%d.", record.id, waitTime, record.failures, Config.CHEST_FAILURE_LIMIT)) end local deadline = os.clock() + waitTime local movement = nil local nextTouchAt = 0 local succeeded, failure = pcall(function() attempt.started = true if record.failures > 0 then movement = ChestFarm.startMovement(treasure, chests, character, rootPart, humanoid, record.failures, deadline) end while ChestFarm.canCollect(treasure, chests, character, rootPart, humanoid) do if ChestFarm.pollReward(attempt) then break end if movement then if movement.failure then error(movement.failure) end if movement.interrupted then break end else rootPart.CFrame = treasure.CFrame rootPart.AssemblyLinearVelocity = Vector3.zero rootPart.AssemblyAngularVelocity = Vector3.zero end if os.clock() >= nextTouchAt and typeof(firetouchinterest) == "function" then nextTouchAt = os.clock() + 0.2 for _, name in ipairs({"HumanoidRootPart", "Torso", "UpperTorso", "LowerTorso", "Left Leg", "Right Leg", "LeftFoot", "RightFoot"}) do local bodyPart = character:FindFirstChild(name) if bodyPart and bodyPart:IsA("BasePart") then pcall(function() firetouchinterest(bodyPart, treasure, 0) firetouchinterest(bodyPart, treasure, 1) end) end end end local remaining = deadline - os.clock() if remaining <= 0 then break end task.wait(math.min(0.05, remaining)) end end) ChestFarm.stopMovement() local confirmDeadline = os.clock() + Config.CHEST_GOLD_CONFIRM_WAIT while not ChestFarm.pollReward(attempt) and not ChestFarm.wasCancelled(character, humanoid) do local remaining = confirmDeadline - os.clock() if remaining <= 0 then break end task.wait(math.min(0.05, remaining)) end if attempt.confirmed then record.failures = 0 record.ignored = false record.confirmedTreasure = treasure if ChestFarm.setStatus then ChestFarm.setStatus(string.format("Chest #%d collected: +%.0f Treasure. Failure count reset.", record.id, attempt.rewardGain or 0)) end elseif attempt.started and not ChestFarm.wasCancelled(character, humanoid) then record.failures = record.failures + 1 if record.failures >= Config.CHEST_FAILURE_LIMIT then record.ignored = true end if ChestFarm.setStatus then local goldText = "Treasure counter unavailable" if attempt.goldChange ~= nil then goldText = string.format("Treasure change %+.0f; need at least +%.0f", attempt.goldChange, Config.CHEST_MIN_GOLD_GAIN) end local chestText = "chest not visible, reward unconfirmed" if ChestFarm.isAvailable(treasure, chests) then chestText = "chest still present" end local ignoredText = "" if record.ignored then ignoredText = " Ignoring this chest location." end ChestFarm.setStatus(string.format("Chest #%d failed %d/%d: %s; %s.%s", record.id, record.failures, Config.CHEST_FAILURE_LIMIT, goldText, chestText, ignoredText)) end end ChestFarm.refreshIgnored(chests) if not succeeded then error(failure) end end function ChestFarm.restorePosition(character, rootPart, humanoid, originalCFrame, originalSeat) if Runtime.closed or player.Character ~= character or not rootPart.Parent or humanoid.Health <= 0 then return end if originalSeat and originalSeat.Parent and not originalSeat.Occupant then rootPart.CFrame = originalSeat.CFrame * CFrame.new(0, 1.5, 0) rootPart.AssemblyLinearVelocity = Vector3.zero task.wait(0.05) if player.Character == character and humanoid.Health > 0 and originalSeat.Parent and not originalSeat.Occupant then pcall(function() originalSeat:Sit(humanoid) end) touchSeat(rootPart, originalSeat) end else rootPart.CFrame = originalCFrame rootPart.AssemblyLinearVelocity = Vector3.zero rootPart.AssemblyAngularVelocity = Vector3.zero end end local isFarmingThreadRunning = false local function farmChestsImpl() if isFarmingThreadRunning then return end isFarmingThreadRunning = true task.spawn(function() while State.isFarming and not Runtime.closed do local character = player.Character local rootPart = character and character:FindFirstChild("HumanoidRootPart") local humanoid = character and character:FindFirstChildOfClass("Humanoid") local originalCFrame = nil local originalSeat = nil local visitedChest = false local succeeded, failure = pcall(function() local chests = workspace:FindFirstChild("Chests") ChestFarm.refreshIgnored(chests) if not chests or not rootPart or not humanoid or humanoid.Health <= 0 or rootPart.Anchored then return end local treasures = {} for _, chest in ipairs(chests:GetChildren()) do local treasure = chest:FindFirstChild("Treasure") if treasure and treasure:IsA("BasePart") and ChestFarm.shouldAttempt(treasure, chests) then table.insert(treasures, treasure) end end if #treasures == 0 then if ChestFarm.setStatus then ChestFarm.setStatus("Waiting for another chest. Broken chests remain ignored.") end return end originalCFrame = rootPart.CFrame originalSeat = humanoid.SeatPart if originalSeat then humanoid.Sit = false humanoid.Jump = true for attempt = 1, 10 do if not humanoid.SeatPart then break end task.wait(0.05) end if humanoid.SeatPart then return end end for _, treasure in ipairs(treasures) do if not State.isFarming or Runtime.closed or player.Character ~= character or humanoid.Health <= 0 then break end if ChestFarm.shouldAttempt(treasure, chests) then visitedChest = true local collected, pickupFailure = pcall(ChestFarm.collect, treasure, chests, character, rootPart, humanoid) if not collected and ChestFarm.setStatus then ChestFarm.setStatus("Chest pickup interrupted: " .. tostring(pickupFailure)) end end end end) if visitedChest and originalCFrame then pcall(ChestFarm.restorePosition, character, rootPart, humanoid, originalCFrame, originalSeat) end if not succeeded and ChestFarm.setStatus then ChestFarm.setStatus("Chest pickup interrupted: " .. tostring(failure)) end task.wait(1.5) end isFarmingThreadRunning = false end) end farmChests = farmChestsImpl local function ensureWaterPlatformImpl() if State.isWaterWalkActive then if not waterPlatform or not waterPlatform.Parent then waterPlatform = Instance.new("Part") waterPlatform.Name = "WaterWalkPlatform" waterPlatform.Size = Vector3.new(16, 1, 16) waterPlatform.Transparency = 0.85 waterPlatform.BrickColor = BrickColor.new("Cyan") waterPlatform.Anchored = true waterPlatform.CanCollide = true waterPlatform.Parent = workspace end else if waterPlatform then waterPlatform:Destroy() waterPlatform = nil end end end ensureWaterPlatform = ensureWaterPlatformImpl --// GUI SETUP (OPTIMIZED LAYOUT TO PREVENT BLOAT) local ScreenGui = Instance.new("ScreenGui") ScreenGui.Name = "DebugUtilityGui" ScreenGui.ResetOnSpawn = false ScreenGui.Parent = player:WaitForChild("PlayerGui") local NotificationLabel = Instance.new("TextLabel") NotificationLabel.Name = "UtilityNotification" NotificationLabel.AnchorPoint = Vector2.new(0.5, 1) NotificationLabel.Position = UDim2.new(0.5, 0, 1, -140) NotificationLabel.Size = UDim2.new(0.8, 0, 0, 72) NotificationLabel.AutomaticSize = Enum.AutomaticSize.Y NotificationLabel.BackgroundColor3 = Color3.fromRGB(30, 40, 55) NotificationLabel.TextColor3 = Color3.new(1, 1, 1) NotificationLabel.TextWrapped = true NotificationLabel.TextSize = 17 NotificationLabel.Font = Enum.Font.SourceSansBold NotificationLabel.RichText = false NotificationLabel.Visible = false NotificationLabel.ZIndex = 110 NotificationLabel.Parent = ScreenGui Instance.new("UICorner", NotificationLabel).CornerRadius = UDim.new(0, 8) local function showLocalNotification(title, message, duration, placement) if Runtime.closed or not ScreenGui.Parent then return end if placement == "BottomRight" then NotificationLabel.AnchorPoint = Vector2.new(1, 1) NotificationLabel.Position = UDim2.new(1, -24, 1, -140) NotificationLabel.Size = UDim2.new(0.4, 0, 0, 80) else NotificationLabel.AnchorPoint = Vector2.new(0.5, 1) NotificationLabel.Position = UDim2.new(0.5, 0, 1, -140) NotificationLabel.Size = UDim2.new(0.8, 0, 0, 72) end Runtime.notificationSerial = Runtime.notificationSerial + 1 local notificationSerial = Runtime.notificationSerial NotificationLabel.Text = title .. "\n" .. message NotificationLabel.Visible = true task.delay(duration, function() if Runtime.closed or notificationSerial ~= Runtime.notificationSerial then return end NotificationLabel.Visible = false end) end local function notifyUser(title, message, duration, placement) if Runtime.closed or not ScreenGui.Parent then return end if not duration then duration = 6 end if placement ~= "BottomRight" then showLocalNotification(title, message, duration, placement) end task.spawn(function() for attempt = 1, 4 do if Runtime.closed or not ScreenGui.Parent then return end local delivered = pcall(function() StarterGui:SetCore("SendNotification", { Title = title, Text = message, Duration = duration, }) end) if delivered then return end task.wait(0.25) end if placement == "BottomRight" then showLocalNotification(title, message, duration, placement) end end) end --// OVERLAY LAYER -- Kept in its own ScreenGui so the toast stack and the ship HUD stay readable -- while the main menu is hidden with the Show/Hide Menu key. local OverlayGui = Instance.new("ScreenGui") OverlayGui.Name = "DebugUtilityOverlay" OverlayGui.ResetOnSpawn = false OverlayGui.IgnoreGuiInset = true OverlayGui.DisplayOrder = 50 OverlayGui.Parent = player:WaitForChild("PlayerGui") --// BOTTOM-RIGHT TOAST STACK local ToastHost = Instance.new("Frame") ToastHost.Name = "ToastHost" ToastHost.AnchorPoint = Vector2.new(1, 1) ToastHost.Position = UDim2.new(1, -18, 1, -18) ToastHost.Size = UDim2.new(0, 320, 0, 320) ToastHost.BackgroundTransparency = 1 ToastHost.Parent = OverlayGui do local layout = Instance.new("UIListLayout") layout.SortOrder = Enum.SortOrder.LayoutOrder layout.VerticalAlignment = Enum.VerticalAlignment.Bottom layout.HorizontalAlignment = Enum.HorizontalAlignment.Right layout.Padding = UDim.new(0, 6) layout.Parent = ToastHost end -- Grouped in one table rather than a dozen chunk-level locals: this file is -- already close to Luau's 200-locals-per-function ceiling. local Toast = { ON = Color3.fromRGB(85, 220, 125), OFF = Color3.fromRGB(240, 95, 95), INFO = Color3.fromRGB(120, 180, 255), WARN = Color3.fromRGB(255, 185, 70), MAX = 5, serial = 0, active = {}, } function Toast.push(title, detail, accent, duration) if Runtime.closed or not OverlayGui.Parent then return end duration = duration or 2.4 accent = accent or Toast.INFO Toast.serial = Toast.serial + 1 local card = Instance.new("Frame") card.Name = "Toast" .. Toast.serial card.Size = UDim2.new(0, 302, 0, 48) card.BackgroundColor3 = Color3.fromRGB(20, 24, 32) card.BackgroundTransparency = 0.1 card.BorderSizePixel = 0 card.LayoutOrder = Toast.serial card.Parent = ToastHost Instance.new("UICorner", card).CornerRadius = UDim.new(0, 7) local stroke = Instance.new("UIStroke") stroke.Color = accent stroke.Transparency = 0.55 stroke.Thickness = 1 stroke.Parent = card local stripe = Instance.new("Frame") stripe.Name = "Stripe" stripe.Size = UDim2.new(0, 4, 1, -12) stripe.Position = UDim2.new(0, 7, 0, 6) stripe.BackgroundColor3 = accent stripe.BorderSizePixel = 0 stripe.Parent = card Instance.new("UICorner", stripe).CornerRadius = UDim.new(0, 2) local titleLabel = Instance.new("TextLabel") titleLabel.Size = UDim2.new(1, -26, 0, 21) titleLabel.Position = UDim2.new(0, 18, 0, 4) titleLabel.BackgroundTransparency = 1 titleLabel.Text = tostring(title) titleLabel.TextColor3 = Color3.fromRGB(238, 243, 252) titleLabel.Font = Enum.Font.SourceSansBold titleLabel.TextSize = 16 titleLabel.TextXAlignment = Enum.TextXAlignment.Left titleLabel.TextTruncate = Enum.TextTruncate.AtEnd titleLabel.Parent = card local detailLabel = Instance.new("TextLabel") detailLabel.Size = UDim2.new(1, -26, 0, 20) detailLabel.Position = UDim2.new(0, 18, 0, 24) detailLabel.BackgroundTransparency = 1 detailLabel.Text = tostring(detail) detailLabel.TextColor3 = accent detailLabel.Font = Enum.Font.SourceSansBold detailLabel.TextSize = 15 detailLabel.TextXAlignment = Enum.TextXAlignment.Left detailLabel.TextTruncate = Enum.TextTruncate.AtEnd detailLabel.Parent = card table.insert(Toast.active, card) while #Toast.active > Toast.MAX do local oldest = table.remove(Toast.active, 1) if oldest then oldest:Destroy() end end task.delay(duration, function() if not card.Parent then return end local info = TweenInfo.new(0.3, Enum.EasingStyle.Quad, Enum.EasingDirection.Out) pcall(function() TweenService:Create(card, info, {BackgroundTransparency = 1}):Play() TweenService:Create(stripe, info, {BackgroundTransparency = 1}):Play() TweenService:Create(stroke, info, {Transparency = 1}):Play() TweenService:Create(titleLabel, info, {TextTransparency = 1}):Play() TweenService:Create(detailLabel, info, {TextTransparency = 1}):Play() end) task.wait(0.32) local index = table.find(Toast.active, card) if index then table.remove(Toast.active, index) end card:Destroy() end) end -- Shared helper so every ON/OFF toast looks identical wherever it comes from. function Toast.toggle(label, isOn, keyText) local title = tostring(label) if keyText and keyText ~= "" and keyText ~= "Unbound" then title = title .. " [" .. keyText .. "]" end Toast.push(title, isOn and "ON" or "OFF", isOn and Toast.ON or Toast.OFF, 2.2) end --// ACTION PING SOUND -- Audio feedback for keybinds and button presses. Rather than parenting a Sound, -- this replays the game's own team-ping client signal locally with firesignal, -- so the click we hear is the game's real ping sound. It is client-side only: -- nothing is sent to the server and no other player sees or hears a ping. local ActionPing = { remote = nil, lastPlayed = 0, minInterval = 0.1, nextId = 1, unavailableWarned = false, } function ActionPing.resolveRemote() if ActionPing.remote and ActionPing.remote.Parent then return ActionPing.remote end local found = nil pcall(function() local folder = ReplicatedStorage:FindFirstChild("TeamPingRemotes") local remote = folder and folder:FindFirstChild("PingAdded") if not remote then remote = ReplicatedStorage:FindFirstChild("PingAdded", true) end if remote and remote:IsA("RemoteEvent") then found = remote end end) ActionPing.remote = found return found end local function playActionPing() if Runtime.closed or not State.isActionPingActive then return end local now = os.clock() if now - ActionPing.lastPlayed < ActionPing.minInterval then return end if typeof(firesignal) ~= "function" then if not ActionPing.unavailableWarned then ActionPing.unavailableWarned = true notifyUser("Action Ping", "This environment does not provide firesignal, so the ping sound cannot play. Turn Action Ping Sound off in the Settings tab to stop this notice.", 7) end return end local remote = ActionPing.resolveRemote() if not remote then if not ActionPing.unavailableWarned then ActionPing.unavailableWarned = true notifyUser("Action Ping", "ReplicatedStorage.TeamPingRemotes.PingAdded was not found, so the ping sound cannot play.", 7) end return end ActionPing.lastPlayed = now task.spawn(function() pcall(function() local serverTime = workspace:GetServerTimeNow() local character = player.Character local root = character and character:FindFirstChild("HumanoidRootPart") ActionPing.nextId = ActionPing.nextId + 1 firesignal(remote.OnClientEvent, { createdAt = serverTime, -- Placed on ourselves and expired almost immediately, so the ping -- marker does not linger on screen; we only want the sound. position = root and root.Position or Vector3.new(0, 0, 0), id = ActionPing.nextId, creatorUserId = player.UserId, creatorName = player.Name, expiresAt = serverTime + 0.35, }) end) end) end local UI = {} UI.Inputs = {} local ToggleUI = {} Keybindings.actions = {} Keybindings.actionOrder = {} Keybindings.bindings = {} Keybindings.rows = {} Keybindings.downInputs = {} Keybindings.validKeys = {} Keybindings.capturing = nil Keybindings.captureModifier = nil Keybindings.captureVersion = 0 Keybindings.windowFocused = true Keybindings.defaults = { LeftBtn = "Q", RightBtn = "E", TurnLeftBtn = "Z", TurnRightBtn = "C", PauseStealBtn = "P", PlayerFlyBtn = "X", StealOpenBtn = "T", SpeedBtn = "R", PlayerNoClipBtn = "V", FullbrightBtn = "B", FreecamBtn = "J", FreecamTPBtn = "G", } Keybindings.labels = { MinBtn = "Minimize or expand menu", FlingModeBtn = "Cycle fling mode", FlingDropdownBtn = "Open target player list", HitboxPartDropdown = "Cycle hitbox target part", CTFModeBtn = "Cycle CTF flag mode", CTFRescanBtn = "Rescan flags and re-detect CTF mode", FireAllOnceBtn = "Fire all cannons once", ShipHUDResetBtn = "Reset ship HUD position", } Keybindings.modifierGroups = { LeftControl = "Control", RightControl = "Control", LeftShift = "Shift", RightShift = "Shift", LeftAlt = "Alt", RightAlt = "Alt", } -- Every Enum.KeyCode is bindable. That already covers the gamepad face buttons, -- bumpers, triggers, D-pad, stick clicks and Start/Select, because Roblox reports -- those as KeyCodes (ButtonA, ButtonL2, DPadUp, ...) on a Gamepad UserInputType. for _, keyCode in ipairs(Enum.KeyCode:GetEnumItems()) do if keyCode ~= Enum.KeyCode.Unknown then Keybindings.validKeys[keyCode.Name] = true end end -- Roblox only surfaces three real mouse buttons to the client; there is no -- MouseButton4/5 in the engine, so the extra thumb buttons on a gaming mouse can -- only be bound by mapping them to a keyboard key in the mouse's own software. -- The wheel is exposed separately through InputChanged, so it is added here as a -- pair of synthetic keys. Keybindings.validKeys.MouseButton1 = true Keybindings.validKeys.MouseButton2 = true Keybindings.validKeys.MouseButton3 = true Keybindings.validKeys.MouseWheelUp = true Keybindings.validKeys.MouseWheelDown = true -- Analogue sticks are reported as axis movement, not presses. They are turned -- into eight synthetic directional keys with a deadzone so they can be bound -- like buttons. Keybindings.thumbstickKeys = { [Enum.KeyCode.Thumbstick1] = {up = "Thumbstick1Up", down = "Thumbstick1Down", left = "Thumbstick1Left", right = "Thumbstick1Right"}, [Enum.KeyCode.Thumbstick2] = {up = "Thumbstick2Up", down = "Thumbstick2Down", left = "Thumbstick2Left", right = "Thumbstick2Right"}, } for _, directions in pairs(Keybindings.thumbstickKeys) do for _, name in pairs(directions) do Keybindings.validKeys[name] = true end end Keybindings.thumbstickDeadzone = 0.65 Keybindings.thumbstickReleaseZone = 0.4 -- Friendlier captions than the raw enum names. Keybindings.keyDisplayNames = { MouseButton1 = "Mouse L", MouseButton2 = "Mouse R", MouseButton3 = "Mouse Mid", MouseWheelUp = "Wheel Up", MouseWheelDown = "Wheel Down", Thumbstick1Up = "L-Stick Up", Thumbstick1Down = "L-Stick Down", Thumbstick1Left = "L-Stick Left", Thumbstick1Right = "L-Stick Right", Thumbstick2Up = "R-Stick Up", Thumbstick2Down = "R-Stick Down", Thumbstick2Left = "R-Stick Left", Thumbstick2Right = "R-Stick Right", ButtonA = "Pad A", ButtonB = "Pad B", ButtonX = "Pad X", ButtonY = "Pad Y", ButtonL1 = "Pad LB", ButtonR1 = "Pad RB", ButtonL2 = "Pad LT", ButtonR2 = "Pad RT", ButtonL3 = "Pad L-Stick Click", ButtonR3 = "Pad R-Stick Click", ButtonStart = "Pad Start", ButtonSelect = "Pad Select", DPadUp = "Pad D-Up", DPadDown = "Pad D-Down", DPadLeft = "Pad D-Left", DPadRight = "Pad D-Right", } function Keybindings.displayKey(keyName) return Keybindings.keyDisplayNames[keyName] or keyName end -- Which physical device a bound key belongs to, used for the Keybinds tab rows. function Keybindings.deviceOf(keyName) if not keyName then return "None" end if string.sub(keyName, 1, 5) == "Mouse" then return "Mouse" end if string.sub(keyName, 1, 6) == "Button" or string.sub(keyName, 1, 4) == "DPad" or string.sub(keyName, 1, 10) == "Thumbstick" then return "Gamepad" end return "Keyboard" end function Keybindings.validateBinding(value) if value == false then return false, true end if type(value) == "string" then value = {Key = value} end if type(value) ~= "table" or type(value.Key) ~= "string" then return nil, false end if not Keybindings.validKeys[value.Key] then return nil, false end local binding = {Key = value.Key, Control = false, Shift = false, Alt = false} for _, modifier in ipairs({"Control", "Shift", "Alt"}) do if value[modifier] ~= nil then if type(value[modifier]) ~= "boolean" then return nil, false end binding[modifier] = value[modifier] end end local ownModifier = Keybindings.modifierGroups[binding.Key] if ownModifier then binding[ownModifier] = false end return binding, true end function Keybindings.formatBinding(binding) if not binding then return "Unbound" end local labels = {} if binding.Control then table.insert(labels, "Ctrl") end if binding.Shift then table.insert(labels, "Shift") end if binding.Alt then table.insert(labels, "Alt") end table.insert(labels, Keybindings.displayKey(binding.Key)) return table.concat(labels, "+") end function Keybindings.setStatus(message) if not Runtime.closed and UI.KeybindStatus then UI.KeybindStatus.Text = message end end --// BINDING CONFLICT DETECTION -- Hard conflict: two press-once actions on the identical key + modifier set. -- Pressing that key runs both, which is almost never what anyone wants. -- Soft conflict: a press-once action sharing a physical key with a hold action. -- Hold checks deliberately ignore extra modifiers, so both can react - worth a -- warning but often fine (Space is Infinite Jump and also Freecam Up). -- Two hold actions on the same key are NOT reported: the shipped defaults bind -- W/A/S/D/Space to freecam, player flight and ship boost on purpose, and only -- one of those features is ever enabled at a time. function Keybindings.bindingSignature(binding) if not binding then return nil end return binding.Key .. "|" .. tostring(binding.Control == true) .. "|" .. tostring(binding.Shift == true) .. "|" .. tostring(binding.Alt == true) end function Keybindings.describeAction(actionId) local action = Keybindings.actions[actionId] if not action then return actionId end return action.category .. " / " .. action.label end function Keybindings.conflictsFor(actionId) local hard, soft = {}, {} local binding = Keybindings.bindings[actionId] local action = Keybindings.actions[actionId] if not binding or not action then return hard, soft end local signature = Keybindings.bindingSignature(binding) for _, otherId in ipairs(Keybindings.actionOrder) do if otherId ~= actionId then local other = Keybindings.bindings[otherId] local otherAction = Keybindings.actions[otherId] if other and otherAction and other.Key == binding.Key then local bothHold = action.isHold and otherAction.isHold local sameCombo = Keybindings.bindingSignature(other) == signature if not bothHold then if sameCombo and not action.isHold and not otherAction.isHold then table.insert(hard, {id = otherId, label = Keybindings.describeAction(otherId)}) else table.insert(soft, {id = otherId, label = Keybindings.describeAction(otherId)}) end end end end end return hard, soft end function Keybindings.conflictSummary(actionId) local hard, soft = Keybindings.conflictsFor(actionId) if #hard > 0 then local names = {} for _, entry in ipairs(hard) do table.insert(names, entry.label) end return "hard", "Conflicts with " .. table.concat(names, ", ") .. " - all of them run on this key.", names end if #soft > 0 then local names = {} for _, entry in ipairs(soft) do table.insert(names, entry.label) end return "soft", "Shares the '" .. Keybindings.bindings[actionId].Key .. "' key with " .. table.concat(names, ", ") .. ". A hold action is involved and hold checks ignore extra modifiers, so both can react.", names end return nil, "", {} end function Keybindings.refreshConflicts() if Runtime.closed then return end local lines = {} local seen = {} local hardCount = 0 for _, actionId in ipairs(Keybindings.actionOrder) do if Keybindings.rows[actionId] then Keybindings.refreshAction(actionId) end local hard = Keybindings.conflictsFor(actionId) for _, entry in ipairs(hard) do local pairKey = actionId < entry.id and (actionId .. "~" .. entry.id) or (entry.id .. "~" .. actionId) if not seen[pairKey] then seen[pairKey] = true hardCount = hardCount + 1 table.insert(lines, Keybindings.formatBinding(Keybindings.bindings[actionId]) .. " - " .. Keybindings.describeAction(actionId) .. " <-> " .. entry.label) end end end Keybindings.hardConflictCount = hardCount if UI.KeybindConflictLabel then if hardCount == 0 then UI.KeybindConflictLabel.Text = "No conflicting bindings." UI.KeybindConflictLabel.TextColor3 = Color3.fromRGB(120, 210, 145) else table.sort(lines) UI.KeybindConflictLabel.Text = hardCount .. " conflicting binding" .. (hardCount == 1 and "" or "s") .. ":\n- " .. table.concat(lines, "\n- ") UI.KeybindConflictLabel.TextColor3 = Color3.fromRGB(255, 140, 140) end end return hardCount end function Keybindings.refreshAction(actionId) local action = Keybindings.actions[actionId] local binding = Keybindings.bindings[actionId] local bindingText = Keybindings.formatBinding(binding) local row = Keybindings.rows[actionId] if row then local severity, description = Keybindings.conflictSummary(actionId) -- Kept separate from bindingText: the device suffix belongs on the -- Keybinds tab row, not appended to every feature button's caption. local rowText = bindingText if binding then rowText = rowText .. " (" .. Keybindings.deviceOf(binding.Key) .. ")" end row.bindButton.Text = rowText row.bindButton.BackgroundColor3 = Color3.fromRGB(55, 70, 100) if severity == "hard" then row.bindButton.Text = rowText .. " (!) conflict" row.bindButton.BackgroundColor3 = Color3.fromRGB(150, 45, 45) elseif severity == "soft" then row.bindButton.Text = rowText .. " (!) shared key" row.bindButton.BackgroundColor3 = Color3.fromRGB(132, 95, 30) end if Keybindings.capturing == actionId then row.bindButton.Text = "Press a key..." row.bindButton.BackgroundColor3 = Color3.fromRGB(150, 100, 25) end if row.conflictLabel then row.conflictLabel.Text = description if severity == "hard" then row.conflictLabel.TextColor3 = Color3.fromRGB(255, 135, 135) else row.conflictLabel.TextColor3 = Color3.fromRGB(250, 200, 105) end row.conflictLabel.Visible = severity ~= nil end end if action.button and action.showHint and action.button.Parent then if action.button.Text ~= action.renderedText then action.baseText = action.button.Text end local buttonText = action.baseText if binding then buttonText = buttonText .. " [" .. bindingText .. "]" end action.renderedText = buttonText if action.button.Text ~= buttonText then action.button.Text = buttonText end end end function Keybindings.registerAction(actionId, category, label, defaultBinding, callback, isHold) assert(not Keybindings.actions[actionId], "Duplicate action: " .. actionId) local binding, valid = Keybindings.validateBinding(defaultBinding) assert(valid, "Invalid default binding: " .. actionId) Keybindings.actions[actionId] = { category = category, label = label, callback = callback, isHold = isHold == true, running = false, } Keybindings.bindings[actionId] = binding table.insert(Keybindings.actionOrder, actionId) end --// KEYBIND FEEDBACK -- Resolves which State flag a button drives so the toast can report the real -- post-toggle value instead of guessing from the button caption. function Keybindings.stateKeyFor(actionId) local action = Keybindings.actions[actionId] if not action or not action.button or type(ToggleUI) ~= "table" then return nil end for stateName, entry in pairs(ToggleUI) do if entry.Btn == action.button then return stateName end end return nil end -- alreadyToasted: the action's own callback already raised a toast (for example -- "Fired ALL on Sloop"), so stay quiet rather than stacking a second card. function Keybindings.announce(actionId, alreadyToasted) if Runtime.closed or not State.isKeybindToastActive or alreadyToasted then return end local action = Keybindings.actions[actionId] if not action then return end local keyText = Keybindings.formatBinding(Keybindings.bindings[actionId]) local label = action.label if action.stateProbe then local succeeded, value = pcall(action.stateProbe) if succeeded and type(value) == "boolean" then Toast.toggle(label, value, keyText) return end end local stateKey = Keybindings.stateKeyFor(actionId) if stateKey ~= nil and type(State[stateKey]) == "boolean" then Toast.toggle(label, State[stateKey] == true, keyText) return end if action.button then local text = action.button.Text or "" if string.find(text, ": OFF", 1, true) then Toast.toggle(label, false, keyText) return elseif string.find(text, ": ON", 1, true) then Toast.toggle(label, true, keyText) return end end local title = label if keyText ~= "Unbound" then title = title .. " [" .. keyText .. "]" end Toast.push(title, "TRIGGERED", Toast.INFO, 1.8) end function Keybindings.runAction(actionId, viaKeybind) local action = Keybindings.actions[actionId] if Runtime.closed or not action or action.isHold or action.running then return end if Keybindings.capturing then return end action.running = true task.spawn(function() if Runtime.closed then action.running = false return end local serialBefore = Toast.serial local succeeded, failure = pcall(action.callback) action.running = false if not succeeded and not Runtime.closed then notifyUser("Action Error", action.label .. ": " .. tostring(failure), 6) if viaKeybind then Toast.push(action.label, "ERROR - see notification", Toast.OFF, 4) end return end if viaKeybind and not Runtime.closed then Keybindings.announce(actionId, Toast.serial ~= serialBefore) end end) end function Keybindings.connectButton(actionId, button, callback) local label = Keybindings.labels[actionId] if not label then label = string.gsub(button.Text, ": ON$", "") label = string.gsub(label, ": OFF$", "") end local category = button.Parent:GetAttribute("BindingCategory") if not category then category = button.Parent.Name end local showHint = true if string.sub(actionId, 1, 4) == "Tab_" then label = "Open " .. button.Text .. " tab" category = "Menu" showHint = false elseif actionId == "MinBtn" then category = "Menu" showHint = false end local defaultBinding = Keybindings.defaults[actionId] if defaultBinding == nil then defaultBinding = false end Keybindings.registerAction(actionId, category, label, defaultBinding, callback, false) local action = Keybindings.actions[actionId] action.button = button action.baseText = button.Text action.showHint = showHint table.insert(Connections, button:GetPropertyChangedSignal("Text"):Connect(function() if Runtime.closed or button.Text == action.renderedText then return end action.baseText = button.Text Keybindings.refreshAction(actionId) end)) Keybindings.refreshAction(actionId) return button.MouseButton1Click:Connect(function() playActionPing() Keybindings.runAction(actionId) end) end function Keybindings.currentModifiers(keyName) local modifiers = { Control = UserInputService:IsKeyDown(Enum.KeyCode.LeftControl) or UserInputService:IsKeyDown(Enum.KeyCode.RightControl), Shift = UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) or UserInputService:IsKeyDown(Enum.KeyCode.RightShift), Alt = UserInputService:IsKeyDown(Enum.KeyCode.LeftAlt) or UserInputService:IsKeyDown(Enum.KeyCode.RightAlt), } local ownModifier = Keybindings.modifierGroups[keyName] if ownModifier then modifiers[ownModifier] = false end return modifiers end function Keybindings.modifiersMatch(binding, allowExtraModifiers) local current = Keybindings.currentModifiers(binding.Key) for _, modifier in ipairs({"Control", "Shift", "Alt"}) do if binding[modifier] and not current[modifier] then return false end if not allowExtraModifiers and current[modifier] ~= binding[modifier] then return false end end return true end function Keybindings.isHeld(actionId) if Runtime.closed or Keybindings.capturing or not Keybindings.windowFocused then return false end if UserInputService:GetFocusedTextBox() then return false end local binding = Keybindings.bindings[actionId] if not binding or not Keybindings.downInputs[binding.Key] then return false end return Keybindings.modifiersMatch(binding, true) end function Keybindings.isGamepadInput(input) local name = input.UserInputType.Name return string.sub(name, 1, 7) == "Gamepad" end function Keybindings.inputName(input) if input.KeyCode ~= Enum.KeyCode.Unknown and Keybindings.validKeys[input.KeyCode.Name] then return input.KeyCode.Name end if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.MouseButton2 or input.UserInputType == Enum.UserInputType.MouseButton3 then return input.UserInputType.Name end return nil end -- InputBegan never fires for the wheel, so it is handled from InputChanged and -- synthesised into a press/release pair. function Keybindings.handleWheel(input, gameProcessed) if Runtime.closed or not Keybindings.windowFocused then return end if input.UserInputType ~= Enum.UserInputType.MouseWheel then return end local delta = input.Position.Z if delta == 0 then return end local keyName = delta > 0 and "MouseWheelUp" or "MouseWheelDown" if Keybindings.capturing then if gameProcessed then return end local binding = Keybindings.currentModifiers(keyName) binding.Key = keyName Keybindings.setBinding(Keybindings.capturing, binding) return end if gameProcessed or UserInputService:GetFocusedTextBox() then return end -- A wheel notch has no hold state, so mark it down for one frame so that -- Keybindings.isHeld can still see it from a hold action. Keybindings.downInputs[keyName] = true Keybindings.fireMatchingActions(keyName) task.delay(0.12, function() Keybindings.downInputs[keyName] = nil end) end -- Turns analogue stick deflection into directional press/release events. function Keybindings.handleThumbstick(input, gameProcessed) if Runtime.closed or not Keybindings.windowFocused then return end local directions = Keybindings.thumbstickKeys[input.KeyCode] if not directions then return end local position = input.Position local axes = { {directions.right, position.X, 1}, {directions.left, position.X, -1}, {directions.up, position.Y, 1}, {directions.down, position.Y, -1}, } for _, axis in ipairs(axes) do local keyName, value, sign = axis[1], axis[2], axis[3] local magnitude = value * sign if magnitude >= Keybindings.thumbstickDeadzone then if not Keybindings.downInputs[keyName] then Keybindings.downInputs[keyName] = true if Keybindings.capturing then local binding = Keybindings.currentModifiers(keyName) binding.Key = keyName Keybindings.setBinding(Keybindings.capturing, binding) elseif not UserInputService:GetFocusedTextBox() then Keybindings.fireMatchingActions(keyName) end end elseif magnitude < Keybindings.thumbstickReleaseZone then -- Separate release threshold so a stick resting near the edge does -- not chatter between pressed and released. Keybindings.downInputs[keyName] = nil end end end function Keybindings.cancelCapture() local previousAction = Keybindings.capturing Keybindings.capturing = nil Keybindings.captureModifier = nil Keybindings.captureVersion = Keybindings.captureVersion + 1 table.clear(Keybindings.downInputs) if previousAction and not Runtime.closed then Keybindings.refreshAction(previousAction) Keybindings.setStatus("Key capture canceled.") end end function Keybindings.setBinding(actionId, value) if not Keybindings.actions[actionId] then return false end local binding, valid = Keybindings.validateBinding(value) if not valid then return false end Keybindings.cancelCapture() Keybindings.bindings[actionId] = binding Keybindings.refreshAction(actionId) local action = Keybindings.actions[actionId] local bindingText = Keybindings.formatBinding(binding) local message = action.label .. ": " .. bindingText if binding then local severity, description, names = Keybindings.conflictSummary(actionId) if severity == "hard" then message = message .. "\n(!) CONFLICT: " .. bindingText .. " is already bound to " .. table.concat(names, ", ") .. ". Pressing it runs every one of them." local extra = "" if #names > 1 then extra = " (+" .. (#names - 1) .. " more)" end Toast.push("Conflict on " .. bindingText, action.label .. " <-> " .. names[1] .. extra, Toast.OFF, 5) notifyUser("Keybind Conflict", bindingText .. " is now shared by: " .. action.label .. ", " .. table.concat(names, ", ") .. ".", 6) elseif severity == "soft" then message = message .. "\n(!) " .. description Toast.push("Shared key: " .. binding.Key, action.label .. " <-> " .. names[1], Toast.WARN, 4) else Toast.push("Bound " .. action.label, bindingText, Toast.INFO, 2.2) end else Toast.push("Cleared " .. action.label, "Unbound", Toast.INFO, 2) end Keybindings.setStatus(message .. "\nUse Save Config Settings to keep this change.") if Keybindings.filterRows then Keybindings.filterRows() end Keybindings.refreshConflicts() return true end function Keybindings.beginCapture(actionId) if Runtime.closed or not Keybindings.actions[actionId] then return end if Keybindings.capturing == actionId then Keybindings.cancelCapture() return end Keybindings.cancelCapture() local focusedBox = UserInputService:GetFocusedTextBox() if focusedBox then focusedBox:ReleaseFocus() end Keybindings.capturing = actionId local captureVersion = Keybindings.captureVersion Keybindings.refreshAction(actionId) Keybindings.setStatus("Binding " .. Keybindings.actions[actionId].label .. ". Press a key or combination. Click this binding again to cancel; Clear removes it.") task.delay(30, function() if Runtime.closed or Keybindings.captureVersion ~= captureVersion then return end if Keybindings.capturing then Keybindings.cancelCapture() Keybindings.setStatus("Key capture expired. Click the binding to try again.") end end) end -- Shared by keyboard/mouse buttons, the wheel and the thumbsticks. function Keybindings.fireMatchingActions(keyName) local matchingActions = {} for _, actionId in ipairs(Keybindings.actionOrder) do local action = Keybindings.actions[actionId] local binding = Keybindings.bindings[actionId] if not action.isHold and binding and binding.Key == keyName then if Keybindings.modifiersMatch(binding, false) then table.insert(matchingActions, actionId) end end end if #matchingActions == 0 then return 0 end playActionPing() if #matchingActions > 1 then -- The binding is shared; say so once rather than leaving the user guessing -- why two features flipped at the same time. Toast.push("Shared binding: " .. Keybindings.formatBinding(Keybindings.bindings[matchingActions[1]]), #matchingActions .. " actions triggered", Toast.WARN, 3) end for _, actionId in ipairs(matchingActions) do Keybindings.runAction(actionId, true) end return #matchingActions end function Keybindings.handleInputBegan(input, gameProcessed) if input.KeyCode == Enum.KeyCode.One and os.clock() < LoopKillEnemies.syntheticInputUntil then return end if Runtime.closed or not Keybindings.windowFocused then return end local keyName = Keybindings.inputName(input) if not keyName then return end local isGamepad = Keybindings.isGamepadInput(input) if Keybindings.capturing then -- Keyboard and gamepad input is accepted even when the game claims it, -- otherwise most controller buttons could never be bound at all. if gameProcessed and input.UserInputType ~= Enum.UserInputType.Keyboard and not isGamepad then return end local binding = Keybindings.currentModifiers(keyName) binding.Key = keyName if Keybindings.modifierGroups[keyName] then Keybindings.captureModifier = binding else Keybindings.setBinding(Keybindings.capturing, binding) end return end if UserInputService:GetFocusedTextBox() then return end -- Explicit keyboard and gamepad binds may share the game's own controls; -- typing and UI mouse clicks stay excluded. if gameProcessed and input.UserInputType ~= Enum.UserInputType.Keyboard and not isGamepad then return end if Keybindings.downInputs[keyName] then return end Keybindings.downInputs[keyName] = true Keybindings.fireMatchingActions(keyName) end function Keybindings.handleInputEnded(input) if input.KeyCode == Enum.KeyCode.One and os.clock() < LoopKillEnemies.syntheticInputUntil then return end local keyName = Keybindings.inputName(input) if not keyName then return end Keybindings.downInputs[keyName] = nil if Runtime.closed then return end if Keybindings.capturing and Keybindings.captureModifier then if Keybindings.captureModifier.Key == keyName then Keybindings.setBinding(Keybindings.capturing, Keybindings.captureModifier) end end end function Keybindings.exportBindings() local savedBindings = {} for _, actionId in ipairs(Keybindings.actionOrder) do local binding = Keybindings.bindings[actionId] if binding then savedBindings[actionId] = { Key = binding.Key, Control = binding.Control, Shift = binding.Shift, Alt = binding.Alt, } else savedBindings[actionId] = false end end return savedBindings end function Keybindings.loadBindings(savedBindings) if savedBindings == nil then return 0, 0 end if type(savedBindings) ~= "table" then return 0, 1 end Keybindings.cancelCapture() local loadedCount = 0 local invalidCount = 0 for _, actionId in ipairs(Keybindings.actionOrder) do if savedBindings[actionId] ~= nil then local binding, valid = Keybindings.validateBinding(savedBindings[actionId]) if valid then Keybindings.bindings[actionId] = binding Keybindings.refreshAction(actionId) loadedCount = loadedCount + 1 else invalidCount = invalidCount + 1 end end end if Keybindings.filterRows then Keybindings.filterRows() end Keybindings.refreshConflicts() return loadedCount, invalidCount end function SavedPositions.encode(position) if not position then return false end return { PlaceId = position.placeId, Transform = {position.cframe:GetComponents()}, } end function SavedPositions.decode(data) if data == false then return nil, true end if type(data) ~= "table" or type(data.PlaceId) ~= "number" or type(data.Transform) ~= "table" then return nil, false end if data.PlaceId ~= data.PlaceId or math.abs(data.PlaceId) == math.huge or #data.Transform ~= 12 then return nil, false end for index = 1, 12 do local component = data.Transform[index] if type(component) ~= "number" or component ~= component or math.abs(component) == math.huge then return nil, false end end local succeeded, savedCFrame = pcall(function() return CFrame.new(table.unpack(data.Transform, 1, 12)) end) if not succeeded then return nil, false end return {placeId = data.PlaceId, cframe = savedCFrame}, true end function SavedPositions.refreshLabel() if Runtime.closed or not UI.SavedLocationLabel then return end if not SavedPositions.location then UI.SavedLocationLabel.Text = "No location saved." return end local position = SavedPositions.location.cframe.Position UI.SavedLocationLabel.Text = string.format("Saved position: %.1f, %.1f, %.1f", position.X, position.Y, position.Z) if SavedPositions.location.placeId ~= game.PlaceId then UI.SavedLocationLabel.Text = "Saved location belongs to a different place." end end function SavedPositions.saveCurrentLocation() local character = player.Character if not character then notifyUser("Saved Location", "Your character is not available yet.", 4) return end local rootPart = character:FindFirstChild("HumanoidRootPart") if not rootPart then notifyUser("Saved Location", "Your character is not ready yet.", 4) return end SavedPositions.location = {placeId = game.PlaceId, cframe = rootPart.CFrame} SavedPositions.refreshLabel() notifyUser("Saved Location", "Current location saved. Save Config Settings to keep it next session.", 5) end function SavedPositions.teleportToSavedLocation() if not SavedPositions.location then notifyUser("Saved Location", "Save a location first.", 4) return end if SavedPositions.location.placeId ~= game.PlaceId then notifyUser("Saved Location", "That saved location belongs to a different place.", 5) return end if not player.Character or not player.Character:FindFirstChild("HumanoidRootPart") then notifyUser("Saved Location", "Your character is not ready yet.", 4) return end teleportToLocation(SavedPositions.location.cframe) end local FULL_SIZE = UDim2.new(0, 360, 0, 520) local MIN_SIZE = UDim2.new(0, 360, 0, 36) local Frame = Instance.new("Frame") Frame.Size = FULL_SIZE Frame.Position = UDim2.new(0.5, -180, 0.25, 0) Frame.BackgroundColor3 = Color3.fromRGB(24, 24, 26) Frame.Active = true Frame.Parent = ScreenGui Instance.new("UICorner", Frame).CornerRadius = UDim.new(0, 8) local TopBar = Instance.new("Frame") TopBar.Size = UDim2.new(1, 0, 0, 36) TopBar.BackgroundTransparency = 1 TopBar.Parent = Frame UI.TitleLabel = Instance.new("TextLabel") UI.TitleLabel.Size = UDim2.new(1, -70, 0.55, 0) UI.TitleLabel.Position = UDim2.new(0, 10, 0, 2) UI.TitleLabel.BackgroundTransparency = 1 UI.TitleLabel.Text = "Pirate Wars Admin Utility" UI.TitleLabel.TextColor3 = Color3.fromRGB(255, 255, 255) UI.TitleLabel.TextScaled = true UI.TitleLabel.TextXAlignment = Enum.TextXAlignment.Left UI.TitleLabel.Font = Enum.Font.SourceSansBold UI.TitleLabel.Parent = TopBar UI.HealthHUDLabel = Instance.new("TextLabel") UI.HealthHUDLabel.Size = UDim2.new(1, -70, 0.4, 0) UI.HealthHUDLabel.Position = UDim2.new(0, 10, 0.55, 0) UI.HealthHUDLabel.BackgroundTransparency = 1 UI.HealthHUDLabel.Text = "Ship Hull: N/A | Target: N/A" UI.HealthHUDLabel.TextColor3 = Color3.fromRGB(180, 180, 180) UI.HealthHUDLabel.TextScaled = true UI.HealthHUDLabel.TextXAlignment = Enum.TextXAlignment.Left UI.HealthHUDLabel.Font = Enum.Font.SourceSans UI.HealthHUDLabel.Parent = TopBar UI.MinBtn = Instance.new("TextButton") UI.MinBtn.Size = UDim2.new(0, 26, 0, 26) UI.MinBtn.Position = UDim2.new(1, -32, 0, 5) UI.MinBtn.BackgroundColor3 = Color3.fromRGB(50, 50, 50) UI.MinBtn.TextColor3 = Color3.new(1, 1, 1) UI.MinBtn.Text = "-" UI.MinBtn.Font = Enum.Font.SourceSansBold UI.MinBtn.TextScaled = true UI.MinBtn.Parent = TopBar Instance.new("UICorner", UI.MinBtn).CornerRadius = UDim.new(0, 4) -- HORIZONTALLY SCROLLING TAB BAR PREVENTS CRUSHING/OVERCROWDING local TabBar = Instance.new("ScrollingFrame") TabBar.Size = UDim2.new(1, -16, 0, 32) TabBar.Position = UDim2.new(0, 8, 0, 40) TabBar.BackgroundTransparency = 1 TabBar.ScrollBarThickness = 2 TabBar.ScrollBarImageColor3 = Color3.fromRGB(80, 80, 80) TabBar.ScrollingDirection = Enum.ScrollingDirection.X TabBar.AutomaticCanvasSize = Enum.AutomaticSize.X TabBar.CanvasSize = UDim2.new(0, 0, 0, 0) TabBar.Parent = Frame local TabListLayout = Instance.new("UIListLayout") TabListLayout.FillDirection = Enum.FillDirection.Horizontal TabListLayout.SortOrder = Enum.SortOrder.LayoutOrder TabListLayout.Padding = UDim.new(0, 4) TabListLayout.Parent = TabBar local Tabs = {} local TabButtons = {} local function selectTab(name, section) local selectedTab = Tabs[name] if not selectedTab then return end UI.ActiveTab = name for tabName, scroll in pairs(Tabs) do if tabName == name then scroll.Visible = TabBar.Visible TabButtons[tabName].BackgroundColor3 = Color3.fromRGB(0, 120, 215) TabButtons[tabName].TextColor3 = Color3.fromRGB(255, 255, 255) else scroll.Visible = false TabButtons[tabName].BackgroundColor3 = Color3.fromRGB(38, 38, 42) TabButtons[tabName].TextColor3 = Color3.fromRGB(190, 190, 190) end end if section and section.Parent == selectedTab then local sectionOffset = section.AbsolutePosition.Y - selectedTab.AbsolutePosition.Y + selectedTab.CanvasPosition.Y selectedTab.CanvasPosition = Vector2.new(0, math.max(0, sectionOffset)) end end local function createTab(name, displayName, order) local tabBtn = Instance.new("TextButton") tabBtn.Size = UDim2.new(0, 76, 1, 0) tabBtn.BackgroundColor3 = Color3.fromRGB(38, 38, 42) tabBtn.Text = displayName tabBtn.TextColor3 = Color3.fromRGB(190, 190, 190) tabBtn.Font = Enum.Font.SourceSansBold tabBtn.TextSize = 14 tabBtn.LayoutOrder = order tabBtn.Parent = TabBar Instance.new("UICorner", tabBtn).CornerRadius = UDim.new(0, 5) local scroll = Instance.new("ScrollingFrame") scroll.Name = name scroll:SetAttribute("BindingCategory", displayName) scroll.Size = UDim2.new(1, -16, 1, -82) scroll.Position = UDim2.new(0, 8, 0, 76) scroll.BackgroundTransparency = 1 scroll.ScrollBarThickness = 4 scroll.Visible = false scroll.Parent = Frame local layout = Instance.new("UIListLayout") layout.Padding = UDim.new(0, 5) layout.SortOrder = Enum.SortOrder.LayoutOrder layout.HorizontalAlignment = Enum.HorizontalAlignment.Center layout.Parent = scroll layout:GetPropertyChangedSignal("AbsoluteContentSize"):Connect(function() scroll.CanvasSize = UDim2.new(0, 0, 0, layout.AbsoluteContentSize.Y + 15) end) Tabs[name] = scroll TabButtons[name] = tabBtn Keybindings.connectButton("Tab_" .. name, tabBtn, function() selectTab(name) end) return scroll end -- Tab names used by saved bindings stay stable when the visible labels change. local PlayerTab = createTab("Player", "Player", 1) local TeleportTab = createTab("Teleport", "Teleport", 2) local ShipTab = createTab("Ship", "Ships", 3) local CannonsTab = createTab("Cannons", "Cannons", 4) local CombatTab = createTab("Combat", "Combat", 5) local FlingTab = createTab("Fling", "Fling", 6) local ObjectivesTab = createTab("Objectives", "Objectives", 7) local VisualsTab = createTab("Visuals", "Visuals", 8) local ShopTab = createTab("Shop", "Shop", 9) local ConfigTab = createTab("Config", "Settings", 10) local KeybindsTab = createTab("Keybinds", "Keybinds", 11) selectTab("Player") -- MODERATOR ALERT UI local ModWarningLabel = Instance.new("TextLabel") ModWarningLabel.Size = UDim2.new(0, 500, 0, 50) ModWarningLabel.Position = UDim2.new(0.5, -250, 0, 20) ModWarningLabel.BackgroundColor3 = Color3.fromRGB(200, 0, 0) ModWarningLabel.TextColor3 = Color3.new(1, 1, 1) ModWarningLabel.Font = Enum.Font.SourceSansBold ModWarningLabel.TextScaled = true ModWarningLabel.Visible = false ModWarningLabel.ZIndex = 100 ModWarningLabel.Text = "🚨 GAME STAFF DETECTED! 🚨" Instance.new("UICorner", ModWarningLabel).CornerRadius = UDim.new(0, 8) ModWarningLabel.Parent = ScreenGui --// SHIP STATUS HUD BAR (DRAGGABLE, SEMI-TRANSPARENT) -- Lives in OverlayGui and defaults to 64px from the top so it sits clear of the -- game's own match status bar. Drag it anywhere; the offset is stored in Config -- so Save Config Settings keeps it. local ShipHUD = {} ShipHUD.Frame = Instance.new("Frame") ShipHUD.Frame.Name = "ShipStatusHUD" ShipHUD.Frame.AnchorPoint = Vector2.new(0.5, 0) ShipHUD.Frame.Position = UDim2.new(0.5, Config.SHIP_HUD_X, 0, Config.SHIP_HUD_Y) ShipHUD.Frame.Size = UDim2.new(0, 0, 0, 34) ShipHUD.Frame.AutomaticSize = Enum.AutomaticSize.X ShipHUD.Frame.BackgroundColor3 = Color3.fromRGB(12, 16, 24) ShipHUD.Frame.BackgroundTransparency = Config.SHIP_HUD_TRANSPARENCY ShipHUD.Frame.BorderSizePixel = 0 ShipHUD.Frame.Active = true ShipHUD.Frame.Visible = false ShipHUD.Frame.Parent = OverlayGui Instance.new("UICorner", ShipHUD.Frame).CornerRadius = UDim.new(0, 6) do local stroke = Instance.new("UIStroke") stroke.Color = Color3.fromRGB(90, 130, 190) stroke.Transparency = 0.6 stroke.Thickness = 1 stroke.Parent = ShipHUD.Frame local padding = Instance.new("UIPadding") padding.PaddingLeft = UDim.new(0, 10) padding.PaddingRight = UDim.new(0, 12) padding.Parent = ShipHUD.Frame local layout = Instance.new("UIListLayout") layout.FillDirection = Enum.FillDirection.Horizontal layout.SortOrder = Enum.SortOrder.LayoutOrder layout.VerticalAlignment = Enum.VerticalAlignment.Center layout.Padding = UDim.new(0, 8) layout.Parent = ShipHUD.Frame end ShipHUD.segments = {} do local separator = Color3.fromRGB(70, 82, 100) local normal = Color3.fromRGB(200, 215, 240) local definitions = { {"Grip", Color3.fromRGB(110, 125, 150), ":::"}, {"Ship", Color3.fromRGB(235, 240, 250), "No ship"}, {"Sep1", separator, "|"}, {"Cannons", normal, "Cannons -"}, {"Sep2", separator, "|"}, {"Reload", Color3.fromRGB(120, 230, 150), "READY!"}, {"Sep3", separator, "|"}, {"AutoFire", normal, "L- R- All- Broadside-"}, {"Sep4", separator, "|"}, {"Hull", normal, "Hull -"}, {"Sep5", separator, "|"}, {"Speed", Color3.fromRGB(170, 190, 215), "0 sps"}, } for order, definition in ipairs(definitions) do local label = Instance.new("TextLabel") label.Name = definition[1] label.Size = UDim2.new(0, 0, 1, 0) label.AutomaticSize = Enum.AutomaticSize.X label.BackgroundTransparency = 1 label.Font = Enum.Font.SourceSansBold label.TextSize = 15 label.TextColor3 = definition[2] label.TextXAlignment = Enum.TextXAlignment.Left label.Text = definition[3] label.LayoutOrder = order label.Parent = ShipHUD.Frame ShipHUD.segments[definition[1]] = label end end function ShipHUD.applyPosition() ShipHUD.Frame.Position = UDim2.new(0.5, Config.SHIP_HUD_X, 0, Config.SHIP_HUD_Y) end function ShipHUD.applyTransparency() local value = tonumber(Config.SHIP_HUD_TRANSPARENCY) or 0.35 Config.SHIP_HUD_TRANSPARENCY = math.clamp(value, 0, 0.95) ShipHUD.Frame.BackgroundTransparency = Config.SHIP_HUD_TRANSPARENCY end function ShipHUD.resetPosition() Config.SHIP_HUD_X = 0 Config.SHIP_HUD_Y = 64 ShipHUD.applyPosition() end do local hudDragging = false local hudDragInput = nil local hudDragStart = nil local hudStartOffset = nil table.insert(Connections, ShipHUD.Frame.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then hudDragging = true hudDragStart = input.Position hudStartOffset = Vector2.new(Config.SHIP_HUD_X, Config.SHIP_HUD_Y) input.Changed:Connect(function() if input.UserInputState == Enum.UserInputState.End then hudDragging = false end end) end end)) table.insert(Connections, ShipHUD.Frame.InputChanged:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then hudDragInput = input end end)) table.insert(Connections, UserInputService.InputChanged:Connect(function(input) if hudDragging and input == hudDragInput and hudDragStart and hudStartOffset then local delta = input.Position - hudDragStart Config.SHIP_HUD_X = math.floor(hudStartOffset.X + delta.X) Config.SHIP_HUD_Y = math.max(0, math.floor(hudStartOffset.Y + delta.Y)) ShipHUD.applyPosition() end end)) end -- CUSTOM CHAT GUI local ChatGui = Instance.new("Frame") ChatGui.Name = "CustomChatGui" ChatGui.Size = UDim2.new(0, 400, 0, 220) ChatGui.Position = UDim2.new(0, 15, 1, -250) ChatGui.BackgroundTransparency = 0.5 ChatGui.BackgroundColor3 = Color3.fromRGB(0, 0, 0) ChatGui.Visible = false ChatGui.Parent = ScreenGui Instance.new("UICorner", ChatGui).CornerRadius = UDim.new(0, 6) local ChatScroll = Instance.new("ScrollingFrame") ChatScroll.Size = UDim2.new(1, -10, 1, -10) ChatScroll.Position = UDim2.new(0, 5, 0, 5) ChatScroll.BackgroundTransparency = 1 ChatScroll.ScrollBarThickness = 4 ChatScroll.CanvasSize = UDim2.new(0, 0, 0, 0) ChatScroll.Parent = ChatGui local ChatLayout = Instance.new("UIListLayout") ChatLayout.SortOrder = Enum.SortOrder.LayoutOrder ChatLayout.Padding = UDim.new(0, 4) ChatLayout.VerticalAlignment = Enum.VerticalAlignment.Bottom ChatLayout.Parent = ChatScroll local chatMessageCount = 0 local function handleIncomingChatMessage(rawMsg) if FlagCapture.onCaptureMessage then pcall(FlagCapture.onCaptureMessage, rawMsg) end local cleanMsg = string.gsub(rawMsg, "\t", " ") cleanMsg = string.gsub(cleanMsg, "\9", " ") chatMessageCount = chatMessageCount + 1 local msgLbl = Instance.new("TextLabel") msgLbl.Size = UDim2.new(1, 0, 0, 16) msgLbl.BackgroundTransparency = 1 msgLbl.Text = cleanMsg msgLbl.TextColor3 = Color3.fromRGB(255, 255, 255) msgLbl.TextXAlignment = Enum.TextXAlignment.Left msgLbl.TextWrapped = true msgLbl.Font = Enum.Font.SourceSansBold msgLbl.TextSize = 15 msgLbl.LayoutOrder = chatMessageCount msgLbl.Parent = ChatScroll task.defer(function() if msgLbl.TextBounds.Y > 16 then msgLbl.Size = UDim2.new(1, 0, 0, msgLbl.TextBounds.Y + 2) end ChatScroll.CanvasSize = UDim2.new(0, 0, 0, ChatLayout.AbsoluteContentSize.Y) ChatScroll.CanvasPosition = Vector2.new(0, ChatScroll.CanvasSize.Y.Offset) end) local children = ChatScroll:GetChildren() local messages = {} for _, child in ipairs(children) do if child:IsA("TextLabel") then table.insert(messages, child) end end if #messages > 50 then table.sort(messages, function(a, b) return a.LayoutOrder < b.LayoutOrder end) messages[1]:Destroy() end end local function attachChatListener(remote) if remote:IsA("RemoteEvent") then table.insert(Connections, remote.OnClientEvent:Connect(function(action, data) if action == "KUpdateCLog" and type(data) == "table" and data[2] and type(data[2]) == "string" then handleIncomingChatMessage(data[2]) end end)) end end for _, obj in ipairs(ReplicatedStorage:GetDescendants()) do attachChatListener(obj) end table.insert(Connections, ReplicatedStorage.DescendantAdded:Connect(attachChatListener)) local function createSectionHeader(tab, title, order) local lbl = Instance.new("TextLabel") lbl.Size = UDim2.new(0.96, 0, 0, 20) lbl.BackgroundTransparency = 1 lbl.Text = "— " .. title .. " —" lbl.TextColor3 = Color3.fromRGB(150, 150, 160) lbl.Font = Enum.Font.SourceSansBold lbl.TextSize = 14 lbl.LayoutOrder = order lbl.Parent = tab return lbl end local function createButton(tab, text, defaultColor, order) local btn = Instance.new("TextButton") btn.Size = UDim2.new(0.96, 0, 0, 30) btn.BackgroundColor3 = defaultColor btn.Text = text btn.TextColor3 = Color3.fromRGB(255, 255, 255) btn.TextScaled = true btn.Font = Enum.Font.SourceSansBold btn.LayoutOrder = order btn.Parent = tab Instance.new("UICorner", btn).CornerRadius = UDim.new(0, 5) return btn end function UI.createInputField(tab, labelText, defaultText, order) local row = Instance.new("Frame") row.Size = UDim2.new(0.96, 0, 0, 52) row.BackgroundTransparency = 1 row.LayoutOrder = order row.Parent = tab local label = Instance.new("TextLabel") label.Size = UDim2.new(1, 0, 0, 22) label.BackgroundTransparency = 1 label.Text = labelText label.TextColor3 = Color3.fromRGB(190, 200, 215) label.Font = Enum.Font.SourceSans label.TextSize = 14 label.TextWrapped = true label.TextXAlignment = Enum.TextXAlignment.Left label.Parent = row local box = Instance.new("TextBox") box.Size = UDim2.new(1, 0, 0, 26) box.Position = UDim2.new(0, 0, 0, 24) box.BackgroundColor3 = Color3.fromRGB(38, 38, 42) box.TextColor3 = Color3.new(1, 1, 1) box.Text = defaultText box.PlaceholderText = labelText box.Font = Enum.Font.SourceSansBold box.TextScaled = true box.Parent = row Instance.new("UICorner", box).CornerRadius = UDim.new(0, 4) return box end local function createInput(tab, placeholder, defaultText, order, callback) local box = UI.createInputField(tab, placeholder, defaultText, order) box.FocusLost:Connect(function() local value = tonumber(box.Text) if value and value == value and math.abs(value) ~= math.huge then callback(value) else box.Text = defaultText end end) return box end local function createTextInput(tab, placeholder, defaultText, order, callback) local box = UI.createInputField(tab, placeholder, defaultText, order) box.FocusLost:Connect(function() callback(box.Text) end) return box end local function createInfoLabel(tab, order, text, color) local lbl = Instance.new("TextLabel") lbl.Size = UDim2.new(0.96, 0, 0, 34) lbl.AutomaticSize = Enum.AutomaticSize.Y lbl.BackgroundTransparency = 1 lbl.TextColor3 = color or Color3.fromRGB(200, 208, 222) lbl.Font = Enum.Font.SourceSans lbl.TextSize = 15 lbl.TextWrapped = true lbl.TextXAlignment = Enum.TextXAlignment.Left lbl.Text = text lbl.LayoutOrder = order lbl.Parent = tab return lbl end -- PLAYER TAB createSectionHeader(PlayerTab, "Movement & Mobility", 1) UI.CharSpeedBtn = createButton(PlayerTab, "Player WalkSpeed Override: OFF", Color3.fromRGB(170, 50, 50), 2) UI.Inputs.PLAYER_WALKSPEED = createInput(PlayerTab, "WalkSpeed", tostring(Config.PLAYER_WALKSPEED), 3, function(val) Config.PLAYER_WALKSPEED = val end) UI.CharJumpBtn = createButton(PlayerTab, "Player JumpPower Override: OFF", Color3.fromRGB(170, 50, 50), 4) UI.Inputs.PLAYER_JUMPPOWER = createInput(PlayerTab, "JumpPower", tostring(Config.PLAYER_JUMPPOWER), 5, function(val) Config.PLAYER_JUMPPOWER = val end) UI.InfJumpBtn = createButton(PlayerTab, "Infinite Jump: OFF", Color3.fromRGB(170, 50, 50), 6) UI.PlayerFlyBtn = createButton(PlayerTab, "Player Fly: OFF", Color3.fromRGB(170, 50, 50), 7) UI.Inputs.PLAYER_FLY_SPEED = createInput(PlayerTab, "Player Fly Speed", tostring(Config.PLAYER_FLY_SPEED), 8, function(val) Config.PLAYER_FLY_SPEED = val end) UI.PlayerNoClipBtn = createButton(PlayerTab, "Player No-Clip: OFF", Color3.fromRGB(170, 50, 50), 9) createSectionHeader(PlayerTab, "Environment & Safety", 10) UI.WaterWalkBtn = createButton(PlayerTab, "Jesus Walk (Water): OFF", Color3.fromRGB(170, 50, 50), 11) UI.Inputs.WATER_WALK_HEIGHT = createInput(PlayerTab, "Water Y Height", tostring(Config.WATER_WALK_HEIGHT), 12, function(val) Config.WATER_WALK_HEIGHT = val end) UI.AntiRagdollBtn = createButton(PlayerTab, "Anti-Ragdoll / Stun: OFF", Color3.fromRGB(170, 50, 50), 13) UI.AntiSitBtn = createButton(PlayerTab, "Anti-Sit / Anti-Seat: OFF", Color3.fromRGB(170, 50, 50), 14) UI.AntiVoidBtn = createButton(PlayerTab, "Anti-Void/Water Death: OFF", Color3.fromRGB(170, 50, 50), 15) UI.Inputs.ANTI_VOID_HEIGHT = createInput(PlayerTab, "Anti-Void Trigger Height", tostring(Config.ANTI_VOID_HEIGHT), 16, function(val) Config.ANTI_VOID_HEIGHT = val end) UI.AutoHPReturnBtn = createButton(PlayerTab, "Auto Return on Low HP: OFF", Color3.fromRGB(170, 50, 50), 17) UI.Inputs.AUTO_RETURN_HP_PCT = createInput(PlayerTab, "HP % Threshold", tostring(Config.AUTO_RETURN_HP_PCT), 18, function(val) Config.AUTO_RETURN_HP_PCT = val end) -- Team controls UI.TeamSection = createSectionHeader(PlayerTab, "Team Management", 19) UI.SwitchTeamBtn = createButton(PlayerTab, "Switch / Travel Team (Cost: 75)", Color3.fromRGB(0, 120, 180), 20) UI.BetrayBtn = createButton(PlayerTab, "Betray Team (Cost: 175) [30m CD]", Color3.fromRGB(180, 45, 45), 21) local TeamInfoLabel = Instance.new("TextLabel") TeamInfoLabel.Size = UDim2.new(0.96, 0, 0, 75) TeamInfoLabel.BackgroundColor3 = Color3.fromRGB(35, 35, 38) TeamInfoLabel.TextColor3 = Color3.fromRGB(200, 200, 200) TeamInfoLabel.Text = "• Travel Team fires server event (75)\n• Betrayal fires server event (175) and inflicts a 30-minute global server cooldown timer." TeamInfoLabel.Font = Enum.Font.SourceSans TeamInfoLabel.TextSize = 14 TeamInfoLabel.TextWrapped = true TeamInfoLabel.LayoutOrder = 22 TeamInfoLabel.Parent = PlayerTab Instance.new("UICorner", TeamInfoLabel).CornerRadius = UDim.new(0, 5) -- REMOTE EVENT SENDER FOR TEAM local function fireTeamChanged(arg) pcall(function() local teamStuff = ReplicatedStorage:FindFirstChild("TeamStuff") local remote = teamStuff and teamStuff:FindFirstChild("TeamChanged") if not remote then remote = ReplicatedStorage:FindFirstChild("TeamChanged", true) end if remote and remote:IsA("RemoteEvent") then remote:FireServer(arg) end end) end Keybindings.connectButton("SwitchTeamBtn", UI.SwitchTeamBtn, function() fireTeamChanged(75) UI.SwitchTeamBtn.Text = "Team Switch Sent (75)!" task.delay(1.5, function() UI.SwitchTeamBtn.Text = "Switch / Travel Team (Cost: 75)" end) end) Keybindings.connectButton("BetrayBtn", UI.BetrayBtn, function() local now = os.time() local diff = now - (State.lastBetrayTime or 0) if diff < BETRAY_COOLDOWN and State.lastBetrayTime > 0 then local rem = BETRAY_COOLDOWN - diff UI.BetrayBtn.Text = string.format("On Cooldown! (%02dm %02ds)", math.floor(rem / 60), rem % 60) return end State.lastBetrayTime = now fireTeamChanged(175) UI.BetrayBtn.Text = "Betrayal Sent (175)!" end) -- Background Betrayal CD Timer Display task.spawn(function() while ScreenGui.Parent do local now = os.time() local diff = now - (State.lastBetrayTime or 0) if diff < BETRAY_COOLDOWN and State.lastBetrayTime > 0 then local rem = BETRAY_COOLDOWN - diff local mins = math.floor(rem / 60) local secs = rem % 60 UI.BetrayBtn.Text = string.format("Betray Team [%02d:%02d]", mins, secs) UI.BetrayBtn.BackgroundColor3 = Color3.fromRGB(110, 50, 50) else if UI.BetrayBtn.Text:find("Betray Team %[") or UI.BetrayBtn.Text:find("Betrayal Sent") then UI.BetrayBtn.Text = "Betray Team (Cost: 175) [30m CD]" UI.BetrayBtn.BackgroundColor3 = Color3.fromRGB(180, 45, 45) end end task.wait(1) end end) -- TELEPORT TAB createSectionHeader(TeleportTab, "Mouse Teleport", 1) UI.ClickTPBtn = createButton(TeleportTab, "Mouse Teleport: OFF", Color3.fromRGB(170, 50, 50), 2) createSectionHeader(TeleportTab, "Saved Location", 3) UI.SaveLocationBtn = createButton(TeleportTab, "Save Current Location", Color3.fromRGB(0, 140, 70), 4) UI.TeleportSavedLocationBtn = createButton(TeleportTab, "Teleport to Saved Location", Color3.fromRGB(60, 90, 160), 5) UI.SavedLocationLabel = Instance.new("TextLabel") UI.SavedLocationLabel.Size = UDim2.new(0.96, 0, 0, 46) UI.SavedLocationLabel.AutomaticSize = Enum.AutomaticSize.Y UI.SavedLocationLabel.BackgroundTransparency = 1 UI.SavedLocationLabel.TextColor3 = Color3.fromRGB(210, 215, 225) UI.SavedLocationLabel.Font = Enum.Font.SourceSans UI.SavedLocationLabel.TextSize = 15 UI.SavedLocationLabel.TextWrapped = true UI.SavedLocationLabel.TextXAlignment = Enum.TextXAlignment.Left UI.SavedLocationLabel.Text = "No location saved." UI.SavedLocationLabel.LayoutOrder = 6 UI.SavedLocationLabel.Parent = TeleportTab createSectionHeader(TeleportTab, "Team Spawns", 7) UI.TPBlueSpawnBtn = createButton(TeleportTab, "TP: Blue Team Spawn", Color3.fromRGB(0, 100, 200), 8) UI.TPRedSpawnBtn = createButton(TeleportTab, "TP: Red Team Spawn", Color3.fromRGB(200, 50, 50), 9) createSectionHeader(TeleportTab, "Key Locations", 10) UI.TPCenterFortBtn = createButton(TeleportTab, "TP: Center Fort / Island", Color3.fromRGB(60, 90, 160), 11) UI.TPNatsShopBtn = createButton(TeleportTab, "TP: Nat's Shop", Color3.fromRGB(60, 90, 160), 12) UI.TPSamsJailBtn = createButton(TeleportTab, "TP: Sam's Jail", Color3.fromRGB(60, 90, 160), 13) UI.TPLavaSpikeBtn = createButton(TeleportTab, "TP: Lava Spike", Color3.fromRGB(60, 90, 160), 14) createSectionHeader(TeleportTab, "Achievement Landmarks", 15) UI.TPBananaBtn = createButton(TeleportTab, "TP: Banana Island", Color3.fromRGB(55, 65, 80), 16) UI.TPPudinzoBtn = createButton(TeleportTab, "TP: Pudinzo", Color3.fromRGB(55, 65, 80), 17) UI.TPSandCastleBtn = createButton(TeleportTab, "TP: Sand Castle", Color3.fromRGB(55, 65, 80), 18) UI.TPColaBtn = createButton(TeleportTab, "TP: Cola Bottle", Color3.fromRGB(55, 65, 80), 19) UI.TPShipBottleBtn = createButton(TeleportTab, "TP: Ship in a Bottle", Color3.fromRGB(55, 65, 80), 20) createInfoLabel(TeleportTab, 21, "Normal teleports try to bring your ship only while you occupy its captain seat and are its only player. Standing on deck or sitting in a passenger seat teleports only you. If anyone else is aboard, only you teleport. The ship keeps its heading. Mouse, saved-location, map, target and freecam teleports share this behavior.") -- SHIPS TAB createSectionHeader(ShipTab, "Boarding & Takeover", 1) UI.StealOpenBtn = createButton(ShipTab, "Teleport to Next Open Ship", Color3.fromRGB(130, 50, 170), 2) UI.BoardCaptainBtn = createButton(ShipTab, "Board Enemy Captain", Color3.fromRGB(190, 85, 0), 3) UI.StealEnemyBtn = createButton(ShipTab, "Steal Enemy Ships: OFF", Color3.fromRGB(170, 50, 50), 4) UI.StealFriendlyBtn = createButton(ShipTab, "Steal Friendly Ships: OFF", Color3.fromRGB(170, 50, 50), 5) UI.PauseStealBtn = createButton(ShipTab, "Pause Steal: OFF", Color3.fromRGB(170, 50, 50), 6) createSectionHeader(ShipTab, "Ship Movement", 7) UI.SpeedBtn = createButton(ShipTab, "Ship Speed Boost: OFF", Color3.fromRGB(170, 50, 50), 8) UI.Inputs.BOOST_SPEED = createInput(ShipTab, "Speed Multiplier", tostring(Config.BOOST_SPEED), 9, function(val) Config.BOOST_SPEED = val end) UI.FlyBtn = createButton(ShipTab, "Ship Flight: OFF", Color3.fromRGB(170, 50, 50), 10) UI.Inputs.FLY_SPEED = createInput(ShipTab, "Fly Speed Val", tostring(Config.FLY_SPEED), 11, function(val) Config.FLY_SPEED = val end) UI.AntiSinkBtn = createButton(ShipTab, "Ship Anti-Sink: OFF", Color3.fromRGB(170, 50, 50), 12) UI.Inputs.MIN_SINK_HEIGHT = createInput(ShipTab, "Min Height (Water Level)", tostring(Config.MIN_SINK_HEIGHT), 13, function(val) Config.MIN_SINK_HEIGHT = val end) UI.ShipNoClipBtn = createButton(ShipTab, "Ship No-Clip: OFF", Color3.fromRGB(170, 50, 50), 14) createSectionHeader(ShipTab, "Steering & Quick Turns", 15) UI.TurnLeftBtn = createButton(ShipTab, "Quick Turn Left 45", Color3.fromRGB(60, 90, 160), 16) UI.TurnRightBtn = createButton(ShipTab, "Quick Turn Right 45", Color3.fromRGB(60, 90, 160), 17) UI.QuickTurnInvertBtn = createButton(ShipTab, "Invert Steer Direction: OFF", Color3.fromRGB(170, 50, 50), 18) UI.Inputs.QUICK_TURN_DEGREES = createInput(ShipTab, "Quick Turn Degrees", tostring(Config.QUICK_TURN_DEGREES), 19, function(val) Config.QUICK_TURN_DEGREES = math.clamp(math.abs(val), 1, 179) if refreshQuickTurnUI then refreshQuickTurnUI() end end) UI.Inputs.QUICK_TURN_LEAD = createInput(ShipTab, "Turn Lead (sec, higher = stops earlier)", tostring(Config.QUICK_TURN_LEAD), 20, function(val) Config.QUICK_TURN_LEAD = math.clamp(val, 0, 3) end) UI.QuickTurnStatusLabel = createInfoLabel(ShipTab, 21, "Holds the helm until the hull has actually swung the requested number of degrees, then reports the result. Turn Lead centres the helm early to allow for the ship coasting: raise it if turns overshoot, lower it if they stop short. Steer direction is detected on the first turn and saved; flip it above if a right turn swings left.", Color3.fromRGB(190, 200, 215)) createSectionHeader(ShipTab, "Bring Open Ships", 22) UI.BringShipsBtn = createButton(ShipTab, "Bring Open Ships: OFF", Color3.fromRGB(170, 50, 50), 23) UI.BringShipsDestinationBtn = createButton(ShipTab, "Bring To: Point", Color3.fromRGB(55, 55, 65), 24) UI.BringShipsHereBtn = createButton(ShipTab, "Set Bring Point Here", Color3.fromRGB(60, 90, 160), 25) UI.BringShipsMouseBtn = createButton(ShipTab, "Set Bring Point at Mouse", Color3.fromRGB(60, 90, 160), 26) UI.BringShipsPointLabel = createInfoLabel(ShipTab, 27, "No bring point saved.") UI.BringShipsPlayerInput = createTextInput(ShipTab, "Destination player username, or me", Config.BRING_SHIPS_PLAYER, 28, function(value) Config.BRING_SHIPS_PLAYER = value end) UI.BringShipsModeBtn = createButton(ShipTab, "Move Method: Auto", Color3.fromRGB(55, 55, 65), 29) UI.Inputs.BRING_SHIPS_SPEED = createInput(ShipTab, "Bring Speed (Studs Per Second)", tostring(Config.BRING_SHIPS_SPEED), 30, function(value) Config.BRING_SHIPS_SPEED = math.clamp(value, 10, 500) end) UI.Inputs.BRING_SHIPS_TIMEOUT = createInput(ShipTab, "Move Timeout Per Ship (Seconds)", tostring(Config.BRING_SHIPS_TIMEOUT), 31, function(value) Config.BRING_SHIPS_TIMEOUT = math.clamp(value, 3, 60) end) UI.Inputs.BRING_SHIPS_ARRIVAL_DISTANCE = createInput(ShipTab, "Arrival Distance (Studs)", tostring(Config.BRING_SHIPS_ARRIVAL_DISTANCE), 32, function(value) Config.BRING_SHIPS_ARRIVAL_DISTANCE = math.clamp(value, 5, 100) end) UI.Inputs.BRING_SHIPS_PASS_DELAY = createInput(ShipTab, "Wait Between Ship Passes (Seconds)", tostring(Config.BRING_SHIPS_PASS_DELAY), 33, function(value) Config.BRING_SHIPS_PASS_DELAY = math.clamp(value, 0.1, 30) end) UI.Inputs.BRING_SHIPS_MAX_DISTANCE = createInput(ShipTab, "Max Distance From Center Fort (Studs)", tostring(Config.BRING_SHIPS_MAX_DISTANCE), 34, function(value) Config.BRING_SHIPS_MAX_DISTANCE = math.clamp(value, 1000, 20000) end) UI.Inputs.BRING_SHIPS_MAX_WATER_OFFSET = createInput(ShipTab, "Max Height Above/Below Water (Studs)", tostring(Config.BRING_SHIPS_MAX_WATER_OFFSET), 35, function(value) Config.BRING_SHIPS_MAX_WATER_OFFSET = math.clamp(value, 30, 1000) end) UI.BringShipsStatus = createInfoLabel(ShipTab, 36, "Loops through open ships on every team. Skips ships in the void, far outside the main islands, or already within Arrival Distance. Unseats you as soon as a ship arrives. Auto tries teleporting, then sailing if the teleport does not hold. Uses horizontal distance and keeps the ship at water height. Player destinations follow that player; me uses your starting point.") createSectionHeader(ShipTab, "Cola Crate Delivery", 37) UI.BringColaToSpawnsBtn = createButton(ShipTab, "Bring Cola Crates to Both Spawns", Color3.fromRGB(0, 105, 140), 38) UI.ColaDeliveryStatus = createInfoLabel(ShipTab, 39, "Spawns and delivers one new Cola Crate to each team's spawn, then returns you. Requires the Cola Bottle unlock and 10 gold total. Press again to cancel.") -- CANNONS TAB createSectionHeader(CannonsTab, "Naval Cannons", 1) UI.AutoFireBtn = createButton(CannonsTab, "Broadside Auto-Fire: OFF", Color3.fromRGB(170, 50, 50), 2) UI.Inputs.AUTO_FIRE_DIST = createInput(CannonsTab, "Auto-Fire Range", tostring(Config.AUTO_FIRE_DIST), 3, function(val) Config.AUTO_FIRE_DIST = val end) UI.LeftBtn = createButton(CannonsTab, "Fire Left Cannons: OFF", Color3.fromRGB(170, 50, 50), 4) UI.RightBtn = createButton(CannonsTab, "Fire Right Cannons: OFF", Color3.fromRGB(170, 50, 50), 5) createSectionHeader(CannonsTab, "Fire Everything (My Seated Ship)", 6) UI.FireAllOnceBtn = createButton(CannonsTab, "Fire ALL Cannons Once", Color3.fromRGB(190, 85, 0), 7) UI.FireAllBtn = createButton(CannonsTab, "Fire ALL Cannons (Hold): OFF", Color3.fromRGB(170, 50, 50), 8) UI.FireAllBroadBtn = createButton(CannonsTab, "Include Non-Cannon Activators: ON", Color3.fromRGB(0, 170, 0), 9) UI.Inputs.FIRE_PER_SECOND = createInput(CannonsTab, "Clicks Per Second", tostring(Config.FIRE_PER_SECOND), 10, function(val) Config.FIRE_PER_SECOND = math.clamp(val, 0.5, 200) end) UI.CannonCountLabel = createInfoLabel(CannonsTab, 11, "Not seated on a ship.", Color3.fromRGB(190, 200, 215)) createSectionHeader(CannonsTab, "Target Ship", 12) UI.TargetInfoBox = Instance.new("TextLabel") UI.TargetInfoBox.Size = UDim2.new(0.96, 0, 0, 68) UI.TargetInfoBox.BackgroundColor3 = Color3.fromRGB(35, 35, 35) UI.TargetInfoBox.TextColor3 = Color3.fromRGB(220, 220, 220) UI.TargetInfoBox.Text = "Target Ship: None\nHull Integrity: N/A\nDistance: N/A\nCaptain: None" UI.TargetInfoBox.Font = Enum.Font.SourceSansBold UI.TargetInfoBox.TextScaled = true UI.TargetInfoBox.LayoutOrder = 13 UI.TargetInfoBox.Parent = CannonsTab Instance.new("UICorner", UI.TargetInfoBox).CornerRadius = UDim.new(0, 5) -- COMBAT TAB createSectionHeader(CombatTab, "Melee & Sword Reach", 1) UI.KillAuraBtn = createButton(CombatTab, "Melee Kill Aura: OFF", Color3.fromRGB(170, 50, 50), 2) UI.Inputs.KILL_AURA_RANGE = createInput(CombatTab, "Kill Aura Range", tostring(Config.KILL_AURA_RANGE), 3, function(val) Config.KILL_AURA_RANGE = val end) UI.ReachBtn = createButton(CombatTab, "Melee Range Extender: OFF", Color3.fromRGB(170, 50, 50), 4) UI.Inputs.MELEE_REACH_SIZE = createInput(CombatTab, "Extender Size (Studs)", tostring(Config.MELEE_REACH_SIZE), 5, function(val) Config.MELEE_REACH_SIZE = val end) createSectionHeader(CombatTab, "Sword Loop", 6) UI.LoopKillEnemiesBtn = createButton(CombatTab, "Loop Kill All Enemies: OFF", Color3.fromRGB(170, 50, 50), 7) UI.Inputs.LOOP_KILL_ATTACKS_PER_SECOND = createInput(CombatTab, "Sword Attacks Per Second", tostring(Config.LOOP_KILL_ATTACKS_PER_SECOND), 8, function(value) Config.LOOP_KILL_ATTACKS_PER_SECOND = math.clamp(value, 1, 20) end) UI.Inputs.LOOP_KILL_BEHIND_DISTANCE = createInput(CombatTab, "Distance Behind Enemy (Studs)", tostring(Config.LOOP_KILL_BEHIND_DISTANCE), 9, function(value) Config.LOOP_KILL_BEHIND_DISTANCE = math.clamp(value, 1.5, 8) end) UI.Inputs.LOOP_KILL_TARGET_SECONDS = createInput(CombatTab, "Seconds Per Enemy Before Cycling", tostring(Config.LOOP_KILL_TARGET_SECONDS), 10, function(value) Config.LOOP_KILL_TARGET_SECONDS = math.clamp(value, 1, 30) end) UI.Inputs.LOOP_KILL_SPAWN_WAIT = createInput(CombatTab, "Wait At Spawn After Kill (Seconds)", tostring(Config.LOOP_KILL_SPAWN_WAIT), 11, function(value) Config.LOOP_KILL_SPAWN_WAIT = math.clamp(value, 0.1, 10) end) UI.LoopKillEnemiesStatus = createInfoLabel(CombatTab, 12, "Equips hotbar slot 1 and follows behind enemies. Returns to your spawn after each target dies, then waits briefly before continuing. Skips teammates and spawn protection. Both sword loops use the settings above.") UI.HitboxSection = createSectionHeader(CombatTab, "Player Hitboxes", 13) UI.HitboxToggleBtn = createButton(CombatTab, "Enemy Hitbox Expander: OFF", Color3.fromRGB(170, 50, 50), 14) UI.HitboxPartDropdown = createButton(CombatTab, "Target Part: " .. Config.HITBOX_TARGET_PART, Color3.fromRGB(55, 55, 65), 15) UI.Inputs.HITBOX_SIZE = createInput(CombatTab, "Hitbox Size Multiplier", tostring(Config.HITBOX_SIZE), 16, function(val) Config.HITBOX_SIZE = val end) UI.HitboxVisualBtn = createButton(CombatTab, "Visualize Hitbox: OFF", Color3.fromRGB(170, 50, 50), 17) -- FLING TAB createSectionHeader(FlingTab, "Target Selection", 1) UI.FlingDropdownBtn = createButton(FlingTab, "Select Target Player...", Color3.fromRGB(55, 55, 65), 2) UI.PlayerListScroll = Instance.new("ScrollingFrame") UI.PlayerListScroll.Size = UDim2.new(0.96, 0, 0, 110) UI.PlayerListScroll.BackgroundColor3 = Color3.fromRGB(35, 35, 35) UI.PlayerListScroll.ScrollBarThickness = 4 UI.PlayerListScroll.Visible = false UI.PlayerListScroll.LayoutOrder = 3 UI.PlayerListScroll.Parent = FlingTab Instance.new("UICorner", UI.PlayerListScroll).CornerRadius = UDim.new(0, 4) UI.DropdownLayout = Instance.new("UIListLayout") UI.DropdownLayout.SortOrder = Enum.SortOrder.LayoutOrder UI.DropdownLayout.Padding = UDim.new(0, 2) UI.DropdownLayout.Parent = UI.PlayerListScroll UI.TargetInputBox = createTextInput(FlingTab, "Target Player (Partial Name)", "", 4, function(text) State.flingTargetStr = text UI.FlingDropdownBtn.Text = "Target: " .. (text == "" and "None" or text) end) Keybindings.connectButton("FlingDropdownBtn", UI.FlingDropdownBtn, function() UI.PlayerListScroll.Visible = not UI.PlayerListScroll.Visible if UI.PlayerListScroll.Visible then for _, child in ipairs(UI.PlayerListScroll:GetChildren()) do if child:IsA("TextButton") then child:Destroy() end end local playersList = Players:GetPlayers() local index = 1 for _, p in ipairs(playersList) do if p ~= player then local pBtn = Instance.new("TextButton") pBtn.Size = UDim2.new(1, -8, 0, 24) pBtn.BackgroundColor3 = Color3.fromRGB(50, 50, 50) pBtn.TextColor3 = Color3.new(1, 1, 1) pBtn.Font = Enum.Font.SourceSansBold pBtn.TextScaled = true pBtn.Text = p.Name .. (p.DisplayName ~= p.Name and (" (@" .. p.DisplayName .. ")") or "") pBtn.LayoutOrder = index pBtn.Parent = UI.PlayerListScroll Instance.new("UICorner", pBtn).CornerRadius = UDim.new(0, 3) pBtn.MouseButton1Click:Connect(function() State.flingTargetStr = p.Name UI.TargetInputBox.Text = p.Name UI.FlingDropdownBtn.Text = "Target: " .. p.Name UI.PlayerListScroll.Visible = false end) index = index + 1 end end UI.PlayerListScroll.CanvasSize = UDim2.new(0, 0, 0, (index - 1) * 26) end end) createSectionHeader(FlingTab, "Target Actions", 5) UI.TPToTargetBtn = createButton(FlingTab, "TP to Target Player", Color3.fromRGB(60, 90, 160), 6) UI.SpectateBtn = createButton(FlingTab, "Spectate Target: OFF", Color3.fromRGB(170, 50, 50), 7) createSectionHeader(FlingTab, "Target Sword Loop", 8) UI.LoopKillTargetBtn = createButton(FlingTab, "Loop Kill Target: OFF", Color3.fromRGB(170, 50, 50), 9) UI.LoopKillTargetStatus = createInfoLabel(FlingTab, 10, "Uses the selected player and the sword in hotbar slot 1. Returns to your spawn after each kill and waits there for this player to respawn. Sword settings and the wait after each kill are in Combat.") createSectionHeader(FlingTab, "Fling Configuration", 11) UI.FlingToggleBtn = createButton(FlingTab, "Ultimate Fling: OFF", Color3.fromRGB(170, 50, 50), 12) UI.FlingModeBtn = createButton(FlingTab, "Mode: " .. flingModes[Config.FLING_MODE_INDEX], Color3.fromRGB(60, 90, 160), 13) UI.Inputs.FLING_POWER = createInput(FlingTab, "Power Multiplier", tostring(Config.FLING_POWER), 14, function(val) Config.FLING_POWER = val end) UI.FlingAttachBtn = createButton(FlingTab, "Attach to Target: OFF", Color3.fromRGB(170, 50, 50), 15) UI.AutoReturnBtn = createButton(FlingTab, "Auto-Return on Success: OFF", Color3.fromRGB(170, 50, 50), 16) UI.TPSafetyBtn = createButton(FlingTab, "Emergency TP to Safety", Color3.fromRGB(190, 100, 0), 17) createSectionHeader(FlingTab, "Fling Aura", 18) UI.FlingAuraBtn = createButton(FlingTab, "Fling Aura: OFF", Color3.fromRGB(170, 50, 50), 19) UI.Inputs.FLING_AURA_RANGE = createInput(FlingTab, "Fling Aura Range", tostring(Config.FLING_AURA_RANGE), 20, function(val) Config.FLING_AURA_RANGE = val end) UI.FlingAuraVisBtn = createButton(FlingTab, "Visualize Aura Range: OFF", Color3.fromRGB(170, 50, 50), 21) UI.FlingAuraTeamBtn = createButton(FlingTab, "Aura Includes Teammates: OFF", Color3.fromRGB(170, 50, 50), 22) -- OBJECTIVES TAB (MISSIONS & FARMS) createSectionHeader(ObjectivesTab, "Capture the Flag", 1) UI.AutoCTFBtn = createButton(ObjectivesTab, "Auto CTF: OFF", Color3.fromRGB(170, 50, 57), 2) UI.CTFModeBtn = createButton(ObjectivesTab, "CTF Mode: Auto", Color3.fromRGB(55, 55, 72), 3) UI.SetCTFCapturePointBtn = createButton(ObjectivesTab, "Set My Capture Point", Color3.fromRGB(60, 90, 167), 4) UI.CTFRescanBtn = createButton(ObjectivesTab, "Rescan Flags & Re-detect Mode", Color3.fromRGB(60, 90, 167), 5) UI.CTFChaseBtn = createButton(ObjectivesTab, "Chase Flag Carrier: OFF", Color3.fromRGB(170, 50, 57), 6) UI.CTFStatusLabel = Instance.new("TextLabel") UI.CTFStatusLabel.Size = UDim2.new(0.96, 0, 0, 46) UI.CTFStatusLabel.AutomaticSize = Enum.AutomaticSize.Y UI.CTFStatusLabel.BackgroundTransparency = 1 UI.CTFStatusLabel.TextColor3 = Color3.fromRGB(210, 215, 225) UI.CTFStatusLabel.Font = Enum.Font.SourceSans UI.CTFStatusLabel.TextSize = 15 UI.CTFStatusLabel.TextWrapped = true UI.CTFStatusLabel.TextXAlignment = Enum.TextXAlignment.Left UI.CTFStatusLabel.Text = "Finds the live enemy-colored or neutral flag wherever it spawns. Delivery uses your friendly flag's recorded starting position. Stand on the exact scoring spot and press Set My Capture Point to save it. Choose CTF Mode manually if automatic detection is incorrect." UI.CTFStatusLabel.LayoutOrder = 9 UI.CTFStatusLabel.Parent = ObjectivesTab UI.Inputs.CTF_PICKUP_ATTEMPTS = createInput(ObjectivesTab, "Pickup Teleports (1 or 2)", tostring(Config.CTF_PICKUP_ATTEMPTS), 7, function(value) Config.CTF_PICKUP_ATTEMPTS = math.clamp(math.floor(value), 1, 2) end) UI.Inputs.CTF_CAPTURE_WAIT = createInput(ObjectivesTab, "Wait After Flag Delivery (Seconds)", tostring(Config.CTF_CAPTURE_WAIT), 8, function(value) Config.CTF_CAPTURE_WAIT = math.clamp(value, 0, 15) end) createSectionHeader(ObjectivesTab, "Capture Limit & Flag Holding", 10) UI.Inputs.CTF_CAPTURE_LIMIT = createInput(ObjectivesTab, "Flags To Capture (0 = Unlimited)", tostring(Config.CTF_CAPTURE_LIMIT), 11, function(value) Config.CTF_CAPTURE_LIMIT = math.clamp(math.floor(value), 0, 1000000) UI.Inputs.CTF_CAPTURE_LIMIT.Text = tostring(Config.CTF_CAPTURE_LIMIT) FlagCapture.refreshProgress() end) UI.CTFLockAtLimitBtn = createButton(ObjectivesTab, "Hold Flag After Capture Limit: OFF", Color3.fromRGB(170, 50, 57), 12) UI.CTFHogFlagBtn = createButton(ObjectivesTab, "Hold Flag / No Capture: OFF", Color3.fromRGB(170, 50, 57), 13) UI.CTFCapturedLabel = createInfoLabel(ObjectivesTab, 14, "Captured this run: 0 / Unlimited") UI.CTFResetCountBtn = createButton(ObjectivesTab, "Reset Capture Count", Color3.fromRGB(60, 90, 167), 15) UI.Inputs.CTF_LOCK_HOLD_DISTANCE = createInput(ObjectivesTab, "Limit Hold Distance From Capture (Studs)", tostring(Config.CTF_LOCK_HOLD_DISTANCE), 16, function(value) Config.CTF_LOCK_HOLD_DISTANCE = math.clamp(value, 30, 500) end) UI.Inputs.CTF_HOLD_REACQUIRE_SECONDS = createInput(ObjectivesTab, "Hold Reacquire Time (Seconds)", tostring(Config.CTF_HOLD_REACQUIRE_SECONDS), 17, function(value) Config.CTF_HOLD_REACQUIRE_SECONDS = math.clamp(value, 180, 600) end) UI.Inputs.CTF_SCORE_CONFIRM_WAIT = createInput(ObjectivesTab, "Max Wait For Capture Confirmation (Seconds)", tostring(Config.CTF_SCORE_CONFIRM_WAIT), 18, function(value) Config.CTF_SCORE_CONFIRM_WAIT = math.clamp(value, 0.25, 8) end) UI.CTFRescanCounterBtn = createButton(ObjectivesTab, "Rescan Capture Counter", Color3.fromRGB(60, 90, 160), 19) UI.CTFCounterStatus = createInfoLabel(ObjectivesTab, 20, "Capture confirmation uses the game's chat messages and top-bar team score.") createInfoLabel(ObjectivesTab, 21, "The counter uses capture messages and your team's score increase after returning the flag. Duplicate chat and score signals count once; failed pickups do not count. Starting Auto CTF or changing teams starts a new count. Hold Flag runs separately from Auto CTF. It takes the flag, returns to your position, and leaves you free to move for 183 seconds before trying again. Hold After Capture Limit uses the same timer and returns away from the scoring point by at least Limit Hold Distance.") createSectionHeader(ObjectivesTab, "Fort Capture", 22) UI.AutoFortBtn = createButton(ObjectivesTab, "Auto Fort (Hold Flags): OFF", Color3.fromRGB(170, 50, 57), 23) createSectionHeader(ObjectivesTab, "Gold Farming", 24) UI.FarmBtn = createButton(ObjectivesTab, "Instant Chest Farm", Color3.fromRGB(170, 50, 57), 25) UI.TreasureHitboxBtn = createButton(ObjectivesTab, "Expand Gold Hitbox: OFF", Color3.fromRGB(170, 50, 57), 26) UI.Inputs.TREASURE_HITBOX_SIZE = createInput(ObjectivesTab, "Gold Hitbox Size", tostring(Config.TREASURE_HITBOX_SIZE), 27, function(val) Config.TREASURE_HITBOX_SIZE = val end) UI.Inputs.CHEST_PICKUP_WAIT = createInput(ObjectivesTab, "Initial Chest Pickup Wait (Seconds)", tostring(Config.CHEST_PICKUP_WAIT), 28, function(value) Config.CHEST_PICKUP_WAIT = math.clamp(value, 0.05, 3) end) UI.Inputs.CHEST_RETRY_EXTRA_WAIT = createInput(ObjectivesTab, "Extra Wait Per Failed Chest Pickup", tostring(Config.CHEST_RETRY_EXTRA_WAIT), 29, function(value) Config.CHEST_RETRY_EXTRA_WAIT = math.clamp(value, 0.05, 2) end) UI.Inputs.CHEST_MAX_PICKUP_WAIT = createInput(ObjectivesTab, "Maximum Chest Pickup Wait (Seconds)", tostring(Config.CHEST_MAX_PICKUP_WAIT), 30, function(value) Config.CHEST_MAX_PICKUP_WAIT = math.clamp(value, Config.CHEST_PICKUP_WAIT, 10) end) UI.Inputs.CHEST_FAILURE_LIMIT = createInput(ObjectivesTab, "Failed Pickups Before Ignoring Chest", tostring(Config.CHEST_FAILURE_LIMIT), 31, function(value) Config.CHEST_FAILURE_LIMIT = math.clamp(math.floor(value), 1, 20) end) UI.Inputs.CHEST_MIN_GOLD_GAIN = createInput(ObjectivesTab, "Minimum Treasure Gain to Confirm Pickup", tostring(Config.CHEST_MIN_GOLD_GAIN), 32, function(value) Config.CHEST_MIN_GOLD_GAIN = math.clamp(value, 1, 1000) end) UI.Inputs.CHEST_GOLD_CONFIRM_WAIT = createInput(ObjectivesTab, "Treasure Update Wait (Seconds)", tostring(Config.CHEST_GOLD_CONFIRM_WAIT), 33, function(value) Config.CHEST_GOLD_CONFIRM_WAIT = math.clamp(value, 0.1, 5) end) UI.RetryIgnoredChestsBtn = createButton(ObjectivesTab, "Retry Ignored Chests", Color3.fromRGB(60, 90, 160), 34) UI.IgnoredChestsStatus = createInfoLabel(ObjectivesTab, 35, "Ignored chest locations: 0. History is kept for this session, including streamed-out chests.") ChestFarm.setIgnoredStatus = function(count, worst) if not Runtime.closed then local message = "Ignored chest locations: " .. tostring(count) .. "." if worst then message = message .. string.format(" Highest failure count: chest #%d, %d/%d.", worst.id, worst.failures, Config.CHEST_FAILURE_LIMIT) end UI.IgnoredChestsStatus.Text = message end end UI.ChestGoldStatus = createInfoLabel(ObjectivesTab, 36, "Waiting to read Treasure from your player-list stats.") ChestFarm.setGoldStatus = function(balance) if not Runtime.closed then if balance ~= nil then UI.ChestGoldStatus.Text = string.format("Treasure balance: %.0f. Pickup needs at least +%.0f and the chest to disappear or report collected.", balance, Config.CHEST_MIN_GOLD_GAIN) else UI.ChestGoldStatus.Text = "Treasure stat unavailable. Pickups cannot be confirmed; failure history will be kept." end end end UI.ChestFarmStatus = createInfoLabel(ObjectivesTab, 37, "Retries use movement and longer visits. A pickup is confirmed by a Treasure increase (normally about 30) plus the chest disappearing or reporting collected. Streaming never resets failures. Retry Ignored Chests clears ignored locations.") ChestFarm.setStatus = function(message) if not Runtime.closed then UI.ChestFarmStatus.Text = message end end createSectionHeader(ObjectivesTab, "Map Trigger Activators", 38) UI.MapFireBtn = createButton(ObjectivesTab, "Auto-Fire Map Activators: OFF", Color3.fromRGB(170, 50, 57), 39) UI.Inputs.MAP_ACTIVATOR_RANGE = createInput(ObjectivesTab, "Activator Range (Studs)", tostring(Config.MAP_ACTIVATOR_RANGE), 40, function(val) Config.MAP_ACTIVATOR_RANGE = val end) UI.MapFireVisBtn = createButton(ObjectivesTab, "Visualize Activator Range: OFF", Color3.fromRGB(170, 50, 57), 41) createInfoLabel(ObjectivesTab, 42, "Map activators share the cannon click queue. Adjust Clicks Per Second in Cannons to change the rate for both.", Color3.fromRGB(190, 200, 215)) -- VISUALS TAB createSectionHeader(VisualsTab, "Player ESP", 1) UI.PlayerESPBtn = createButton(VisualsTab, "Enemy Player ESP: OFF", Color3.fromRGB(170, 50, 50), 2) UI.AllyPlayerESPBtn = createButton(VisualsTab, "Ally Player Highlight: OFF", Color3.fromRGB(170, 50, 50), 3) createSectionHeader(VisualsTab, "Ship ESP", 4) UI.ESPBtn = createButton(VisualsTab, "Enemy Ship ESP: OFF", Color3.fromRGB(170, 50, 50), 5) UI.AllyESPBtn = createButton(VisualsTab, "Ally Ship Highlight: OFF", Color3.fromRGB(170, 50, 50), 6) createSectionHeader(VisualsTab, "Environment & Camera", 7) UI.FullbrightBtn = createButton(VisualsTab, "Fullbright / Clear Fog: OFF", Color3.fromRGB(170, 50, 50), 8) UI.ZoomUncapBtn = createButton(VisualsTab, "Uncap Camera Zoom: OFF", Color3.fromRGB(170, 50, 50), 9) createSectionHeader(VisualsTab, "Freecam Navigation", 10) UI.FreecamBtn = createButton(VisualsTab, "Freecam: OFF", Color3.fromRGB(170, 50, 50), 11) UI.Inputs.FREECAM_SPEED = createInput(VisualsTab, "Freecam Speed", tostring(Config.FREECAM_SPEED), 12, function(val) Config.FREECAM_SPEED = val end) UI.FreecamTPBtn = createButton(VisualsTab, "TP to Cam & Exit", Color3.fromRGB(60, 90, 160), 13) createSectionHeader(VisualsTab, "HUD & Chat", 14) UI.ShipHUDBtn = createButton(VisualsTab, "Ship Status HUD: OFF", Color3.fromRGB(170, 50, 50), 15) UI.ShipHUDResetBtn = createButton(VisualsTab, "Reset HUD Position", Color3.fromRGB(60, 90, 160), 16) UI.Inputs.SHIP_HUD_TRANSPARENCY = createInput(VisualsTab, "HUD Transparency (0-0.95)", tostring(Config.SHIP_HUD_TRANSPARENCY), 17, function(val) Config.SHIP_HUD_TRANSPARENCY = math.clamp(val, 0, 0.95) ShipHUD.applyTransparency() end) createInfoLabel(VisualsTab, 18, "Bar shows: ship, cannon count (left/right/extra), reload state, left/right/all/broadside auto-fire state, hull parts remaining vs lost, and speed. Drag the bar to move it; it sits below the game's own match bar by default.\n" .. "Reload reads a real Loaded/CanFire/Cooldown value off the cannon when the ship exposes one, and shows 'Reload n/a' when it does not. There is deliberately no estimated countdown: every ship has its own cannon timing, so a single guessed number was wrong more often than right.", Color3.fromRGB(190, 200, 215)) UI.CustomChatBtn = createButton(VisualsTab, "Custom Chat Display: OFF", Color3.fromRGB(170, 50, 50), 19) -- SHOP TAB local function buyShopItem(itemName) pcall(function() local ShopRE = ReplicatedStorage:FindFirstChild("Shop") if ShopRE and ShopRE:IsA("RemoteEvent") then ShopRE:FireServer(itemName) end end) end createSectionHeader(ShopTab, "Utility Items", 1) UI.BuySpikeCleanerBtn = createButton(ShopTab, "Buy Spike Cleaner (25 Gold)", Color3.fromRGB(0, 120, 0), 2) UI.BuySpyglassBtn = createButton(ShopTab, "Buy Spyglass (100 Gold)", Color3.fromRGB(0, 120, 0), 3) UI.BuyPirateJuiceBtn = createButton(ShopTab, "Buy Pirate Juice (150 Gold)", Color3.fromRGB(0, 120, 0), 4) createSectionHeader(ShopTab, "Weapons & Firearms", 5) UI.BuyBowBtn = createButton(ShopTab, "Buy Bow (200 Gold)", Color3.fromRGB(0, 120, 0), 6) UI.BuyCutlassBtn = createButton(ShopTab, "Buy Cutlass (215 Gold)", Color3.fromRGB(0, 120, 0), 7) UI.BuyDualFlintlockBtn = createButton(ShopTab, "Buy Dual Flintlock (235 Gold)", Color3.fromRGB(0, 120, 0), 8) UI.BuyBlunderbussBtn = createButton(ShopTab, "Buy Blunderbuss (250 Gold)", Color3.fromRGB(0, 120, 0), 9) UI.BuyMusketBtn = createButton(ShopTab, "Buy Musket (300 Gold)", Color3.fromRGB(0, 120, 0), 10) UI.BuyTimeBombBtn = createButton(ShopTab, "Buy Time Bomb (600 Gold)", Color3.fromRGB(0, 120, 0), 11) UI.BuyRocketBtn = createButton(ShopTab, "Buy Rocket Launcher (800 Gold)", Color3.fromRGB(0, 120, 0), 12) Keybindings.connectButton("BuySpikeCleanerBtn", UI.BuySpikeCleanerBtn, function() buyShopItem("SpikeCleaner") end) Keybindings.connectButton("BuySpyglassBtn", UI.BuySpyglassBtn, function() buyShopItem("Spyglass") end) Keybindings.connectButton("BuyPirateJuiceBtn", UI.BuyPirateJuiceBtn, function() buyShopItem("PirateJuice") end) Keybindings.connectButton("BuyBowBtn", UI.BuyBowBtn, function() buyShopItem("Bow") end) Keybindings.connectButton("BuyCutlassBtn", UI.BuyCutlassBtn, function() buyShopItem("Cutlass") end) Keybindings.connectButton("BuyDualFlintlockBtn", UI.BuyDualFlintlockBtn, function() buyShopItem("DualFlintlock") end) Keybindings.connectButton("BuyBlunderbussBtn", UI.BuyBlunderbussBtn, function() buyShopItem("Blunderbuss") end) Keybindings.connectButton("BuyMusketBtn", UI.BuyMusketBtn, function() buyShopItem("Musket") end) Keybindings.connectButton("BuyTimeBombBtn", UI.BuyTimeBombBtn, function() buyShopItem("ClassicTimebomb") end) Keybindings.connectButton("BuyRocketBtn", UI.BuyRocketBtn, function() buyShopItem("RocketLauncher") end) --// EXCLUSIVE SHIP SPAWNS -- These go through ReplicatedStorage.RequestExclusiveSpawn. The server still -- checks the gold cost and whether the achievement is actually owned, so this is -- only a shortcut to the request - it does not grant a ship you have not earned -- and cannot pay for. A refused request simply does nothing. local EXCLUSIVE_SHIPS = { {key = "ColaCrate", label = "Cola Crate", cost = 5, achievement = "Cola Bottle"}, {key = "BananaBoat", label = "Banana Boat", cost = 45, achievement = "Banana Island"}, {key = "RetroBoat", label = "Retro Boat", cost = 65, achievement = "Pudinzo"}, {key = "SandShip", label = "Sand Ship", cost = 265, achievement = "Sand Castle"}, {key = "LavaShip", label = "Lava Ship", cost = 335, achievement = "Lava Spike"}, } local function requestExclusiveSpawn(shipKey, shipLabel) local remote = nil pcall(function() local found = ReplicatedStorage:FindFirstChild("RequestExclusiveSpawn") if not found then found = ReplicatedStorage:FindFirstChild("RequestExclusiveSpawn", true) end if found and found:IsA("RemoteEvent") then remote = found end end) if not remote then Toast.push("Exclusive Spawn", "RequestExclusiveSpawn remote not found", Toast.OFF, 4) notifyUser("Exclusive Spawn", "ReplicatedStorage.RequestExclusiveSpawn was not found in this place.", 6) return false end local sent = pcall(function() remote:FireServer("spawn", shipKey) end) if sent then Toast.push("Spawn requested: " .. shipLabel, "Server decides if you can afford/own it", Toast.INFO, 3) else Toast.push("Exclusive Spawn", "Request failed for " .. shipLabel, Toast.OFF, 4) end return sent end function ColaDelivery.setStatus(message) if not Runtime.closed and UI.ColaDeliveryStatus then UI.ColaDeliveryStatus.Text = message end end function ColaDelivery.cancel() ColaDelivery.cancelled = true end function ColaDelivery.canContinue(character) local humanoid = character and character:FindFirstChildOfClass("Humanoid") return ColaDelivery.active and not ColaDelivery.cancelled and not Runtime.closed and player.Character == character and humanoid and humanoid.Health > 0 end function ColaDelivery.leaveSeat(humanoid) for attempt = 1, 15 do humanoid.Sit = false humanoid.Jump = true if not humanoid.SeatPart then return true end task.wait(0.05) end return humanoid.SeatPart == nil end function ColaDelivery.isNewCrate(ship, previousShips) if previousShips[ship] or not ship:IsA("Model") or ship.Parent ~= ShipsFolder then return false end local name = string.lower(ship.Name) return string.find(name, "cola", 1, true) ~= nil and string.find(name, "crate", 1, true) ~= nil end function ColaDelivery.findNewCrate(previousShips, humanoid) local seatedShip = Teleport.shipFromPart(humanoid.SeatPart) if seatedShip and ColaDelivery.isNewCrate(seatedShip, previousShips) then return seatedShip end local matches = {} for _, ship in ipairs(ShipsFolder:GetChildren()) do if ColaDelivery.isNewCrate(ship, previousShips) then local owner = ship:GetAttribute("OwnerUserId") local seat = ShipBring.captainSeat(ship) if owner == player.UserId then return ship end if owner == nil and seat and (not seat.Occupant or seat.Occupant == humanoid) then table.insert(matches, ship) end end end if #matches == 1 then return matches[1] end return nil end function ColaDelivery.deliverOne(destination, label, character) local humanoid = character:FindFirstChildOfClass("Humanoid") local rootPart = character:FindFirstChild("HumanoidRootPart") if not ColaDelivery.leaveSeat(humanoid) or not ColaDelivery.canContinue(character) then return false, "Could not leave the current seat, or delivery was cancelled." end local previousShips = {} for _, ship in ipairs(ShipsFolder:GetChildren()) do previousShips[ship] = true end ColaDelivery.setStatus("Requesting a new Cola Crate for " .. label .. ".") if not requestExclusiveSpawn("ColaCrate", "Cola Crate") then return false, "The Cola Crate spawn request failed." end local deadline = os.clock() + 8 local crate = nil while ColaDelivery.canContinue(character) and os.clock() < deadline do crate = ColaDelivery.findNewCrate(previousShips, humanoid) if crate then break end task.wait(0.1) end if not crate then return false, "No new Cola Crate was confirmed. Check the Cola Bottle unlock and available gold." end ColaDelivery.crate = crate local seat = ShipBring.captainSeat(crate) if not seat then return false, "The new Cola Crate has no available captain seat." end ColaDelivery.seat = seat deadline = os.clock() + 3 while ColaDelivery.canContinue(character) and os.clock() < deadline do if not crate.Parent or not seat.Parent or (seat.Occupant and seat.Occupant ~= humanoid) then return false, "The new Cola Crate disappeared or another player took its seat." end if humanoid.SeatPart == seat and seat.Occupant == humanoid then break end rootPart.CFrame = seat.CFrame * CFrame.new(0, 1.5, 0) pcall(function() seat:Sit(humanoid) end) touchSeat(rootPart, seat) task.wait(0.1) end if not ColaDelivery.canContinue(character) then return false, "Cola delivery cancelled." end if humanoid.SeatPart ~= seat or seat.Occupant ~= humanoid then return false, "Could not captain the new Cola Crate." end task.wait(0.25) if not ColaDelivery.canContinue(character) then return false, "Cola delivery cancelled." end local moved, result = pcall(Teleport.tryBringShip, destination, character, rootPart, humanoid) if not moved or not result then return false, "Could not move this crate while captaining it alone." end if not ColaDelivery.leaveSeat(humanoid) then return false, "The crate arrived, but the captain seat did not release you." end task.wait(0.6) if not crate.Parent or not seat.Parent or ShipBring.horizontalDistance(seat.Position, destination.Position) > 30 or math.abs(seat.Position.Y - destination.Position.Y) > 40 then return false, "The server did not keep the Cola Crate at " .. label .. "." end table.insert(ColaDelivery.delivered, crate) ColaDelivery.crate = nil ColaDelivery.seat = nil ColaDelivery.setStatus("Cola Crate left at " .. label .. ".") return true end function ColaDelivery.start() if ColaDelivery.active then ColaDelivery.cancel() ColaDelivery.setStatus("Cancelling cola delivery and leaving the current crate.") return end local blueSpawn = getTeamSpawnLocation("blue") local redSpawn = getTeamSpawnLocation("red") local character = player.Character local rootPart = character and character:FindFirstChild("HumanoidRootPart") local humanoid = character and character:FindFirstChildOfClass("Humanoid") if not ShipsFolder or not blueSpawn or not redSpawn or not rootPart or not humanoid or humanoid.Health <= 0 then ColaDelivery.setStatus("Both team spawns, the Ships folder, and your character must be available before delivery.") return end if rootPart.Anchored then ColaDelivery.setStatus("Exit freecam before delivering Cola Crates.") return end local originalCFrame = rootPart.CFrame ColaDelivery.active = true ColaDelivery.cancelled = false ColaDelivery.delivered = {} ShipBring.releaseShip() UI.BringColaToSpawnsBtn.Text = "Cancel Cola Crate Delivery" task.spawn(function() local message = "Cola delivery cancelled." local succeeded, failure = pcall(function() for _, destination in ipairs({{cframe = blueSpawn, label = "Blue Team Spawn"}, {cframe = redSpawn, label = "Red Team Spawn"}}) do if not ColaDelivery.canContinue(character) then return end local delivered, reason = ColaDelivery.deliverOne(destination.cframe, destination.label, character) if not delivered then message = reason return end end for _, crate in ipairs(ColaDelivery.delivered) do if not crate.Parent then message = "A delivered crate was removed by the server when the next crate spawned." return end end message = "Delivered one Cola Crate to each team spawn." end) if not succeeded then message = "Cola delivery stopped: " .. tostring(failure) elseif ColaDelivery.cancelled then message = "Cola delivery cancelled." end if player.Character == character and humanoid.Health > 0 then local detached = ColaDelivery.leaveSeat(humanoid) if detached and not Runtime.closed and rootPart.Parent then rootPart.CFrame = originalCFrame rootPart.AssemblyLinearVelocity = Vector3.zero rootPart.AssemblyAngularVelocity = Vector3.zero end end ColaDelivery.active = false ColaDelivery.crate = nil ColaDelivery.seat = nil if not Runtime.closed then UI.BringColaToSpawnsBtn.Text = "Bring Cola Crates to Both Spawns" ColaDelivery.setStatus(message) notifyUser("Cola Crate Delivery", message, 6) end end) end Keybindings.connectButton("BringColaToSpawnsBtn", UI.BringColaToSpawnsBtn, ColaDelivery.start) createSectionHeader(ShopTab, "Boat Achievements Unlocker", 13) UI.UnlockAchievementsBtn = createButton(ShopTab, "Unlock All Achievement Boats", Color3.fromRGB(0, 140, 70), 14) createSectionHeader(ShopTab, "Exclusive Ships (Achievement Locked)", 15) do local order = 16 for _, ship in ipairs(EXCLUSIVE_SHIPS) do local actionId = "Spawn" .. ship.key .. "Btn" local button = createButton(ShopTab, string.format("Spawn %s (%d Gold)", ship.label, ship.cost), Color3.fromRGB(0, 105, 140), order) UI[actionId] = button order = order + 1 createInfoLabel(ShopTab, order, string.format("Requires the %s achievement. Cost %d gold.", ship.achievement, ship.cost), Color3.fromRGB(165, 178, 196)) order = order + 1 Keybindings.labels[actionId] = "Spawn " .. ship.label Keybindings.connectButton(actionId, button, function() requestExclusiveSpawn(ship.key, ship.label) end) end createInfoLabel(ShopTab, order, "The server verifies gold and achievement ownership for every one of these, so a request for a ship you have not unlocked or cannot pay for is simply ignored.", Color3.fromRGB(190, 200, 215)) end -- SETTINGS TAB createSectionHeader(ConfigTab, "Staff Alerts & Panic", 1) UI.ModDetectorBtn = createButton(ConfigTab, "Mod/Staff Detector: ON", Color3.fromRGB(0, 170, 0), 2) UI.ModPanicToggleBtn = createButton(ConfigTab, "Auto Panic on Staff: OFF", Color3.fromRGB(170, 50, 50), 3) UI.ManualPanicBtn = createButton(ConfigTab, "Run Panic Now", Color3.fromRGB(190, 60, 40), 4) createSectionHeader(ConfigTab, "Configuration Management", 5) UI.SaveConfigBtn = createButton(ConfigTab, "Save Config Settings", Color3.fromRGB(60, 90, 160), 6) UI.LoadConfigBtn = createButton(ConfigTab, "Load Config Settings", Color3.fromRGB(60, 90, 160), 7) createSectionHeader(ConfigTab, "Notifications", 8) UI.KeybindToastBtn = createButton(ConfigTab, "Keybind Toast Popups: ON", Color3.fromRGB(0, 170, 0), 9) UI.ActionPingBtn = createButton(ConfigTab, "Action Ping Sound: ON", Color3.fromRGB(0, 170, 0), 10) createInfoLabel(ConfigTab, 11, "Keybind toasts appear at the bottom right whenever a feature is toggled with a key, showing the feature name, the key used and the new ON/OFF state. They stay visible while the menu is hidden.\n" .. "Action Ping Sound replays the game's own team-ping client signal locally for audio confirmation on every keybind and menu button. It is purely local: nothing reaches the server and no other player sees or hears a ping. It needs a firesignal implementation.", Color3.fromRGB(190, 200, 215)) createSectionHeader(ConfigTab, "Session", 12) UI.AntiAfkBtn = createButton(ConfigTab, "Anti-AFK: OFF", Color3.fromRGB(170, 50, 50), 13) UI.CloseBtn = createButton(ConfigTab, "Close Admin Tool", Color3.fromRGB(75, 75, 80), 14) LoopKillEnemies.version = 0 LoopKillEnemies.syntheticInputUntil = 0 LoopKillEnemies.lastTargetUserId = 0 LoopKillEnemies.nextAttackAt = 0 LoopKillEnemies.nextEquipAt = 0 LoopKillEnemies.equipAttempts = 0 function LoopKillEnemies.setStatus(message) if Runtime.closed then return end if State.isLoopKillTargetActive then UI.LoopKillTargetStatus.Text = message else UI.LoopKillEnemiesStatus.Text = message end end function LoopKillEnemies.isActive() return State.isLoopKillEnemiesActive or State.isLoopKillTargetActive end function LoopKillEnemies.releaseAttack() if LoopKillEnemies.attackActive and LoopKillEnemies.sword then pcall(function() LoopKillEnemies.sword:Deactivate() end) end LoopKillEnemies.attackActive = false end function LoopKillEnemies.restoreRotation() local humanoid = LoopKillEnemies.rotationHumanoid if humanoid and humanoid.Parent then pcall(function() humanoid.AutoRotate = LoopKillEnemies.originalAutoRotate end) end LoopKillEnemies.rotationHumanoid = nil end function LoopKillEnemies.clearTarget() LoopKillEnemies.target = nil LoopKillEnemies.targetCharacter = nil LoopKillEnemies.targetHumanoid = nil LoopKillEnemies.targetStarted = nil end function LoopKillEnemies.spawnIsUsable(spawn) if not spawn or not spawn.Parent or not spawn:IsA("SpawnLocation") or not spawn.Enabled then return false end if spawn.Neutral then return true end if player.Team and not player.Neutral then return spawn.TeamColor == player.Team.TeamColor end return false end function LoopKillEnemies.getSpawnCFrame() local record = LoopKillEnemies.spawnRecord if record and record.team == player.Team and record.neutral == player.Neutral and record.respawnLocation == player.RespawnLocation then if not record.part.Parent then -- Keep the home position when streaming removes a distant spawn. return record.cframe end if LoopKillEnemies.spawnIsUsable(record.part) then record.cframe = record.part.CFrame + Vector3.new(0, 5, 0) return record.cframe end end local chosenSpawn = nil local neutralSpawn = nil if LoopKillEnemies.spawnIsUsable(player.RespawnLocation) then chosenSpawn = player.RespawnLocation else for _, instance in ipairs(workspace:GetDescendants()) do if LoopKillEnemies.spawnIsUsable(instance) then if instance.Neutral then if not neutralSpawn then neutralSpawn = instance end else chosenSpawn = instance break end end end end if not chosenSpawn then chosenSpawn = neutralSpawn end if not chosenSpawn then LoopKillEnemies.spawnRecord = nil return nil end LoopKillEnemies.spawnRecord = { part = chosenSpawn, cframe = chosenSpawn.CFrame + Vector3.new(0, 5, 0), team = player.Team, neutral = player.Neutral, respawnLocation = player.RespawnLocation, } return LoopKillEnemies.spawnRecord.cframe end function LoopKillEnemies.targetFinished() if not LoopKillEnemies.targetCharacter then return false end if LoopKillEnemies.targetHumanoid and LoopKillEnemies.targetHumanoid.Health <= 0 then return true end local target = LoopKillEnemies.target if not target or target.Parent ~= Players or target.Character ~= LoopKillEnemies.targetCharacter then return true end return not LoopKillEnemies.targetCharacter.Parent end function LoopKillEnemies.reset() LoopKillEnemies.version = LoopKillEnemies.version + 1 LoopKillEnemies.releaseAttack() LoopKillEnemies.restoreRotation() LoopKillEnemies.clearTarget() LoopKillEnemies.returnPending = false LoopKillEnemies.resumeAt = 0 LoopKillEnemies.character = nil LoopKillEnemies.sword = nil LoopKillEnemies.nextAttackAt = 0 LoopKillEnemies.nextEquipAt = 0 LoopKillEnemies.equipAttempts = 0 end function LoopKillEnemies.setEnabled(active, mode) LoopKillEnemies.reset() State.isLoopKillEnemiesActive = active == true and mode ~= "Target" State.isLoopKillTargetActive = active == true and mode == "Target" UI.LoopKillTargetStatus.Text = "Stopped. Uses the selected player and slot 1 sword; returns to your spawn after each kill." UI.LoopKillEnemiesStatus.Text = "Stopped." if State.isLoopKillEnemiesActive then UI.LoopKillEnemiesBtn.Text = "Loop Kill All Enemies: ON" UI.LoopKillEnemiesBtn.BackgroundColor3 = Color3.fromRGB(0, 170, 0) LoopKillEnemies.setStatus("Selecting the sword in hotbar slot 1.") else UI.LoopKillEnemiesBtn.Text = "Loop Kill All Enemies: OFF" UI.LoopKillEnemiesBtn.BackgroundColor3 = Color3.fromRGB(170, 50, 50) LoopKillEnemies.setStatus("Stopped.") end if State.isLoopKillTargetActive then UI.LoopKillTargetBtn.Text = "Loop Kill Target: ON" UI.LoopKillTargetBtn.BackgroundColor3 = Color3.fromRGB(0, 170, 0) LoopKillEnemies.setStatus("Following the selected Fling target with the slot 1 sword.") else UI.LoopKillTargetBtn.Text = "Loop Kill Target: OFF" UI.LoopKillTargetBtn.BackgroundColor3 = Color3.fromRGB(170, 50, 50) end end function LoopKillEnemies.enemyCharacter(candidate) if candidate == player or not candidate or candidate.Parent ~= Players then return nil end if not State.isLoopKillTargetActive and player.Team and candidate.Team == player.Team and not player.Neutral and not candidate.Neutral then return nil end local character = candidate.Character if not character or not character.Parent or character:FindFirstChildOfClass("ForceField") then return nil end local rootPart = character:FindFirstChild("HumanoidRootPart") local humanoid = character:FindFirstChildOfClass("Humanoid") if not rootPart or not rootPart:IsA("BasePart") or not humanoid or humanoid.Health <= 0 then return nil end return character, rootPart end function LoopKillEnemies.chooseTarget() if State.isLoopKillTargetActive then return LoopKillEnemies.getSelectedPlayer() end local candidates = Players:GetPlayers() table.sort(candidates, function(first, second) return first.UserId < second.UserId end) local firstAvailable = nil for _, candidate in ipairs(candidates) do if LoopKillEnemies.enemyCharacter(candidate) then if not firstAvailable then firstAvailable = candidate end if candidate.UserId > LoopKillEnemies.lastTargetUserId then return candidate end end end return firstAvailable end function LoopKillEnemies.canContinue(character, version) if Runtime.closed or not LoopKillEnemies.isActive() or LoopKillEnemies.version ~= version then return false end if player.Character ~= character or not character.Parent then return false end if Keybindings.capturing or not Keybindings.windowFocused or UserInputService:GetFocusedTextBox() then return false end local humanoid = character:FindFirstChildOfClass("Humanoid") return humanoid ~= nil and humanoid.Health > 0 end function LoopKillEnemies.pressSlotOne() -- Backpack child order is not a reliable hotbar index after tools are moved. LoopKillEnemies.syntheticInputUntil = os.clock() + 0.5 Keybindings.downInputs.One = nil local serviceAvailable, inputService = pcall(function() return game:GetService("VirtualInputManager") end) if serviceAvailable then local pressed = pcall(function() inputService:SendKeyEvent(true, Enum.KeyCode.One, false, game) end) pcall(function() inputService:SendKeyEvent(false, Enum.KeyCode.One, false, game) end) if pressed then return true end end if type(keypress) == "function" and type(keyrelease) == "function" then local pressed = pcall(keypress, 0x31) pcall(keyrelease, 0x31) return pressed end return false end function LoopKillEnemies.findOnlySword(character, backpack) -- If simulated hotbar input is unavailable, accept one identifiable sword. local containers = {character} if backpack then table.insert(containers, backpack) end local foundSword = nil for _, container in ipairs(containers) do for _, item in ipairs(container:GetChildren()) do if item:IsA("Tool") then local name = string.lower(item.Name) local isSword = string.find(name, "sword", 1, true) or string.find(name, "cutlass", 1, true) or string.find(name, "saber", 1, true) or string.find(name, "sabre", 1, true) if isSword then if foundSword then return nil end foundSword = item end end end end return foundSword end function LoopKillEnemies.equipSword(character, humanoid, now) local backpack = player:FindFirstChildOfClass("Backpack") local sword = LoopKillEnemies.sword if sword and sword.Parent == character then return sword end if sword and backpack and sword.Parent == backpack then humanoid:EquipTool(sword) return nil end LoopKillEnemies.sword = nil if now < LoopKillEnemies.nextEquipAt then return nil end for _, keyName in ipairs({"One", "LeftControl", "RightControl", "LeftShift", "RightShift", "LeftAlt", "RightAlt"}) do if UserInputService:IsKeyDown(Enum.KeyCode[keyName]) then LoopKillEnemies.setStatus("Release the hotkey to select sword slot 1.") return nil end end local version = LoopKillEnemies.version LoopKillEnemies.setStatus("Equipping hotbar slot 1...") humanoid:UnequipTools() task.wait(0.06) if not LoopKillEnemies.canContinue(character, version) then return nil end LoopKillEnemies.pressSlotOne() task.wait(0.2) if not LoopKillEnemies.canContinue(character, version) then return nil end sword = character:FindFirstChildOfClass("Tool") if not sword then sword = LoopKillEnemies.findOnlySword(character, backpack) if sword then humanoid:EquipTool(sword) end end if sword then LoopKillEnemies.sword = sword LoopKillEnemies.equipAttempts = 0 LoopKillEnemies.nextAttackAt = os.clock() + 0.1 return nil end LoopKillEnemies.equipAttempts = LoopKillEnemies.equipAttempts + 1 LoopKillEnemies.nextEquipAt = os.clock() + 1 if LoopKillEnemies.equipAttempts >= 3 then LoopKillEnemies.setEnabled(false) local message = "Could not select hotbar slot 1. Check that its sword is available and hotbar input is supported." LoopKillEnemies.setStatus(message) notifyUser("Loop Kill All Enemies", message, 7) end return nil end function LoopKillEnemies.pauseReason() if ColaDelivery.active then return "Waiting for cola delivery to finish." end if Keybindings.capturing or not Keybindings.windowFocused or UserInputService:GetFocusedTextBox() then return "Paused while typing, binding keys, or outside the game window." end for _, entry in ipairs({ {"isFreecamActive", "Freecam"}, {"isPlayerFlyActive", "Player Fly"}, {"isAutoCTFActive", "Auto CTF"}, {"isAutoFortActive", "Auto Fort"}, {"isFlingAttachActive", "Attach to Target"}, {"isFlingAuraActive", "Fling Aura"}, {"isBringShipsActive", "Bring Open Ships"}, }) do if State[entry[1]] then return "Paused while " .. entry[2] .. " controls your position." end end return nil end function LoopKillEnemies.step(now) if LoopKillEnemies.attackActive and now >= LoopKillEnemies.releaseAt then LoopKillEnemies.releaseAttack() end if Runtime.closed or not LoopKillEnemies.isActive() then if LoopKillEnemies.character then LoopKillEnemies.reset() end return end if LoopKillEnemies.targetFinished() then LoopKillEnemies.releaseAttack() LoopKillEnemies.restoreRotation() LoopKillEnemies.clearTarget() LoopKillEnemies.returnPending = true end local reason = LoopKillEnemies.pauseReason() if reason then LoopKillEnemies.releaseAttack() LoopKillEnemies.restoreRotation() LoopKillEnemies.setStatus(reason) return end local character = player.Character if not character or not character.Parent then LoopKillEnemies.reset() LoopKillEnemies.setStatus("Waiting for your character to respawn.") return end local rootPart = character:FindFirstChild("HumanoidRootPart") local humanoid = character:FindFirstChildOfClass("Humanoid") if not rootPart or not humanoid or humanoid.Health <= 0 then LoopKillEnemies.reset() LoopKillEnemies.setStatus("Waiting for your character to respawn.") return end if LoopKillEnemies.character ~= character then LoopKillEnemies.reset() LoopKillEnemies.character = character end if humanoid.SeatPart or humanoid.Sit then LoopKillEnemies.releaseAttack() humanoid.Sit = false LoopKillEnemies.setStatus("Leaving the seat before following enemies.") return end if rootPart.Anchored then LoopKillEnemies.releaseAttack() LoopKillEnemies.setStatus("Waiting for your character to be movable.") return end local spawnCFrame = LoopKillEnemies.getSpawnCFrame() if not spawnCFrame then LoopKillEnemies.setEnabled(false) local message = "Stopped: your team spawn or a neutral spawn could not be found. Enable the loop near your spawn so its return position can be saved." LoopKillEnemies.setStatus(message) notifyUser("Sword Loop", message, 7) return end if LoopKillEnemies.returnPending then rootPart.CFrame = spawnCFrame rootPart.AssemblyLinearVelocity = Vector3.zero rootPart.AssemblyAngularVelocity = Vector3.zero LoopKillEnemies.returnPending = false LoopKillEnemies.resumeAt = now + math.clamp(Config.LOOP_KILL_SPAWN_WAIT, 0.1, 10) LoopKillEnemies.setStatus("Returned to your spawn. Waiting before the next target.") return end if now < LoopKillEnemies.resumeAt then LoopKillEnemies.setStatus("Waiting at your spawn before the next target.") return end local sword = LoopKillEnemies.equipSword(character, humanoid, now) if not sword then return end local targetCharacter, targetRoot = LoopKillEnemies.enemyCharacter(LoopKillEnemies.target) local targetSeconds = math.clamp(Config.LOOP_KILL_TARGET_SECONDS, 1, 30) local changeTarget = false if State.isLoopKillTargetActive then changeTarget = LoopKillEnemies.getSelectedPlayer() ~= LoopKillEnemies.target elseif LoopKillEnemies.targetStarted then changeTarget = now - LoopKillEnemies.targetStarted >= targetSeconds end if not targetCharacter or targetCharacter ~= LoopKillEnemies.targetCharacter or changeTarget then LoopKillEnemies.releaseAttack() LoopKillEnemies.clearTarget() LoopKillEnemies.target = LoopKillEnemies.chooseTarget() targetCharacter, targetRoot = LoopKillEnemies.enemyCharacter(LoopKillEnemies.target) if not targetCharacter then LoopKillEnemies.restoreRotation() if State.isLoopKillTargetActive then LoopKillEnemies.setStatus("Select a player above. Waiting for that player to be alive and out of spawn protection.") else LoopKillEnemies.setStatus("Waiting for a living enemy without spawn protection.") end return end LoopKillEnemies.targetCharacter = targetCharacter LoopKillEnemies.targetHumanoid = targetCharacter:FindFirstChildOfClass("Humanoid") LoopKillEnemies.targetStarted = now LoopKillEnemies.lastTargetUserId = LoopKillEnemies.target.UserId end if not LoopKillEnemies.rotationHumanoid then LoopKillEnemies.rotationHumanoid = humanoid LoopKillEnemies.originalAutoRotate = humanoid.AutoRotate humanoid.AutoRotate = false end local forward = targetRoot.CFrame.LookVector forward = Vector3.new(forward.X, 0, forward.Z) if forward.Magnitude < 0.01 then forward = Vector3.new(0, 0, -1) end local distance = math.clamp(Config.LOOP_KILL_BEHIND_DISTANCE, 1.5, 8) local behindPosition = targetRoot.Position - forward.Unit * distance rootPart.CFrame = CFrame.lookAt(behindPosition, targetRoot.Position) rootPart.AssemblyLinearVelocity = Vector3.zero rootPart.AssemblyAngularVelocity = Vector3.zero LoopKillEnemies.setStatus("Attacking " .. LoopKillEnemies.target.Name .. " with " .. sword.Name .. ".") if now >= LoopKillEnemies.nextAttackAt and sword.Enabled then local attacksPerSecond = math.clamp(Config.LOOP_KILL_ATTACKS_PER_SECOND, 1, 20) sword:Activate() LoopKillEnemies.attackActive = true LoopKillEnemies.releaseAt = now + 0.04 LoopKillEnemies.nextAttackAt = now + 1 / attacksPerSecond end end ShipBring.version = 0 ShipBring.queue = {} ShipBring.index = 0 ShipBring.nextPassAt = 0 ShipBring.enabled = false ShipBring.playSpaceCenter = Vector3.new(-1013.25, 0, 1049.49) ShipBring.waterHeight = findWaterHeight() ShipBring.teleportChecks = setmetatable({}, {__mode = "k"}) ShipBring.sailFallback = setmetatable({}, {__mode = "k"}) function ShipBring.setStatus(message) if not Runtime.closed and UI.BringShipsStatus then UI.BringShipsStatus.Text = message end end function ShipBring.releaseShip() if ShipBring.ship and ShipBring.index >= #ShipBring.queue then ShipBring.nextPassAt = os.clock() + Config.BRING_SHIPS_PASS_DELAY end local seat = ShipBring.seat local humanoid = ShipBring.humanoid if seat and seat.Parent and humanoid and seat.Occupant == humanoid then pcall(function() if seat:IsA("VehicleSeat") then seat.ThrottleFloat = 0 seat.SteerFloat = 0 end local assemblyRoot = seat.AssemblyRootPart if assemblyRoot then assemblyRoot.AssemblyLinearVelocity = Vector3.zero assemblyRoot.AssemblyAngularVelocity = Vector3.zero end end) end if seat and humanoid and humanoid.Parent then if humanoid.SeatPart == seat or seat.Occupant == humanoid then pcall(function() humanoid.Sit = false humanoid.Jump = true end) end end ShipBring.ship = nil ShipBring.seat = nil ShipBring.humanoid = nil ShipBring.phase = "FindShip" end function ShipBring.reset(returnPlayer) ShipBring.version = ShipBring.version + 1 local originalSeat = ShipBring.seat ShipBring.releaseShip() ShipBring.queue = {} ShipBring.index = 0 ShipBring.nextPassAt = 0 table.clear(ShipBring.teleportChecks) table.clear(ShipBring.sailFallback) local version = ShipBring.version local character = ShipBring.character local returnCFrame = ShipBring.returnCFrame if returnPlayer and character and returnCFrame then task.spawn(function() -- Wait for the seat weld to release before moving the character alone. for attempt = 1, 12 do if ShipBring.version ~= version or State.isBringShipsActive or player.Character ~= character then return end local humanoid = character:FindFirstChildOfClass("Humanoid") local rootPart = character:FindFirstChild("HumanoidRootPart") if not humanoid or not rootPart or humanoid.Health <= 0 then return end if humanoid.SeatPart and humanoid.SeatPart ~= originalSeat then return end if not humanoid.SeatPart and not humanoid.Sit then rootPart.CFrame = returnCFrame rootPart.AssemblyLinearVelocity = Vector3.zero rootPart.AssemblyAngularVelocity = Vector3.zero return end humanoid.Sit = false task.wait(0.05) end end) end end function ShipBring.setEnabled(active) local wasEnabled = ShipBring.enabled State.isBringShipsActive = active == true ShipBring.enabled = State.isBringShipsActive ShipBring.reset(wasEnabled and not State.isBringShipsActive) if State.isBringShipsActive then if not wasEnabled then ShipBring.character = nil ShipBring.returnCFrame = nil end UI.BringShipsBtn.Text = "Bring Open Ships: ON" UI.BringShipsBtn.BackgroundColor3 = Color3.fromRGB(0, 170, 0) ShipBring.setStatus("Finding open captain seats.") else UI.BringShipsBtn.Text = "Bring Open Ships: OFF" UI.BringShipsBtn.BackgroundColor3 = Color3.fromRGB(170, 50, 50) ShipBring.setStatus("Stopped.") end end function ShipBring.refreshUI() UI.BringShipsDestinationBtn.Text = "Bring To: " .. Config.BRING_SHIPS_DESTINATION UI.BringShipsModeBtn.Text = "Move Method: " .. Config.BRING_SHIPS_MODE UI.BringShipsPlayerInput.Text = Config.BRING_SHIPS_PLAYER UI.BringShipsPointLabel.Text = "No bring point saved." if ShipBring.point then local position = ShipBring.point.cframe.Position UI.BringShipsPointLabel.Text = string.format("Bring point: %.1f, %.1f, %.1f", position.X, position.Y, position.Z) end end function ShipBring.savePoint(useMouse) local character = player.Character local rootPart = character and character:FindFirstChild("HumanoidRootPart") if not rootPart then return end local pointCFrame = rootPart.CFrame if useMouse then if not mouse.Target then notifyUser("Bring Open Ships", "Point at a visible surface first.", 4) return end pointCFrame = CFrame.new(mouse.Hit.Position) end ShipBring.point = {placeId = game.PlaceId, cframe = pointCFrame} Config.BRING_SHIPS_DESTINATION = "Point" ShipBring.refreshUI() notifyUser("Bring Open Ships", "Bring point saved. Save Config Settings to keep it next session.", 5) end function ShipBring.destination() if Config.BRING_SHIPS_DESTINATION == "Point" then if ShipBring.point and ShipBring.point.placeId == game.PlaceId then return ShipBring.point.cframe.Position end return nil, "Set a bring point here or at the mouse first." end local query = string.lower(Config.BRING_SHIPS_PLAYER) query = string.match(query, "^%s*(.-)%s*$") if query == "" then return nil, "Enter a destination player's name." end if query == "me" or query == string.lower(player.Name) then if ShipBring.returnCFrame then return ShipBring.returnCFrame.Position end return nil, "Waiting for your starting position." end local match = nil for _, candidate in ipairs(Players:GetPlayers()) do if string.lower(candidate.Name) == query then match = candidate break end end if not match then for _, candidate in ipairs(Players:GetPlayers()) do if string.find(string.lower(candidate.Name), query, 1, true) or string.find(string.lower(candidate.DisplayName), query, 1, true) then if match then return nil, "More than one player matches. Enter their full username." end match = candidate end end end local character = match and match.Character local rootPart = character and character:FindFirstChild("HumanoidRootPart") local humanoid = character and character:FindFirstChildOfClass("Humanoid") if rootPart and humanoid and humanoid.Health > 0 then return rootPart.Position end return nil, "Waiting for the destination player to be available." end function ShipBring.pauseReason() if Keybindings.capturing or not Keybindings.windowFocused or UserInputService:GetFocusedTextBox() then return "Paused while typing, binding keys, or outside the game window." end for _, entry in ipairs({ {"isAntiSitActive", "Anti-Sit"}, {"isFreecamActive", "Freecam"}, {"isPlayerFlyActive", "Player Fly"}, {"isAutoCTFActive", "Auto CTF"}, {"isAutoFortActive", "Auto Fort"}, {"isFlingAttachActive", "Attach to Target"}, {"isFlingActive", "Ultimate Fling"}, {"isFlingAuraActive", "Fling Aura"}, }) do if State[entry[1]] then return "Turn off " .. entry[2] .. " before bringing ships." end end return nil end function ShipBring.horizontalDistance(first, second) return Vector3.new(first.X - second.X, 0, first.Z - second.Z).Magnitude end function ShipBring.isInPlaySpace(seat) local position = seat.Position for _, coordinate in ipairs({position.X, position.Y, position.Z}) do if coordinate ~= coordinate or math.abs(coordinate) == math.huge then return false end end if math.abs(position.Y - ShipBring.waterHeight) > Config.BRING_SHIPS_MAX_WATER_OFFSET then return false end -- Center Fort stays fixed even when broken ships fly far away. return ShipBring.horizontalDistance(position, ShipBring.playSpaceCenter) <= Config.BRING_SHIPS_MAX_DISTANCE end function ShipBring.hasArrived(seat, destination) return ShipBring.horizontalDistance(seat.Position, destination) <= Config.BRING_SHIPS_ARRIVAL_DISTANCE end function ShipBring.refreshTeleportChecks(now) for ship, record in pairs(ShipBring.teleportChecks) do if ship.Parent ~= ShipsFolder or not record.seat.Parent then ShipBring.teleportChecks[ship] = nil elseif now >= record.checkAt then if record.useAutoFallback and not ShipBring.hasArrived(record.seat, record.destination) then ShipBring.sailFallback[ship] = true end ShipBring.teleportChecks[ship] = nil end end end function ShipBring.captainSeat(ship) local seat = getMainDriverSeat(ship) local passengerSeats = {} if seat then return seat end -- Include smaller boats that the existing ship helper leaves out. for _, part in ipairs(ship:GetDescendants()) do if part:IsA("Seat") then table.insert(passengerSeats, part) end local name = string.lower(part.Name) local namedCaptainSeat = part:IsA("Seat") and (string.find(name, "captain", 1, true) or string.find(name, "pilot", 1, true) or string.find(name, "helm", 1, true) or string.find(name, "driver", 1, true)) if part:IsA("VehicleSeat") or namedCaptainSeat then if not seat or part.Position.Y > seat.Position.Y then seat = part end end end if not seat and #passengerSeats == 1 then return passengerSeats[1] end return seat end function ShipBring.selectShip(destination, now) if ShipBring.index >= #ShipBring.queue then if now < ShipBring.nextPassAt then return end ShipBring.queue = {} ShipBring.index = 0 if ShipsFolder then for _, ship in ipairs(ShipsFolder:GetChildren()) do if ship:IsA("Model") then table.insert(ShipBring.queue, ship) end end end ShipBring.nextPassAt = now + Config.BRING_SHIPS_PASS_DELAY end while ShipBring.index < #ShipBring.queue do ShipBring.index = ShipBring.index + 1 local ship = ShipBring.queue[ShipBring.index] if ship.Parent == ShipsFolder and not ShipBring.teleportChecks[ship] then local seat = ShipBring.captainSeat(ship) if seat and not seat.Occupant and not seat.Disabled and not seat.Anchored and ShipBring.isInPlaySpace(seat) then if not ShipBring.hasArrived(seat, destination) then ShipBring.ship = ship ShipBring.seat = seat ShipBring.phase = "Board" ShipBring.phaseStarted = now ShipBring.startedAt = now ShipBring.lastBoardAttempt = -math.huge return end end end end ShipBring.setStatus("Pass complete. Skipping delivered ships and ships outside the main play space.") end function ShipBring.step(now) if Runtime.closed or not State.isBringShipsActive or ColaDelivery.active then return end ShipBring.refreshTeleportChecks(now) local pauseReason = ShipBring.pauseReason() if pauseReason then ShipBring.releaseShip() ShipBring.setStatus(pauseReason) return end local character = player.Character local rootPart = character and character:FindFirstChild("HumanoidRootPart") local humanoid = character and character:FindFirstChildOfClass("Humanoid") if not rootPart or not humanoid or humanoid.Health <= 0 then ShipBring.releaseShip() ShipBring.setStatus("Waiting for your character to respawn.") return end if ShipBring.character ~= character then ShipBring.reset(false) ShipBring.character = character ShipBring.returnCFrame = rootPart.CFrame end if rootPart.Anchored then ShipBring.releaseShip() ShipBring.setStatus("Waiting for your character to be movable.") return end local destination, failure = ShipBring.destination() if not destination then ShipBring.releaseShip() ShipBring.setStatus(failure) return end if not ShipBring.ship then if humanoid.SeatPart or humanoid.Sit then humanoid.Sit = false humanoid.Jump = true return end ShipBring.selectShip(destination, now) return end local ship = ShipBring.ship local seat = ShipBring.seat if not ship.Parent or not seat.Parent or (seat.Occupant and seat.Occupant ~= humanoid) then ShipBring.releaseShip() ShipBring.setStatus("Skipped a ship whose captain seat is no longer available.") return end if not ShipBring.isInPlaySpace(seat) then ShipBring.releaseShip() ShipBring.setStatus("Skipped a ship in the void or outside the main play space.") return end if ShipBring.hasArrived(seat, destination) then ShipBring.releaseShip() ShipBring.setStatus("Ship is within Arrival Distance. Leaving the seat and checking the next open ship.") return end if now - ShipBring.startedAt >= Config.BRING_SHIPS_TIMEOUT then ShipBring.releaseShip() ShipBring.setStatus("Move timed out. Continuing with the next open ship.") return end if ShipBring.phase == "Board" then if seat.Occupant == humanoid and humanoid.SeatPart == seat then ShipBring.humanoid = humanoid ShipBring.phase = "Settle" ShipBring.phaseStarted = now elseif now - ShipBring.phaseStarted > 2 then ShipBring.releaseShip() ShipBring.setStatus("Could not occupy the captain seat. Trying the next ship.") elseif now - ShipBring.lastBoardAttempt >= 0.25 then ShipBring.lastBoardAttempt = now ShipBring.humanoid = humanoid rootPart.CFrame = seat.CFrame * CFrame.new(0, 1.5, 0) pcall(function() seat:Sit(humanoid) end) touchSeat(rootPart, seat) ShipBring.setStatus("Boarding " .. ship.Name .. ".") end return end if seat.Occupant ~= humanoid or humanoid.SeatPart ~= seat then ShipBring.releaseShip() return end local assemblyRoot = seat.AssemblyRootPart if not assemblyRoot or assemblyRoot.Anchored then ShipBring.releaseShip() ShipBring.setStatus("Ship is anchored or has no movable assembly. Skipping it.") return end local distance = ShipBring.horizontalDistance(seat.Position, destination) if ShipBring.phase == "Settle" then if now - ShipBring.phaseStarted < 0.3 then return end if Config.BRING_SHIPS_MODE == "Sail" or (Config.BRING_SHIPS_MODE == "Auto" and ShipBring.sailFallback[ship]) then ShipBring.phase = "Sail" else -- Keep the ship at its current water height and preserve its heading. local desiredSeat = CFrame.new(destination.X, seat.Position.Y, destination.Z) * seat.CFrame.Rotation ship:PivotTo(desiredSeat * seat.CFrame:Inverse() * ship:GetPivot()) assemblyRoot.AssemblyLinearVelocity = Vector3.zero assemblyRoot.AssemblyAngularVelocity = Vector3.zero if ShipBring.hasArrived(seat, destination) then -- Verify later so arriving does not keep the player in the seat. ShipBring.teleportChecks[ship] = { seat = seat, destination = destination, checkAt = now + 0.6, useAutoFallback = Config.BRING_SHIPS_MODE == "Auto", } ShipBring.releaseShip() ShipBring.setStatus("Ship arrived. Leaving the seat immediately and checking the next open ship.") else ShipBring.phase = "Verify" ShipBring.phaseStarted = now ShipBring.setStatus("Checking the teleport for " .. ship.Name .. ".") end end return end if ShipBring.phase == "Verify" then if now - ShipBring.phaseStarted < 0.6 then return end if Config.BRING_SHIPS_MODE == "Teleport" then ShipBring.releaseShip() ShipBring.setStatus("Teleport did not hold. Trying the next open ship.") return end ShipBring.phase = "Sail" end if ShipBring.phase == "Sail" then local direction = Vector3.new(destination.X - seat.Position.X, 0, destination.Z - seat.Position.Z).Unit local forward = seat.CFrame.LookVector local angle = math.atan2(forward.Z * direction.X - forward.X * direction.Z, forward.X * direction.X + forward.Z * direction.Z) local steer = math.clamp(-angle * 2, -1, 1) if Config.QUICK_TURN_INVERT_STEER then steer = -steer end if seat:IsA("VehicleSeat") then seat.ThrottleFloat = 1 seat.SteerFloat = steer end local speed = math.min(Config.BRING_SHIPS_SPEED, distance * 3) assemblyRoot.AssemblyLinearVelocity = direction * speed + Vector3.new(0, assemblyRoot.AssemblyLinearVelocity.Y, 0) assemblyRoot.AssemblyAngularVelocity = Vector3.new(0, math.clamp(angle * 3, -2, 2), 0) ShipBring.setStatus(string.format("Moving %s: %.0f studs remaining.", ship.Name, distance)) end end --// MAPPED TOGGLE BUTTONS FOR CONFIG UPDATES ToggleUI = { isAutoFireActive = { Btn = UI.AutoFireBtn, Text = "Broadside Auto-Fire" }, isSpammingLeft = { Btn = UI.LeftBtn, Text = "Fire Left Cannons" }, isSpammingRight = { Btn = UI.RightBtn, Text = "Fire Right Cannons" }, isSpammingAll = { Btn = UI.FireAllBtn, Text = "Fire ALL Cannons (Hold)" }, isFireAllBroad = { Btn = UI.FireAllBroadBtn, Text = "Include Non-Cannon Activators" }, isShipHUDActive = { Btn = UI.ShipHUDBtn, Text = "Ship Status HUD" }, isKeybindToastActive = { Btn = UI.KeybindToastBtn, Text = "Keybind Toast Popups" }, isActionPingActive = { Btn = UI.ActionPingBtn, Text = "Action Ping Sound" }, isCTFChaseCarrier = { Btn = UI.CTFChaseBtn, Text = "Chase Flag Carrier" }, isCTFHogFlagActive = { Btn = UI.CTFHogFlagBtn, Text = "Hold Flag / No Capture" }, isCTFLockAtLimitActive = { Btn = UI.CTFLockAtLimitBtn, Text = "Hold Flag After Capture Limit" }, isHitboxExpanderActive = { Btn = UI.HitboxToggleBtn, Text = "Enemy Hitbox Expander" }, isHitboxVisualsActive = { Btn = UI.HitboxVisualBtn, Text = "Visualize Hitbox" }, isKillAuraActive = { Btn = UI.KillAuraBtn, Text = "Melee Kill Aura" }, isLoopKillEnemiesActive = { Btn = UI.LoopKillEnemiesBtn, Text = "Loop Kill All Enemies" }, isLoopKillTargetActive = { Btn = UI.LoopKillTargetBtn, Text = "Loop Kill Target" }, isBringShipsActive = { Btn = UI.BringShipsBtn, Text = "Bring Open Ships" }, isReachActive = { Btn = UI.ReachBtn, Text = "Melee Range Extender" }, isAutoMapFireActive = { Btn = UI.MapFireBtn, Text = "Auto-Fire Map Activators" }, isMapFireVisualizerActive = { Btn = UI.MapFireVisBtn, Text = "Visualize Activator Range" }, isStealEnemyActive = { Btn = UI.StealEnemyBtn, Text = "Steal Enemy Ships" }, isStealFriendlyActive = { Btn = UI.StealFriendlyBtn, Text = "Steal Friendly Ships" }, isStealPaused = { Btn = UI.PauseStealBtn, Text = "Pause Steal" }, isSpeedBoosting = { Btn = UI.SpeedBtn, Text = "Ship Speed Boost" }, isFlying = { Btn = UI.FlyBtn, Text = "Ship Flight" }, isAntiSinkActive = { Btn = UI.AntiSinkBtn, Text = "Ship Anti-Sink" }, isShipNoClipActive = { Btn = UI.ShipNoClipBtn, Text = "Ship No-Clip" }, isESPActive = { Btn = UI.ESPBtn, Text = "Enemy Ship ESP" }, isAllyESPActive = { Btn = UI.AllyESPBtn, Text = "Ally Ship Highlight" }, isPlayerESPActive = { Btn = UI.PlayerESPBtn, Text = "Enemy Player ESP" }, isAllyPlayerESPActive = { Btn = UI.AllyPlayerESPBtn, Text = "Ally Player Highlight" }, isFullbrightActive = { Btn = UI.FullbrightBtn, Text = "Fullbright / Clear Fog" }, isModDetectorActive = { Btn = UI.ModDetectorBtn, Text = "Mod/Staff Detector" }, isModPanicActive = { Btn = UI.ModPanicToggleBtn, Text = "Auto Panic on Staff" }, isWaterWalkActive = { Btn = UI.WaterWalkBtn, Text = "Jesus Walk (Water)" }, isTreasureHitboxActive = { Btn = UI.TreasureHitboxBtn, Text = "Expand Gold Hitbox" }, isClickTPActive = { Btn = UI.ClickTPBtn, Text = "Mouse Teleport" }, isCharSpeedActive = { Btn = UI.CharSpeedBtn, Text = "Player WalkSpeed Override" }, isCharJumpActive = { Btn = UI.CharJumpBtn, Text = "Player JumpPower Override" }, isInfJumpActive = { Btn = UI.InfJumpBtn, Text = "Infinite Jump" }, isPlayerNoClipActive = { Btn = UI.PlayerNoClipBtn, Text = "Player No-Clip" }, isAntiRagdollActive = { Btn = UI.AntiRagdollBtn, Text = "Anti-Ragdoll / Stun" }, isAntiSitActive = { Btn = UI.AntiSitBtn, Text = "Anti-Sit / Anti-Seat" }, isAntiVoidActive = { Btn = UI.AntiVoidBtn, Text = "Anti-Void/Water Death" }, isAutoCTFActive = { Btn = UI.AutoCTFBtn, Text = "Auto CTF" }, isAutoFortActive = { Btn = UI.AutoFortBtn, Text = "Auto Fort (Hold Flags)" }, isPlayerFlyActive = { Btn = UI.PlayerFlyBtn, Text = "Player Fly" }, isFlingActive = { Btn = UI.FlingToggleBtn, Text = "Ultimate Fling" }, isFlingAttachActive = { Btn = UI.FlingAttachBtn, Text = "Attach to Target" }, isSpectating = { Btn = UI.SpectateBtn, Text = "Spectate Target" }, isAutoReturnActive = { Btn = UI.AutoReturnBtn, Text = "Auto-Return on Success" }, isFlingAuraActive = { Btn = UI.FlingAuraBtn, Text = "Fling Aura" }, isFlingAuraVisActive = { Btn = UI.FlingAuraVisBtn, Text = "Visualize Aura Range" }, isFlingAuraTeamActive = { Btn = UI.FlingAuraTeamBtn, Text = "Aura Includes Teammates" }, isCustomChatActive = { Btn = UI.CustomChatBtn, Text = "Custom Chat Display" }, isFreecamActive = { Btn = UI.FreecamBtn, Text = "Freecam" }, isZoomUncapActive = { Btn = UI.ZoomUncapBtn, Text = "Uncap Camera Zoom" }, isAutoHPReturnActive = { Btn = UI.AutoHPReturnBtn, Text = "Auto Return on Low HP" }, isAntiAfkActive = { Btn = UI.AntiAfkBtn, Text = "Anti-AFK" }, } --// FREECAM CONTROLLER IMPLEMENTATION local function setFreecamImpl(active) State.isFreecamActive = active UI.FreecamBtn.Text = "Freecam: " .. (State.isFreecamActive and "ON" or "OFF") UI.FreecamBtn.BackgroundColor3 = State.isFreecamActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) local cam = workspace.CurrentCamera local char = player.Character local hrp = char and char:FindFirstChild("HumanoidRootPart") local hum = char and char:FindFirstChildOfClass("Humanoid") if State.isFreecamActive then if State.isSpectating then State.isSpectating = false UI.SpectateBtn.Text = "Spectate Target: OFF" UI.SpectateBtn.BackgroundColor3 = Color3.fromRGB(170, 50, 50) end if cam then cam.CameraType = Enum.CameraType.Scriptable freecamCF = cam.CFrame local rx, ry, _ = cam.CFrame:ToOrientation() freecamRot = Vector2.new(rx, ry) end if hrp and hum and not hum.Sit then hrp.Anchored = true end else if cam then cam.CameraType = Enum.CameraType.Custom if hum then cam.CameraSubject = hum end end UserInputService.MouseBehavior = Enum.MouseBehavior.Default if hrp then hrp.Anchored = false end end end setFreecam = setFreecamImpl local function toggleFreecamImpl() setFreecam(not State.isFreecamActive) end toggleFreecam = toggleFreecamImpl local function teleportToFreecamImpl() if not State.isFreecamActive or not freecamCF then return end local targetCFrame = freecamCF setFreecam(false) teleportToLocation(targetCFrame) end teleportToFreecam = teleportToFreecamImpl --// DYNAMIC SIDE EFFECTS HANDLER local function ApplyStateSideEffects() Config.LOOP_KILL_ATTACKS_PER_SECOND = math.clamp(Config.LOOP_KILL_ATTACKS_PER_SECOND, 1, 20) Config.LOOP_KILL_BEHIND_DISTANCE = math.clamp(Config.LOOP_KILL_BEHIND_DISTANCE, 1.5, 8) Config.LOOP_KILL_TARGET_SECONDS = math.clamp(Config.LOOP_KILL_TARGET_SECONDS, 1, 30) Config.LOOP_KILL_SPAWN_WAIT = math.clamp(Config.LOOP_KILL_SPAWN_WAIT, 0.1, 10) for _, setting in ipairs({"LOOP_KILL_ATTACKS_PER_SECOND", "LOOP_KILL_BEHIND_DISTANCE", "LOOP_KILL_TARGET_SECONDS", "LOOP_KILL_SPAWN_WAIT"}) do UI.Inputs[setting].Text = tostring(Config[setting]) end if State.isLoopKillTargetActive then LoopKillEnemies.setEnabled(true, "Target") else LoopKillEnemies.setEnabled(State.isLoopKillEnemiesActive) end for _, setting in ipairs({ {"CTF_PICKUP_ATTEMPTS", 1, 2}, {"CTF_CAPTURE_WAIT", 0, 15}, {"CTF_SCORE_CONFIRM_WAIT", 0.25, 8}, {"CHEST_PICKUP_WAIT", 0.05, 3}, {"CHEST_RETRY_EXTRA_WAIT", 0.05, 2}, {"CHEST_MAX_PICKUP_WAIT", 0.05, 10}, {"CHEST_FAILURE_LIMIT", 1, 20}, {"CHEST_MIN_GOLD_GAIN", 1, 1000}, {"CHEST_GOLD_CONFIRM_WAIT", 0.1, 5}, {"CTF_CAPTURE_LIMIT", 0, 1000000}, {"CTF_LOCK_HOLD_DISTANCE", 30, 500}, {"CTF_HOLD_REACQUIRE_SECONDS", 180, 600}, {"BRING_SHIPS_SPEED", 10, 500}, {"BRING_SHIPS_TIMEOUT", 3, 60}, {"BRING_SHIPS_ARRIVAL_DISTANCE", 5, 100}, {"BRING_SHIPS_PASS_DELAY", 0.1, 30}, {"BRING_SHIPS_MAX_DISTANCE", 1000, 20000}, {"BRING_SHIPS_MAX_WATER_OFFSET", 30, 1000}, }) do Config[setting[1]] = math.clamp(Config[setting[1]], setting[2], setting[3]) UI.Inputs[setting[1]].Text = tostring(Config[setting[1]]) end Config.CHEST_FAILURE_LIMIT = math.floor(Config.CHEST_FAILURE_LIMIT) UI.Inputs.CHEST_FAILURE_LIMIT.Text = tostring(Config.CHEST_FAILURE_LIMIT) Config.CHEST_MAX_PICKUP_WAIT = math.max(Config.CHEST_MAX_PICKUP_WAIT, Config.CHEST_PICKUP_WAIT) UI.Inputs.CHEST_MAX_PICKUP_WAIT.Text = tostring(Config.CHEST_MAX_PICKUP_WAIT) Config.CTF_PICKUP_ATTEMPTS = math.floor(Config.CTF_PICKUP_ATTEMPTS) UI.Inputs.CTF_PICKUP_ATTEMPTS.Text = tostring(Config.CTF_PICKUP_ATTEMPTS) Config.CTF_CAPTURE_LIMIT = math.floor(Config.CTF_CAPTURE_LIMIT) UI.Inputs.CTF_CAPTURE_LIMIT.Text = tostring(Config.CTF_CAPTURE_LIMIT) FlagCapture.resetRun() if State.isCTFHogFlagActive then FlagCapture.setHoldEnabled(true) else FlagCapture.setEnabled(State.isAutoCTFActive) end if Config.BRING_SHIPS_DESTINATION ~= "Point" and Config.BRING_SHIPS_DESTINATION ~= "Player" then Config.BRING_SHIPS_DESTINATION = "Point" end if Config.BRING_SHIPS_MODE ~= "Auto" and Config.BRING_SHIPS_MODE ~= "Teleport" and Config.BRING_SHIPS_MODE ~= "Sail" then Config.BRING_SHIPS_MODE = "Auto" end ShipBring.refreshUI() ShipBring.setEnabled(State.isBringShipsActive) UI.FarmBtn.Text = State.isFarming and "Stop Chest Farm" or "Instant Chest Farm" UI.FarmBtn.BackgroundColor3 = State.isFarming and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if State.isFarming then farmChests() end if State.isMapFireVisualizerActive then if not State.mapFireVisualizer then State.mapFireVisualizer = createDualSidedSphere("MapFireVisualizer", Color3.fromRGB(255, 100, 100)) end else if State.mapFireVisualizer then State.mapFireVisualizer:Destroy(); State.mapFireVisualizer = nil end end if State.isFlingAuraVisActive then if not State.flingAuraVisualizer then State.flingAuraVisualizer = createDualSidedSphere("FlingAuraVisualizer", Color3.fromRGB(200, 50, 255)) end else if State.flingAuraVisualizer then State.flingAuraVisualizer:Destroy(); State.flingAuraVisualizer = nil end end if State.isHitboxExpanderActive then task.spawn(updateHitboxes) else restoreHitboxes() end if State.isTreasureHitboxActive then task.spawn(updateTreasureHitboxes) else restoreTreasureHitboxes() end ChatGui.Visible = (State.isCustomChatActive == true) if not State.isFullbrightActive then Lighting.Ambient = origLighting.Ambient Lighting.ColorShift_Bottom = origLighting.ColorShift_Bottom Lighting.ColorShift_Top = origLighting.ColorShift_Top Lighting.FogEnd = origLighting.FogEnd Lighting.GlobalShadows = origLighting.GlobalShadows Lighting.ClockTime = origLighting.ClockTime end ensureWaterPlatform() if not State.isPlayerNoClipActive then for part in pairs(Cache.originalPlayerCollisions) do if part and part.Parent then part.CanCollide = true end end table.clear(Cache.originalPlayerCollisions) end if not State.isShipNoClipActive then for part in pairs(Cache.originalShipCollisions) do if part and part.Parent then part.CanCollide = true end end table.clear(Cache.originalShipCollisions) end local char = player.Character local hum = char and char:FindFirstChildOfClass("Humanoid") if hum then if not State.isCharSpeedActive then hum.WalkSpeed = 16 end if not State.isCharJumpActive then hum.JumpPower = 50 end if not State.isPlayerFlyActive then hum.PlatformStand = false end if State.isAntiSitActive then hum:SetStateEnabled(Enum.HumanoidStateType.Seated, false) if hum.Sit then hum.Sit = false hum:ChangeState(Enum.HumanoidStateType.Jumping) end else hum:SetStateEnabled(Enum.HumanoidStateType.Seated, true) end end if not State.isPlayerFlyActive then if playerFlyBrake then playerFlyBrake:Destroy(); playerFlyBrake = nil end if playerFlyGyro then playerFlyGyro:Destroy(); playerFlyGyro = nil end end if not State.isFlingActive then restoreFlingStates() end if not State.isReachActive then for tool, props in pairs(Cache.originalToolProps) do if tool and tool.Parent then local handle = tool:FindFirstChild("Handle") if handle then handle.Size = props.Size; handle.Transparency = props.Transparency; handle.Massless = props.Massless local vis = handle:FindFirstChild("ReachVisual"); if vis then vis:Destroy() end end end end table.clear(Cache.originalToolProps) end if not State.isFreecamActive then local cam = workspace.CurrentCamera if cam then cam.CameraType = Enum.CameraType.Custom if hum then cam.CameraSubject = hum end end UserInputService.MouseBehavior = Enum.MouseBehavior.Default local hrp = char and char:FindFirstChild("HumanoidRootPart") if hrp then hrp.Anchored = false end end if updateModWarning then updateModWarning() end Config.FLING_MODE_INDEX = math.clamp(math.floor(tonumber(Config.FLING_MODE_INDEX) or 1), 1, #flingModes) UI.FlingModeBtn.Text = "Mode: " .. (flingModes[Config.FLING_MODE_INDEX] or "Vehicle") if UI.Inputs.FLING_POWER then UI.Inputs.FLING_POWER.Text = tostring(Config.FLING_POWER) end if UI.Inputs.FREECAM_SPEED then UI.Inputs.FREECAM_SPEED.Text = tostring(Config.FREECAM_SPEED) end UI.TargetInputBox.Text = State.flingTargetStr or "" UI.FlingDropdownBtn.Text = "Target: " .. ((State.flingTargetStr == nil or State.flingTargetStr == "") and "None" or State.flingTargetStr) UI.HitboxPartDropdown.Text = "Target Part: " .. Config.HITBOX_TARGET_PART if State.isZoomUncapActive then player.CameraMaxZoomDistance = 9999 else player.CameraMaxZoomDistance = originalMaxZoomDistance end -- Ship HUD bar Config.SHIP_HUD_X = math.floor(tonumber(Config.SHIP_HUD_X) or 0) Config.SHIP_HUD_Y = math.max(0, math.floor(tonumber(Config.SHIP_HUD_Y) or 64)) ShipHUD.applyTransparency() ShipHUD.applyPosition() ShipHUD.Frame.Visible = State.isShipHUDActive == true if UI.Inputs.SHIP_HUD_TRANSPARENCY then UI.Inputs.SHIP_HUD_TRANSPARENCY.Text = tostring(Config.SHIP_HUD_TRANSPARENCY) end -- Quick Turn -- refreshQuickTurnUI below applies this to ShipTurn.steerSign; ShipTurn itself -- is declared further down the file and is not in scope here. Config.QUICK_TURN_INVERT_STEER = (Config.QUICK_TURN_INVERT_STEER == true) Config.QUICK_TURN_DEGREES = math.clamp(math.abs(tonumber(Config.QUICK_TURN_DEGREES) or 45), 1, 179) if UI.Inputs.QUICK_TURN_DEGREES then UI.Inputs.QUICK_TURN_DEGREES.Text = tostring(Config.QUICK_TURN_DEGREES) end Config.QUICK_TURN_LEAD = math.clamp(tonumber(Config.QUICK_TURN_LEAD) or 0.35, 0, 3) if UI.Inputs.QUICK_TURN_LEAD then UI.Inputs.QUICK_TURN_LEAD.Text = tostring(Config.QUICK_TURN_LEAD) end if refreshQuickTurnUI then refreshQuickTurnUI() end -- Auto CTF mode override if Config.CTF_MODE_OVERRIDE ~= "Auto" and Config.CTF_MODE_OVERRIDE ~= "TwoFlag" and Config.CTF_MODE_OVERRIDE ~= "Neutral" then Config.CTF_MODE_OVERRIDE = "Auto" end if refreshCTFModeUI then refreshCTFModeUI() end end --// PANIC TRIGGER LOGIC local function triggerPanic(staffName) ChestFarm.stopMovement() ColaDelivery.cancel() ShipBring.setEnabled(false) LoopKillEnemies.setEnabled(false) Keybindings.cancelCapture() State.isSpammingLeft = false State.isSpammingRight = false State.isSpammingAll = false State.isAutoFireActive = false State.isSpeedBoosting = false State.isFlying = false State.isShipNoClipActive = false State.isWaterWalkActive = false State.isPlayerFlyActive = false State.isPlayerNoClipActive = false State.isCharSpeedActive = false State.isCharJumpActive = false State.isInfJumpActive = false State.isAutoCTFActive = false State.isCTFHogFlagActive = false UI.CTFHogFlagBtn.Text = "Hold Flag / No Capture: OFF" UI.CTFHogFlagBtn.BackgroundColor3 = Color3.fromRGB(170, 50, 50) State.isAutoFortActive = false State.isKillAuraActive = false State.isReachActive = false State.isHitboxExpanderActive = false State.isTreasureHitboxActive = false State.isAntiSitActive = false State.isFlingActive = false State.isFlingAttachActive = false State.isFlingAuraActive = false State.isAutoMapFireActive = false State.isStealEnemyActive = false State.isStealFriendlyActive = false State.isFarming = false State.isFreecamActive = false State.isZoomUncapActive = false State.isAutoHPReturnActive = false -- Drop anything still queued so clicking stops on this frame rather than -- trickling out for another second after the panic. if Fire then Fire.clear() end local off = Color3.fromRGB(170, 50, 50) UI.LeftBtn.Text = "Fire Left Cannons: OFF"; UI.LeftBtn.BackgroundColor3 = off UI.RightBtn.Text = "Fire Right Cannons: OFF"; UI.RightBtn.BackgroundColor3 = off UI.FireAllBtn.Text = "Fire ALL Cannons (Hold): OFF"; UI.FireAllBtn.BackgroundColor3 = off UI.AutoFireBtn.Text = "Broadside Auto-Fire: OFF"; UI.AutoFireBtn.BackgroundColor3 = off UI.SpeedBtn.Text = "Ship Speed Boost: OFF"; UI.SpeedBtn.BackgroundColor3 = off UI.FlyBtn.Text = "Ship Flight: OFF"; UI.FlyBtn.BackgroundColor3 = off UI.ShipNoClipBtn.Text = "Ship No-Clip: OFF"; UI.ShipNoClipBtn.BackgroundColor3 = off UI.WaterWalkBtn.Text = "Jesus Walk (Water): OFF"; UI.WaterWalkBtn.BackgroundColor3 = off UI.PlayerFlyBtn.Text = "Player Fly: OFF"; UI.PlayerFlyBtn.BackgroundColor3 = off UI.PlayerNoClipBtn.Text = "Player No-Clip: OFF"; UI.PlayerNoClipBtn.BackgroundColor3 = off UI.CharSpeedBtn.Text = "Player WalkSpeed Override: OFF"; UI.CharSpeedBtn.BackgroundColor3 = off UI.CharJumpBtn.Text = "Player JumpPower Override: OFF"; UI.CharJumpBtn.BackgroundColor3 = off UI.InfJumpBtn.Text = "Infinite Jump: OFF"; UI.InfJumpBtn.BackgroundColor3 = off UI.AntiSitBtn.Text = "Anti-Sit / Anti-Seat: OFF"; UI.AntiSitBtn.BackgroundColor3 = off UI.AutoCTFBtn.Text = "Auto CTF: OFF"; UI.AutoCTFBtn.BackgroundColor3 = off UI.AutoFortBtn.Text = "Auto Fort (Hold Flags): OFF"; UI.AutoFortBtn.BackgroundColor3 = off UI.KillAuraBtn.Text = "Melee Kill Aura: OFF"; UI.KillAuraBtn.BackgroundColor3 = off UI.ReachBtn.Text = "Melee Range Extender: OFF"; UI.ReachBtn.BackgroundColor3 = off UI.HitboxToggleBtn.Text = "Enemy Hitbox Expander: OFF"; UI.HitboxToggleBtn.BackgroundColor3 = off UI.TreasureHitboxBtn.Text = "Expand Gold Hitbox: OFF"; UI.TreasureHitboxBtn.BackgroundColor3 = off UI.FlingToggleBtn.Text = "Ultimate Fling: OFF"; UI.FlingToggleBtn.BackgroundColor3 = off UI.FlingAttachBtn.Text = "Attach to Target: OFF"; UI.FlingAttachBtn.BackgroundColor3 = off UI.FlingAuraBtn.Text = "Fling Aura: OFF"; UI.FlingAuraBtn.BackgroundColor3 = off UI.MapFireBtn.Text = "Auto-Fire Map Activators: OFF"; UI.MapFireBtn.BackgroundColor3 = off UI.StealEnemyBtn.Text = "Steal Enemy Ships: OFF"; UI.StealEnemyBtn.BackgroundColor3 = off UI.StealFriendlyBtn.Text = "Steal Friendly Ships: OFF"; UI.StealFriendlyBtn.BackgroundColor3 = off UI.FarmBtn.Text = "Instant Chest Farm"; UI.FarmBtn.BackgroundColor3 = Color3.fromRGB(170, 50, 50) UI.FreecamBtn.Text = "Freecam: OFF"; UI.FreecamBtn.BackgroundColor3 = off UI.ZoomUncapBtn.Text = "Uncap Camera Zoom: OFF"; UI.ZoomUncapBtn.BackgroundColor3 = off UI.AutoHPReturnBtn.Text = "Auto Return on Low HP: OFF"; UI.AutoHPReturnBtn.BackgroundColor3 = off restoreHitboxes() restoreFlingStates() restoreTreasureHitboxes() if activeFlyBrake then activeFlyBrake:Destroy(); activeFlyBrake = nil end if playerFlyBrake then playerFlyBrake:Destroy(); playerFlyBrake = nil end if playerFlyGyro then playerFlyGyro:Destroy(); playerFlyGyro = nil end if waterPlatform then waterPlatform:Destroy(); waterPlatform = nil end local cam = workspace.CurrentCamera if cam then cam.CameraType = Enum.CameraType.Custom end UserInputService.MouseBehavior = Enum.MouseBehavior.Default local char = player.Character if char then local hum = char:FindFirstChildOfClass("Humanoid") if hum then hum.WalkSpeed = 16 hum.JumpPower = 50 hum.PlatformStand = false hum:SetStateEnabled(Enum.HumanoidStateType.Seated, true) player.CameraMaxZoomDistance = originalMaxZoomDistance if cam then cam.CameraSubject = hum end end local hrp = char:FindFirstChild("HumanoidRootPart") if hrp then hrp.Anchored = false end end for part in pairs(Cache.originalPlayerCollisions) do if part and part.Parent then part.CanCollide = true end end table.clear(Cache.originalPlayerCollisions) for part in pairs(Cache.originalShipCollisions) do if part and part.Parent then part.CanCollide = true end end table.clear(Cache.originalShipCollisions) local panicMessage = "Panic action executed manually." if staffName then panicMessage = "Staff " .. tostring(staffName) .. " detected. Panic action executed." end notifyUser("Panic Executed", panicMessage, 8) end local StaffDetector = { groupId = StaffSettings.GroupId, playerRecords = {}, } if StaffDetector.groupId == 0 and game.CreatorType == Enum.CreatorType.Group then StaffDetector.groupId = game.CreatorId end function StaffDetector.roleMatches(roleName, rank) if StaffSettings.MinimumRank > 0 and type(rank) == "number" then if rank >= StaffSettings.MinimumRank then return true end end if type(roleName) ~= "string" then return false end for word in string.gmatch(string.lower(roleName), "%a+") do if StaffSettings.RoleWords[word] then return true end end return false end local function isPlayerStaff(candidate) if StaffSettings.UserIds[candidate.UserId] == true then return true, "configured staff" end if game.CreatorType == Enum.CreatorType.User and candidate.UserId == game.CreatorId then return true, "experience owner" end if StaffDetector.groupId <= 0 then return false end local lookupSucceeded, membership = pcall(function() return GroupService:GetRolesInGroupAsync(candidate.UserId, StaffDetector.groupId) end) if lookupSucceeded and type(membership) == "table" then if membership.IsMember == false then return false end if type(membership.Roles) == "table" then for _, role in ipairs(membership.Roles) do if type(role) == "table" and StaffDetector.roleMatches(role.Name, role.Rank) then return true, "possible staff; group role: " .. tostring(role.Name) end end return false end end -- Compatibility with clients that do not expose the multi-role API. local fallbackSucceeded, roleName, rank = pcall(function() local fallbackRole = candidate:GetRoleInGroup(StaffDetector.groupId) local fallbackRank = 0 if StaffSettings.MinimumRank > 0 then fallbackRank = candidate:GetRankInGroup(StaffDetector.groupId) end return fallbackRole, fallbackRank end) if fallbackSucceeded and type(roleName) == "string" then if StaffDetector.roleMatches(roleName, rank) then return true, "possible staff; group role: " .. roleName end return false end return nil end local function updateModWarningImpl() if Runtime.closed or not ModWarningLabel.Parent then return end if not State.isModDetectorActive then ModWarningLabel.Visible = false return end local staffNames = {} for _, candidate in ipairs(Players:GetPlayers()) do if candidate ~= player and Cache.knownMods[candidate.UserId] then table.insert(staffNames, candidate.Name) end end table.sort(staffNames) if #staffNames > 0 then ModWarningLabel.Text = "STAFF / POSSIBLE STAFF: " .. table.concat(staffNames, ", ") ModWarningLabel.Visible = true else ModWarningLabel.Visible = false ModWarningLabel.Text = "No detected staff currently present" end end updateModWarning = updateModWarningImpl function StaffDetector.notifyDeparture(record) if record.departureNotified then return end record.departureNotified = true if State.isModDetectorActive and not Runtime.closed then notifyUser("STAFF LEFT", record.name .. " left the server (" .. record.reason .. ").", 8) end end local function processPlayerForMod(candidate) if Runtime.closed or candidate == player or candidate.Parent ~= Players then return end if StaffDetector.playerRecords[candidate] then return end local record = { name = candidate.Name, hasLeft = false, departureNotified = false, isStaff = false, reason = "detected staff", } StaffDetector.playerRecords[candidate] = record task.spawn(function() local isStaff local reason for attempt = 1, 3 do if Runtime.closed then return end isStaff, reason = isPlayerStaff(candidate) if isStaff ~= nil then break end if record.hasLeft then break end if attempt < 3 then task.wait(attempt * 0.5) end end if Runtime.closed then return end if isStaff == nil then warn("Staff lookup unavailable for " .. record.name .. "; staff status is unknown.") return end if not isStaff then return end record.isStaff = true record.reason = reason -- A completed lookup must never reinsert a departed player into the cache. if record.hasLeft then StaffDetector.notifyDeparture(record) return end if candidate.Parent ~= Players or StaffDetector.playerRecords[candidate] ~= record then return end Cache.knownMods[candidate.UserId] = true updateModWarning() if State.isModDetectorActive then notifyUser("STAFF DETECTED", record.name .. " (" .. record.reason .. ").", 10) if State.isModPanicActive then triggerPanic(record.name) end end end) end local function processPlayerLeaving(candidate) if Runtime.closed or candidate == player then return end local record = StaffDetector.playerRecords[candidate] local wasKnownStaff = Cache.knownMods[candidate.UserId] == true if record then record.hasLeft = true end -- PlayerRemoving may fire while the player still appears in GetPlayers(). Cache.knownMods[candidate.UserId] = nil StaffDetector.playerRecords[candidate] = nil updateModWarning() if record and (record.isStaff or wasKnownStaff) then StaffDetector.notifyDeparture(record) end end table.insert(Connections, Players.PlayerAdded:Connect(processPlayerForMod)) table.insert(Connections, Players.PlayerRemoving:Connect(processPlayerLeaving)) for _, candidate in ipairs(Players:GetPlayers()) do processPlayerForMod(candidate) end --// FLING & TARGET HELPER FUNCTIONS local function getFlingTargetPlayer(nameStr) if not nameStr or nameStr == "" then return nil end local lowerName = string.lower(nameStr) for _, p in ipairs(Players:GetPlayers()) do if p ~= player then if string.find(string.lower(p.Name), lowerName) or string.find(string.lower(p.DisplayName), lowerName) then return p end end end return nil end function LoopKillEnemies.getSelectedPlayer() return getFlingTargetPlayer(State.flingTargetStr) end local function teleportToTargetPlayer() local targetPlayer = getFlingTargetPlayer(State.flingTargetStr) local character = targetPlayer and targetPlayer.Character local rootPart = character and character:FindFirstChild("HumanoidRootPart") if rootPart then teleportToLocation(rootPart.CFrame * CFrame.new(0, 0, 3)) end end local function setStealPause(stateVal) if stateVal == nil then State.isStealPaused = not State.isStealPaused else State.isStealPaused = stateVal end UI.PauseStealBtn.Text = "Pause Steal: " .. (State.isStealPaused and "ON" or "OFF") UI.PauseStealBtn.BackgroundColor3 = State.isStealPaused and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end --// UI DRAG & MINIMIZE local isMinimized = false table.insert(Connections, Keybindings.connectButton("MinBtn", UI.MinBtn, function() isMinimized = not isMinimized if isMinimized then UI.MinBtn.Text = "+"; TabBar.Visible = false for _, s in pairs(Tabs) do s.Visible = false end Frame.Size = MIN_SIZE else UI.MinBtn.Text = "-" TabBar.Visible = true selectTab(UI.ActiveTab) Frame.Size = FULL_SIZE end end)) local dragging, dragInput, dragStart, startPos, dragConn table.insert(Connections, TopBar.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then dragging = true; dragStart = input.Position; startPos = Frame.Position if dragConn then dragConn:Disconnect() end dragConn = input.Changed:Connect(function() if input.UserInputState == Enum.UserInputState.End then dragging = false; dragConn:Disconnect(); dragConn = nil end end) end end)) table.insert(Connections, TopBar.InputChanged:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then dragInput = input end end)) table.insert(Connections, UserInputService.InputChanged:Connect(function(input) if input == dragInput and dragging then local delta = input.Position - dragStart Frame.Position = UDim2.new(startPos.X.Scale, startPos.X.Offset + delta.X, startPos.Y.Scale, startPos.Y.Offset + delta.Y) end end)) -- The old isValidCannon lived here. classifyShipDetector (further down) now does -- the same VALID_CANNONS ancestor walk and additionally reports the loosely -- named weapons that the port/starboard broadsides used to miss entirely. local function isValidMapActivator(clickDetector) local parent = clickDetector.Parent while parent and parent ~= workspace do if VALID_CANNONS[parent.Name] then return true end local lowerName = string.lower(parent.Name) if string.find(lowerName, "button") or string.find(lowerName, "mine") or string.find(lowerName, "demolition") or string.find(lowerName, "explosive") or string.find(lowerName, "trigger") then return true end parent = parent.Parent end return false end local function getInstancePosition(instance) if instance:IsA("BasePart") then return instance.Position end if instance:IsA("Model") and instance.PrimaryPart then return instance.PrimaryPart.Position end for _, child in ipairs(instance:GetDescendants()) do if child:IsA("BasePart") then return child.Position end end return nil end -- Broader than VALID_CANNONS: bow/stern chasers, swivels and mortars are often -- named something else entirely, which is exactly why they never fired with the -- port/starboard broadsides. local classifyShipDetector do local FIREABLE_WORDS = { "cannon", "gun", "mortar", "swivel", "ballista", "harpoon", "chaser", "carronade", "culverin", "bombard", "launcher", "torpedo", "flamethrower", "broadside", "turret", } local ACTIVATOR_WORDS = { "button", "mine", "demolition", "explosive", "trigger", "activator", "lever", } -- Returns isFireable, isStrictCannon. function classifyShipDetector(clickDetector) local parent = clickDetector.Parent local depth = 0 while parent and parent ~= workspace and depth < 12 do if VALID_CANNONS[parent.Name] then return true, true end local lowerName = string.lower(parent.Name) for _, word in ipairs(FIREABLE_WORDS) do if string.find(lowerName, word, 1, true) then return true, false end end for _, word in ipairs(ACTIVATOR_WORDS) do if string.find(lowerName, word, 1, true) then return true, false end end parent = parent.Parent depth = depth + 1 end return false, false end end local function scanCannons(ship, seatPart) Cache.LeftCannons = {} Cache.RightCannons = {} Cache.AllCannons = {} Cache.ShipActivators = {} Cache.FrontCannons = {} Cache.RearCannons = {} Cache.unplacedCannons = 0 if not ship or not ship.Parent then return end local referenceCFrame = seatPart and seatPart.CFrame or ship:GetPivot() local captainSeat = getMainDriverSeat(ship) if captainSeat then referenceCFrame = captainSeat.CFrame elseif ship.PrimaryPart then referenceCFrame = ship.PrimaryPart.CFrame end for _, obj in ipairs(ship:GetDescendants()) do if obj:IsA("ClickDetector") then local fireable, strict = classifyShipDetector(obj) if fireable then -- A position is only needed to decide port vs starboard. It used -- to gate membership entirely, so any cannon whose parent had no -- resolvable position was dropped from every list and could never -- be fired by anything. Membership no longer depends on it. local pos = getInstancePosition(obj.Parent) local relativePos = pos and referenceCFrame:PointToObjectSpace(pos) or nil if strict then table.insert(Cache.AllCannons, obj) if relativePos then if relativePos.X < 0 then table.insert(Cache.LeftCannons, obj) else table.insert(Cache.RightCannons, obj) end -- -Z is forward in Roblox object space. if relativePos.Z < 0 then table.insert(Cache.FrontCannons, obj) else table.insert(Cache.RearCannons, obj) end else -- Unplaceable but still fireable: give it to both -- broadsides so no side loses coverage. table.insert(Cache.LeftCannons, obj) table.insert(Cache.RightCannons, obj) Cache.unplacedCannons = (Cache.unplacedCannons or 0) + 1 end else table.insert(Cache.ShipActivators, obj) end end end end end --// FAIR FIRING ENGINE -- The previous version restarted at index 1 on every pass and yielded mid-list, -- so on ships (or maps) with many detectors the first handful were fired over -- and over while the tail of the list was rarely or never reached - the blind -- spots in coverage. This tracks which detectors have already had their turn in -- the current round and only moves to a new round once every one of them has -- fired, so coverage is exactly even no matter how long the list is or how the -- list churns between passes. Fire = { -- One shared, frame-paced queue. Everything that wants to click a detector -- puts it in here; a single Heartbeat drain is the only thing that actually -- calls fireclickdetector. pending = {}, queued = {}, -- detector -> true, so a source cannot flood the queue cursor = 1, firedTotal = 0, droppedTotal = 0, lastDrain = 0, lastQueueDepth = 0, signature = nil, -- which fireclickdetector call form this executor accepts probed = false, allowance = 0, -- fractional click credit carried between frames } -- Work out once which call form this executor's fireclickdetector accepts. -- pcall success only tells us the call did not ERROR; it cannot tell us the -- click was delivered, so there is no point "retrying" other forms per call - -- that just doubles the traffic on the exact bottleneck we are fighting. function Fire.probeSignature(sample) if Fire.probed or not sample then return Fire.signature end Fire.probed = true if typeof(fireclickdetector) ~= "function" then Fire.signature = nil return nil end if pcall(fireclickdetector, sample, 0, "MouseClick") then Fire.signature = "full" elseif pcall(fireclickdetector, sample, 0) then Fire.signature = "distance" elseif pcall(fireclickdetector, sample) then Fire.signature = "plain" else Fire.signature = nil end return Fire.signature end function Fire.detector(detector) if not detector or not detector.Parent then return false end if not Fire.probed then Fire.probeSignature(detector) end local signature = Fire.signature if signature == "full" then return pcall(fireclickdetector, detector, 0, "MouseClick") elseif signature == "distance" then return pcall(fireclickdetector, detector, 0) elseif signature == "plain" then return pcall(fireclickdetector, detector) end return false end function Fire.queueDepth() return #Fire.pending - Fire.cursor + 1 end -- Adds detectors that are not already waiting. Returns how many were added. function Fire.enqueue(list) if not list then return 0 end local maxDepth = math.max(16, math.floor(tonumber(Config.FIRE_QUEUE_MAX) or 400)) local added = 0 for _, detector in ipairs(list) do if Fire.queueDepth() >= maxDepth then Fire.droppedTotal = Fire.droppedTotal + 1 break end if detector and detector.Parent and not Fire.queued[detector] then Fire.queued[detector] = true table.insert(Fire.pending, detector) added = added + 1 end end return added end -- Drains according to a per-second allowance rather than a per-frame count, so -- the send rate is independent of frame rate and can be held just under the -- server's ceiling. function Fire.drain(perFrame) local fired = 0 local total = #Fire.pending while fired < perFrame and Fire.cursor <= total do local detector = Fire.pending[Fire.cursor] Fire.cursor = Fire.cursor + 1 if detector then Fire.queued[detector] = nil if detector.Parent and Fire.detector(detector) then fired = fired + 1 Fire.firedTotal = Fire.firedTotal + 1 end end end if Fire.cursor > #Fire.pending then table.clear(Fire.pending) Fire.cursor = 1 elseif Fire.cursor > 256 then -- Compact. Producers like the map-activator sweep keep the queue -- permanently non-empty, so without this the array grows without bound -- for the whole session even though the live depth stays small. local compacted = table.create(#Fire.pending - Fire.cursor + 1) for index = Fire.cursor, #Fire.pending do table.insert(compacted, Fire.pending[index]) end Fire.pending = compacted Fire.cursor = 1 end Fire.lastQueueDepth = Fire.queueDepth() return fired end function Fire.clear() table.clear(Fire.pending) table.clear(Fire.queued) Fire.cursor = 1 Fire.lastQueueDepth = 0 end -- Universal "fire everything on the ship I am sitting on": every recognised -- cannon regardless of side, plus swivels/mortars/triggers when the broad -- option is enabled. local function fireAllShipWeapons() if not CurrentShip or not CurrentShip.Parent then return 0, 0 end local cannonCount = Fire.enqueue(Cache.AllCannons) local extraCount = 0 if State.isFireAllBroad then extraCount = Fire.enqueue(Cache.ShipActivators) end if cannonCount + extraCount > 0 then local now = tick() lastLeftFire = now lastRightFire = now end return cannonCount, extraCount end -- One press, several passes. A refused click is invisible to us, so the only way -- to improve single-shot coverage is to come back around and ask again once the -- queue has drained; each extra pass picks up whatever the previous one lost. local function fireAllShipWeaponsRepeated() local passes = math.clamp(math.floor(tonumber(Config.FIRE_ONE_SHOT_PASSES) or 2), 1, 5) local cannonCount, extraCount = fireAllShipWeapons() if cannonCount + extraCount == 0 or passes <= 1 then return cannonCount, extraCount, 1 end task.spawn(function() local rate = math.clamp(tonumber(Config.FIRE_PER_SECOND) or 8.3, 0.5, 200) for _ = 2, passes do -- Wait for the previous pass to clear so the passes do not interleave -- and push the send rate above what the queue is pacing for. local deadline = os.clock() + ((cannonCount + extraCount) / rate) + 2 while Fire.queueDepth() > 0 and os.clock() < deadline do task.wait(0.1) end if Runtime.closed or not CurrentShip or not CurrentShip.Parent then return end fireAllShipWeapons() end end) return cannonCount, extraCount, passes end -- Cached part census. Also used by the HUD to report parts lost / unwelded, -- which is available even when the ship exposes a real health value. local function getShipPartStats(ship) if not ship or not ship.Parent or not ship:IsA("Model") then return 0, 0 end local now = tick() if not Cache.shipPartCount[ship] or (now - (Cache.lastPartCountUpdate[ship] or 0) > 1.5) then local currentParts = 0 for _, p in ipairs(ship:GetDescendants()) do if p:IsA("BasePart") and not p:IsA("Seat") and not p:IsA("VehicleSeat") then currentParts = currentParts + 1 end end Cache.shipPartCount[ship] = currentParts Cache.lastPartCountUpdate[ship] = now if not Cache.shipInitialParts[ship] or currentParts > Cache.shipInitialParts[ship] then Cache.shipInitialParts[ship] = currentParts end end local currentParts = Cache.shipPartCount[ship] or 0 local maxParts = Cache.shipInitialParts[ship] or currentParts return currentParts, maxParts end local function getShipHealthData(ship) if not ship or not ship:IsA("Model") then return 0, 0, 0 end local hum = ship:FindFirstChildOfClass("Humanoid") or ship:FindFirstChild("Humanoid", true) if hum and hum.MaxHealth > 0 then return math.floor(hum.Health), math.floor(hum.MaxHealth), math.clamp(hum.Health / hum.MaxHealth, 0, 1) end for _, name in ipairs({"Health", "Durability", "HullHealth", "HP"}) do local val = ship:FindFirstChild(name) or ship:FindFirstChild(string.lower(name)) if val and (val:IsA("IntValue") or val:IsA("NumberValue")) then local maxObj = ship:FindFirstChild("Max" .. val.Name) local maxNum = maxObj and maxObj.Value or 100 local ratio = maxNum > 0 and (val.Value / maxNum) or 0 return math.floor(val.Value), math.floor(maxNum), math.clamp(ratio, 0, 1) end end local currentParts, maxParts = getShipPartStats(ship) local pct = maxParts > 0 and (currentParts / maxParts) or 1 return currentParts, maxParts, math.clamp(pct, 0, 1) end local function getShipAffiliation(ship) local allyColor = Color3.fromRGB(0, 255, 0); local enemyColor = Color3.fromRGB(255, 50, 50) local activeTeam = nil; local activeName = nil if not Cache.shipSeats[ship] or tick() - (Cache.lastShipSeatScan[ship] or 0) > 2 then Cache.shipSeats[ship] = {} for _, part in ipairs(ship:GetDescendants()) do if part:IsA("VehicleSeat") or part:IsA("Seat") then table.insert(Cache.shipSeats[ship], part) end end Cache.lastShipSeatScan[ship] = tick() end for _, part in ipairs(Cache.shipSeats[ship]) do if part.Occupant then local occChar = part.Occupant.Parent local occPlayer = Players:GetPlayerFromCharacter(occChar) if occPlayer and occPlayer.Team then activeTeam = occPlayer.Team; activeName = occPlayer.DisplayName or occPlayer.Name Cache.shipAffiliation[ship] = activeTeam; Cache.shipOwner[ship] = activeName break end end end local effectiveTeam = activeTeam or Cache.shipAffiliation[ship] local effectiveName = activeName or Cache.shipOwner[ship] or "Unknown" if not effectiveTeam then return "unmanned", Color3.fromRGB(150, 150, 150), "Unknown" end if player.Team and effectiveTeam == player.Team then return "friendly", allyColor, effectiveName else local eColor = effectiveTeam.TeamColor and effectiveTeam.TeamColor.Color or enemyColor return "enemy", eColor, effectiveName end end local function initShipCache(ship) task.wait(0.2) if not ship or not ship.Parent then return end Cache.shipSeats[ship] = {} for _, part in ipairs(ship:GetDescendants()) do if part:IsA("VehicleSeat") or part:IsA("Seat") then table.insert(Cache.shipSeats[ship], part) end end Cache.lastShipSeatScan[ship] = tick() local root = ship.PrimaryPart or ship:FindFirstChildWhichIsA("BasePart") if not root then task.wait(0.25); root = ship.PrimaryPart or ship:FindFirstChildWhichIsA("BasePart") end if not root then return end local pivot = root.Position; local closestDist = 250; local closestTeam = nil; local closestName = nil for _, p in ipairs(Players:GetPlayers()) do if p.Character and p.Character.PrimaryPart then local dist = (p.Character.PrimaryPart.Position - pivot).Magnitude if dist < closestDist and p.Team then closestDist = dist; closestTeam = p.Team; closestName = p.DisplayName or p.Name end end end if closestTeam then Cache.shipAffiliation[ship] = closestTeam; Cache.shipOwner[ship] = closestName end end local function isNewlySpawnedTarget(ship, seat) if not (State.isStealEnemyActive or State.isStealFriendlyActive) then return false end if seat.Occupant ~= nil then return false end local status = getShipAffiliation(ship) if State.isStealEnemyActive and status == "enemy" then return true end if State.isStealFriendlyActive and status == "friendly" then return true end return false end local function checkAutoBroadside() if not State.isAutoFireActive or not CurrentShip then return end local captainSeat = getMainDriverSeat(CurrentShip) local refCF = captainSeat and captainSeat.CFrame or (CurrentShip.PrimaryPart and CurrentShip.PrimaryPart.CFrame) or CurrentShip:GetPivot() local closestEnemy = nil; local closestDist = Config.AUTO_FIRE_DIST; local closestRelPos = nil for _, ship in ipairs(ShipsFolder:GetChildren()) do if ship:IsA("Model") and ship ~= CurrentShip and not isLifeboatName(ship.Name) then local status = getShipAffiliation(ship) if status == "enemy" then local enemyPos = ship:GetPivot().Position local relPos = refCF:PointToObjectSpace(enemyPos) local dist = relPos.Magnitude if dist <= closestDist and dist > 15 then closestDist = dist; closestEnemy = ship; closestRelPos = relPos end end end end if not closestEnemy or not closestRelPos then return end local now = tick(); local absX = math.abs(closestRelPos.X); local absZ = math.abs(closestRelPos.Z) if absX > (absZ * 0.35) then if closestRelPos.X > 0 and #Cache.RightCannons > 0 then if now - lastRightFire >= 0.8 then lastRightFire = now -- Enqueued, not burst. Firing the whole broadside in one frame -- put it far over the server's click ceiling and most of it was -- discarded; the queue releases it at a rate that lands. Fire.enqueue(Cache.RightCannons) end elseif closestRelPos.X < 0 and #Cache.LeftCannons > 0 then if now - lastLeftFire >= 0.8 then lastLeftFire = now Fire.enqueue(Cache.LeftCannons) end end end end --// QUICK TURN CONTROLLER -- Drives the helm through VehicleSeat.Steer, the same channel the real controls -- use, and stops when the hull has actually swung the requested number of -- degrees. The old CFrame "pivot" method is gone: a client-side PivotTo on a -- server-owned vehicle assembly is reverted on the next physics step, so it -- never worked here. -- -- The reason the degree setting used to be ignored: progress was measured from -- Model:GetPivot(). These ships have no PrimaryPart, and for such a model -- GetPivot() returns an AXIS-ALIGNED bounding-box CFrame whose rotation never -- changes as the hull turns. Measured progress was therefore always ~0, so the -- loop never reached its target, kept steering until the timeout, and massively -- overturned - while simultaneously reporting "0 degrees, no effect". Heading is -- now read from a real part that rotates with the hull. local ShipTurn = { token = 0, active = false, targetDelta = 0, lastResult = "", -- +1 means VehicleSeat.Steer = 1 turns the hull right. Detected on the first -- turn and remembered; Config.QUICK_TURN_INVERT_STEER persists it. steerSign = 1, } -- A part whose CFrame genuinely rotates with the ship. The helm seat is the best -- reference because Steer is relative to the vehicle's own orientation anyway. function ShipTurn.referencePart(ship, seat) if seat and seat.Parent and seat:IsA("BasePart") then return seat end if ship.PrimaryPart then return ship.PrimaryPart end return ship:FindFirstChildWhichIsA("BasePart", true) end -- Compass heading in degrees; increasing = turning right (clockwise from above). function ShipTurn.headingOf(cframe) local look = cframe.LookVector return math.deg(math.atan2(look.X, -look.Z)) end function ShipTurn.wrapAngle(degrees) return (degrees + 180) % 360 - 180 end -- Current yaw rate in degrees/sec, positive = turning right. Used to anticipate -- how far the hull will coast after the helm is centred. function ShipTurn.yawRate(part) if not part or not part.Parent then return 0 end local ok, velocity = pcall(function() return part.AssemblyAngularVelocity end) if not ok or not velocity then return 0 end -- +Y angular velocity is counter-clockwise from above, which is a LEFT turn. return -math.deg(velocity.Y) end function ShipTurn.cancel() ShipTurn.token = ShipTurn.token + 1 ShipTurn.active = false ShipTurn.targetDelta = 0 end function ShipTurn.setStatus(message) ShipTurn.lastResult = message if not Runtime.closed and UI.QuickTurnStatusLabel then UI.QuickTurnStatusLabel.Text = message end end function ShipTurn.setSteer(seat, value) if not seat or not seat.Parent or not seat:IsA("VehicleSeat") then return end pcall(function() seat.Steer = value seat.SteerFloat = value end) end -- deltaDegrees: negative = left, positive = right. function ShipTurn.start(deltaDegrees) local ship = CurrentShip if not ship or not ship.Parent then ShipTurn.setStatus("Quick Turn needs you seated at a ship's helm.") Toast.push("Quick Turn", "Not piloting a ship", Toast.OFF, 2.5) return end local character = player.Character local humanoid = character and character:FindFirstChildOfClass("Humanoid") local seat = humanoid and humanoid.SeatPart if not seat or not seat:IsA("VehicleSeat") then seat = getMainDriverSeat(ship) end if not seat or not seat:IsA("VehicleSeat") then ShipTurn.setStatus("Quick Turn needs a VehicleSeat helm on this ship; none was found.") Toast.push("Quick Turn", "No helm seat found", Toast.OFF, 3) return end local reference = ShipTurn.referencePart(ship, seat) if not reference then ShipTurn.setStatus("Quick Turn could not find a part to measure the ship's heading from.") Toast.push("Quick Turn", "No reference part", Toast.OFF, 3) return end deltaDegrees = math.clamp(deltaDegrees, -179, 179) if ShipTurn.active then -- Tapping again adds to the turn already in progress. ShipTurn.targetDelta = math.clamp(ShipTurn.targetDelta + deltaDegrees, -179, 179) Toast.push("Quick Turn", string.format("Queued: now %.0f deg", ShipTurn.targetDelta), Toast.INFO, 1.5) return end ShipTurn.token = ShipTurn.token + 1 local token = ShipTurn.token ShipTurn.active = true ShipTurn.targetDelta = deltaDegrees task.spawn(function() local timeout = math.clamp(tonumber(Config.QUICK_TURN_TIMEOUT) or 6, 0.5, 30) local lead = math.clamp(tonumber(Config.QUICK_TURN_LEAD) or 0.35, 0, 3) local tolerance = math.clamp(tonumber(Config.QUICK_TURN_TOLERANCE) or 3, 0.5, 30) local startHeading = ShipTurn.headingOf(reference.CFrame) local startClock = os.clock() local steerValidated = false local peak = 0 while ShipTurn.token == token and not Runtime.closed do if not ship.Parent or ship ~= CurrentShip or not reference.Parent then break end local now = os.clock() if now - startClock > timeout then break end local achieved = ShipTurn.wrapAngle(ShipTurn.headingOf(reference.CFrame) - startHeading) peak = math.max(peak, math.abs(achieved)) local remaining = ShipTurn.targetDelta - achieved local rate = ShipTurn.yawRate(reference) -- Stop once we are inside tolerance AND the hull has stopped swinging, -- so a ship still coasting keeps getting counter-steer. if math.abs(remaining) <= tolerance and math.abs(rate) <= 8 then break end -- Steer direction auto-correction. Whether Steer = 1 swings the bow -- right depends on the game's steering code and on how the helm seat -- is oriented inside the hull, so it is measured rather than assumed. if not steerValidated and now - startClock > 0.45 then if math.abs(achieved) >= 2.5 then steerValidated = true if (achieved > 0) ~= (ShipTurn.targetDelta > 0) then ShipTurn.steerSign = -ShipTurn.steerSign Config.QUICK_TURN_INVERT_STEER = (ShipTurn.steerSign < 0) if refreshQuickTurnUI then refreshQuickTurnUI() end Toast.push("Quick Turn", "Steer direction was inverted - corrected and saved", Toast.WARN, 4) end elseif now - startClock > 1.5 then steerValidated = true end end -- Anticipate the coast: a heavy hull keeps rotating after the helm is -- centred, so aim at where we WILL be, not where we are. Without this -- the turn always overshoots regardless of the requested angle. local projected = remaining - (rate * lead) local steer = 0 if projected > 0.5 then steer = 1 elseif projected < -0.5 then steer = -1 end ShipTurn.setSteer(seat, steer * ShipTurn.steerSign) task.wait() end ShipTurn.setSteer(seat, 0) if ShipTurn.token ~= token then return end ShipTurn.active = false local requested = ShipTurn.targetDelta ShipTurn.targetDelta = 0 -- Let the hull settle, then report what actually stuck. task.wait(0.4) if Runtime.closed or not ship.Parent or not reference.Parent or ShipTurn.token ~= token then return end local settled = ShipTurn.wrapAngle(ShipTurn.headingOf(reference.CFrame) - startHeading) local progress = math.abs(requested) > 0 and (settled / requested) or 0 local side = requested < 0 and "left" or "right" local summary if progress >= 0.75 and progress <= 1.35 then summary = string.format("Quick Turn %s: asked %.0f deg, turned %.0f deg.", side, math.abs(requested), math.abs(settled)) Toast.push("Quick Turn " .. side, string.format("%.0f deg", math.abs(settled)), Toast.ON, 2.4) elseif progress > 1.35 then summary = string.format("Quick Turn %s overshot: asked %.0f deg, turned %.0f deg. Raise Turn Lead so the helm centres earlier.", side, math.abs(requested), math.abs(settled)) Toast.push("Quick Turn " .. side, string.format("overshot to %.0f deg", math.abs(settled)), Toast.WARN, 4) elseif progress >= 0.1 then summary = string.format("Quick Turn %s fell short: asked %.0f deg, turned %.0f deg. Lower Turn Lead, or raise the timeout if the ship simply turns slowly.", side, math.abs(requested), math.abs(settled)) Toast.push("Quick Turn " .. side, string.format("only %.0f deg", math.abs(settled)), Toast.WARN, 4) else summary = string.format("Quick Turn %s had no lasting effect (net %.0f deg, peak %.0f deg during the turn). The server is rejecting or undoing the helm input.", side, settled, peak) Toast.push("Quick Turn " .. side, "no effect", Toast.OFF, 4) end ShipTurn.setStatus(summary) end) end function refreshQuickTurnUI() if Runtime.closed then return end local degrees = math.floor(math.clamp(tonumber(Config.QUICK_TURN_DEGREES) or 45, 1, 179)) if UI.TurnLeftBtn then UI.TurnLeftBtn.Text = "Quick Turn Left " .. degrees end if UI.TurnRightBtn then UI.TurnRightBtn.Text = "Quick Turn Right " .. degrees end if UI.QuickTurnInvertBtn then local inverted = Config.QUICK_TURN_INVERT_STEER == true ShipTurn.steerSign = inverted and -1 or 1 UI.QuickTurnInvertBtn.Text = "Invert Steer Direction: " .. (inverted and "ON" or "OFF") UI.QuickTurnInvertBtn.BackgroundColor3 = inverted and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end end local function boardEnemyCaptain() if not ShipsFolder then return end local myPos = player.Character and player.Character:GetPivot().Position or Vector3.zero local closestEnemySeat = nil; local closestDist = math.huge for _, ship in ipairs(ShipsFolder:GetChildren()) do if ship:IsA("Model") and ship ~= CurrentShip and not isLifeboatName(ship.Name) then local status = getShipAffiliation(ship) if status == "enemy" then local seat = getMainDriverSeat(ship) if seat and seat.Occupant then local dist = (seat.Position - myPos).Magnitude if dist < closestDist then closestDist = dist; closestEnemySeat = seat end end end end end if closestEnemySeat and player.Character then local char = player.Character; local hum = char:FindFirstChildOfClass("Humanoid"); local hrp = char:FindFirstChild("HumanoidRootPart") if hum and hrp then if hum.Sit then hum.Sit = false task.wait(0.05) end hrp.CFrame = closestEnemySeat.CFrame * CFrame.new(0, 1.5, 3.5) for _, tool in ipairs(player.Backpack:GetChildren()) do if tool:IsA("Tool") then hum:EquipTool(tool); break end end end end end local function claimSpawnedSeat(seat, isManual) if ColaDelivery.active and not isManual then return end if State.isBringShipsActive and not isManual then return end if not isManual and State.isStealPaused then return end if not seat or seat.Occupant ~= nil or State.isAntiSitActive then return end local char = player.Character; if not char then return end local hum = char:FindFirstChildOfClass("Humanoid"); local hrp = char:FindFirstChild("HumanoidRootPart") if not hum or not hrp then return end if hum.Sit then hum.Sit = false task.wait(0.03) end if not isManual and State.isStealPaused then return end if seat.Occupant ~= nil or State.isAntiSitActive then return end local startTime = tick() while (tick() - startTime < 0.4) and hum.SeatPart ~= seat do if not isManual and State.isStealPaused then return end if seat.Occupant ~= nil and seat.Occupant ~= hum then return end if State.isAntiSitActive then return end hrp.CFrame = seat.CFrame * CFrame.new(0, 0.5, 0) pcall(function() seat:Sit(hum) end) touchSeat(hrp, seat) task.wait(0.04) end if hum.SeatPart == seat then if not isManual then setStealPause(true) end end end local openShipIteratorIndex = 0 local function tpToNextOpenShip() if not ShipsFolder then return end local validOpenShips = {} for _, ship in ipairs(ShipsFolder:GetChildren()) do if ship:IsA("Model") and not isLifeboatName(ship.Name) then local seat = getMainDriverSeat(ship) if seat and seat.Occupant == nil then table.insert(validOpenShips, ship) end end end if #validOpenShips == 0 then return end openShipIteratorIndex = (openShipIteratorIndex % #validOpenShips) + 1 local targetShip = validOpenShips[openShipIteratorIndex] local targetSeat = getMainDriverSeat(targetShip) if targetSeat then claimSpawnedSeat(targetSeat, true) end end local function tryStealNewlySpawnedShip(ship) if ColaDelivery.active then return end if State.isStealPaused or State.isAntiSitActive then return end if not State.isStealEnemyActive and not State.isStealFriendlyActive then return end if not ship:IsA("Model") or isLifeboatName(ship.Name) then return end task.spawn(function() local seat = getMainDriverSeat(ship) if not seat then local retries = 0 while retries < 20 and not seat do if State.isStealPaused or State.isAntiSitActive then return end task.wait(0.05) seat = getMainDriverSeat(ship); retries = retries + 1 end end if State.isStealPaused or State.isAntiSitActive or not seat or seat.Occupant ~= nil then return end local cacheWait = 0 while cacheWait < 20 and not Cache.shipAffiliation[ship] do task.wait(0.05); cacheWait = cacheWait + 1 end if isNewlySpawnedTarget(ship, seat) then claimSpawnedSeat(seat, false) end end) end local function toggleWaterWalk() State.isWaterWalkActive = not State.isWaterWalkActive UI.WaterWalkBtn.Text = "Jesus Walk (Water): " .. (State.isWaterWalkActive and "ON" or "OFF") UI.WaterWalkBtn.BackgroundColor3 = State.isWaterWalkActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) ensureWaterPlatform() end local function TogglePlayerNoClip() State.isPlayerNoClipActive = not State.isPlayerNoClipActive UI.PlayerNoClipBtn.Text = "Player No-Clip: " .. (State.isPlayerNoClipActive and "ON" or "OFF") UI.PlayerNoClipBtn.BackgroundColor3 = State.isPlayerNoClipActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if not State.isPlayerNoClipActive then for part in pairs(Cache.originalPlayerCollisions) do if part and part.Parent then part.CanCollide = true end end table.clear(Cache.originalPlayerCollisions) end end local function updatePlayerESP() local processedPlayers = {} for _, p in ipairs(Players:GetPlayers()) do if p ~= player then processedPlayers[p] = true local char = p.Character if char and char:FindFirstChild("HumanoidRootPart") then local isEnemy = (player.Team == nil) or (p.Team == nil) or (p.Team ~= player.Team) local shouldShow = (isEnemy and State.isPlayerESPActive) or (not isEnemy and State.isAllyPlayerESPActive) if shouldShow then local color = isEnemy and Color3.fromRGB(255, 50, 50) or Color3.fromRGB(0, 255, 0) local myPos = player.Character and player.Character:GetPivot().Position or Vector3.zero local dist = (char.HumanoidRootPart.Position - myPos).Magnitude local hl = Cache.playerEspHighlights[p] if not hl or not hl.Parent or hl.Adornee ~= char then if hl then hl:Destroy() end hl = Instance.new("Highlight") hl.Name = "PlayerESPHighlight"; hl.FillTransparency = 0.5 hl.DepthMode = Enum.HighlightDepthMode.AlwaysOnTop hl.Adornee = char; hl.Parent = espContainer Cache.playerEspHighlights[p] = hl end hl.FillColor = color; hl.OutlineColor = color local lbl = Cache.playerEspLabels[p] local targetAdornee = char:FindFirstChild("Head") or char.PrimaryPart if not lbl or not lbl.Parent or lbl.Adornee ~= targetAdornee then if lbl then lbl:Destroy() end lbl = Instance.new("BillboardGui") lbl.Size = UDim2.new(0, 140, 0, 24); lbl.AlwaysOnTop = true local txt = Instance.new("TextLabel") txt.Name = "Text" txt.Size = UDim2.new(1,0,1,0); txt.BackgroundTransparency = 1 txt.TextColor3 = Color3.new(1,1,1); txt.TextScaled = true; txt.Font = Enum.Font.SourceSansBold txt.Parent = lbl; lbl.Parent = espContainer Cache.playerEspLabels[p] = lbl end lbl.Adornee = targetAdornee local txt = lbl:FindFirstChild("Text") if txt then local hum = char:FindFirstChildOfClass("Humanoid") local hp = hum and hum.Health or 0 txt.Text = string.format("[%s] %.0f st | %.0f HP", p.Name, dist, hp) end else if Cache.playerEspHighlights[p] then Cache.playerEspHighlights[p]:Destroy(); Cache.playerEspHighlights[p] = nil end if Cache.playerEspLabels[p] then Cache.playerEspLabels[p]:Destroy(); Cache.playerEspLabels[p] = nil end end else if Cache.playerEspHighlights[p] then Cache.playerEspHighlights[p]:Destroy(); Cache.playerEspHighlights[p] = nil end if Cache.playerEspLabels[p] then Cache.playerEspLabels[p]:Destroy(); Cache.playerEspLabels[p] = nil end end end end for p, hl in pairs(Cache.playerEspHighlights) do if not processedPlayers[p] or not hl.Adornee or not hl.Adornee.Parent then hl:Destroy() Cache.playerEspHighlights[p] = nil end end for p, lbl in pairs(Cache.playerEspLabels) do if not processedPlayers[p] or not lbl.Adornee or not lbl.Adornee.Parent then lbl:Destroy() Cache.playerEspLabels[p] = nil end end end local function updateESP() if not ShipsFolder then return end local closestEnemy = nil; local closestEnemyDist = math.huge; local closestEnemyName = "None" local myPos = player.Character and player.Character:GetPivot().Position or Vector3.zero local processedShips = {} for _, ship in ipairs(ShipsFolder:GetChildren()) do if ship:IsA("Model") then processedShips[ship] = true if ship == CurrentShip or isLifeboatName(ship.Name) then cleanupESP(ship) else local status, color, occName = getShipAffiliation(ship) local rootPos = ship:GetPivot().Position local dist = (rootPos - myPos).Magnitude local shouldShow = false if status == "enemy" and State.isESPActive then shouldShow = true if dist < closestEnemyDist then closestEnemyDist = dist; closestEnemy = ship; closestEnemyName = occName end elseif status == "friendly" and State.isAllyESPActive then shouldShow = true end if shouldShow then local highlight = Cache.espHighlights[ship] if not highlight or not highlight.Parent or highlight.Adornee ~= ship then if highlight then highlight:Destroy() end highlight = Instance.new("Highlight") highlight.Name = "ESPHighlight"; highlight.FillTransparency = 0.5; highlight.DepthMode = Enum.HighlightDepthMode.AlwaysOnTop highlight.Adornee = ship; highlight.Parent = espContainer Cache.espHighlights[ship] = highlight end highlight.FillColor = color; highlight.OutlineColor = color local targetAdornee = getMainDriverSeat(ship) or ship.PrimaryPart or ship:FindFirstChildWhichIsA("BasePart") local distanceLabel = Cache.espLabels[ship] if not distanceLabel or not distanceLabel.Parent or distanceLabel.Adornee ~= targetAdornee then if distanceLabel then distanceLabel:Destroy() end distanceLabel = Instance.new("BillboardGui") distanceLabel.Name = "ESPDistance"; distanceLabel.Size = UDim2.new(0, 140, 0, 24); distanceLabel.AlwaysOnTop = true local textLabel = Instance.new("TextLabel") textLabel.Name = "Text" textLabel.Size = UDim2.new(1, 0, 1, 0); textLabel.BackgroundTransparency = 1; textLabel.TextColor3 = Color3.new(1, 1, 1) textLabel.TextScaled = true; textLabel.Font = Enum.Font.SourceSansBold; textLabel.Parent = distanceLabel distanceLabel.Parent = espContainer; Cache.espLabels[ship] = distanceLabel end distanceLabel.Adornee = targetAdornee or ship local _, _, pct = getShipHealthData(ship) local tl = distanceLabel:FindFirstChild("Text") if tl then tl.Text = string.format("[%s] %.0f st | %.0f%%", occName, dist, pct * 100) end else cleanupESP(ship) end end end end for ship, hl in pairs(Cache.espHighlights) do if not processedShips[ship] or not ship.Parent or not hl.Adornee or not hl.Adornee.Parent then cleanupESP(ship) end end local _, _, myPct = getShipHealthData(CurrentShip) local myHpStr = CurrentShip and string.format("%.0f%%", myPct * 100) or "N/A" if closestEnemy then local tHp, tMaxHp, tPct = getShipHealthData(closestEnemy) UI.HealthHUDLabel.Text = string.format("My Hull: %s | Target: %.0f%% (%.0fst)", myHpStr, tPct * 100, closestEnemyDist) UI.TargetInfoBox.Text = string.format("Target: %s\nHull Integrity: %.0f%% (%d/%d)\nDistance: %.0f studs\nCaptain: %s", closestEnemy.Name, tPct * 100, tHp, tMaxHp, closestEnemyDist, closestEnemyName) else UI.HealthHUDLabel.Text = string.format("My Hull: %s | Target: None", myHpStr) UI.TargetInfoBox.Text = "Target Ship: None in range\nHull Integrity: N/A\nDistance: N/A\nCaptain: None" end end task.spawn(function() while ScreenGui.Parent do pcall(function() updateESP() updatePlayerESP() end) task.wait(0.2) end end) --// SHIP SEAT MONITOR LOOP Cache.lastCannonRescan = 0 task.spawn(function() while ScreenGui.Parent do pcall(function() if ShipsFolder and player.Character then local hum = player.Character:FindFirstChildOfClass("Humanoid") if hum and hum.SeatPart and (hum.SeatPart:IsA("VehicleSeat") or hum.SeatPart:IsA("Seat")) then local ship = getShipFromSeat(hum.SeatPart) if ship and ship ~= CurrentShip then if State.isShipNoClipActive then for part in pairs(Cache.originalShipCollisions) do if part and part.Parent then part.CanCollide = true end end table.clear(Cache.originalShipCollisions) end CurrentShip = ship; lockedAltitude = nil; scanCannons(ship, hum.SeatPart) Cache.lastCannonRescan = tick() elseif ship and ship == CurrentShip and tick() - Cache.lastCannonRescan > 2.5 then -- Cannons get shot off and repaired mid-match, so keep the -- lists (and therefore the HUD count) honest. Cache.lastCannonRescan = tick() scanCannons(ship, hum.SeatPart) end else if State.isShipNoClipActive then for part in pairs(Cache.originalShipCollisions) do if part and part.Parent then part.CanCollide = true end end table.clear(Cache.originalShipCollisions) end if CurrentShip then ShipTurn.cancel() end CurrentShip = nil; lockedAltitude = nil Cache.LeftCannons = {}; Cache.RightCannons = {} Cache.AllCannons = {}; Cache.ShipActivators = {} Cache.FrontCannons = {}; Cache.RearCannons = {} end end end) task.wait(0.3) end end) --// CANNON SPAMMER LOOP -- All three paths only ENQUEUE. The queue is drained at a fixed, small number of -- clicks per frame by the Heartbeat drain below, because the real reason some -- cannons never went off was not scheduling order but volume: a whole broadside -- clicked inside one frame mostly gets dropped before it reaches the server. task.spawn(function() while ScreenGui.Parent do pcall(function() -- These only ever top the queue up. Because Fire.enqueue skips -- detectors that are already waiting, a fast producer can never -- starve the tail of the list or push the queue into a backlog. if State.isSpammingLeft and #Cache.LeftCannons > 0 then if Fire.enqueue(Cache.LeftCannons) > 0 then lastLeftFire = tick() end end if State.isSpammingRight and #Cache.RightCannons > 0 then if Fire.enqueue(Cache.RightCannons) > 0 then lastRightFire = tick() end end if State.isSpammingAll and CurrentShip then fireAllShipWeapons() end end) task.wait(0.1) end end) --// FIRE QUEUE DRAIN -- The single place fireclickdetector is actually called. Pacing the clicks over -- frames instead of bursting them is what stops the engine dropping most of a -- large volley before it reaches the server. table.insert(Connections, RunService.Heartbeat:Connect(function(deltaTime) if Runtime.closed then return end if Fire.queueDepth() <= 0 then -- Nothing waiting: do not let allowance build up into a burst that would -- blow straight through the ceiling the moment something is queued. Fire.allowance = 0 return end local rate = math.clamp(tonumber(Config.FIRE_PER_SECOND) or 5, 0.5, 200) Fire.allowance = math.min((Fire.allowance or 0) + deltaTime * rate, math.max(1, rate * 0.25)) local budget = math.floor(Fire.allowance) if budget >= 1 then Fire.allowance = Fire.allowance - budget Fire.lastDrain = tick() Fire.drain(budget) end end)) --// SHIP STATUS HUD UPDATE LOOP -- Reads a real reload/loaded value off a cannon. Returns isLive = false when the -- ship exposes nothing usable; there is deliberately no estimated countdown, -- because cannon timing differs per ship and a single guessed number was wrong -- far more often than it was right. function ShipHUD.readLiveReload() for index, detector in ipairs(Cache.AllCannons) do if index > 4 then break end local model = detector.Parent local depth = 0 while model and model ~= workspace and depth < 6 do for _, child in ipairs(model:GetChildren()) do local lower = string.lower(child.Name) if child:IsA("BoolValue") and (lower == "loaded" or lower == "canfire" or lower == "ready" or lower == "isloaded" or lower == "reloaded") then return (child.Value and 0 or -1), true end if (child:IsA("NumberValue") or child:IsA("IntValue")) and (lower == "cooldown" or lower == "reload" or lower == "reloadtime" or lower == "timer" or lower == "debounce") then return math.max(0, child.Value), true end end model = model.Parent depth = depth + 1 end end return nil, false end function ShipHUD.update() local segments = ShipHUD.segments local ship = CurrentShip if not ship or not ship.Parent then segments.Ship.Text = "No ship" segments.Ship.TextColor3 = Color3.fromRGB(170, 180, 195) segments.Cannons.Text = "Cannons -" segments.Reload.Text = "-" segments.Reload.TextColor3 = Color3.fromRGB(170, 180, 195) segments.AutoFire.Text = "L- R- All- Broadside-" segments.Hull.Text = "Hull -" segments.Speed.Text = "0 sps" return end segments.Ship.Text = ship.Name segments.Ship.TextColor3 = Color3.fromRGB(235, 240, 250) local leftCount = #Cache.LeftCannons local rightCount = #Cache.RightCannons local totalCannons = #Cache.AllCannons local extraCount = #Cache.ShipActivators local cannonText = string.format("Cannons %d (%dL/%dR", totalCannons, leftCount, rightCount) if extraCount > 0 then cannonText = cannonText .. string.format(" +%d other", extraCount) end segments.Cannons.Text = cannonText .. ")" segments.Cannons.TextColor3 = totalCannons > 0 and Color3.fromRGB(200, 215, 240) or Color3.fromRGB(240, 150, 150) -- Reload is shown ONLY when the ship actually exposes a loaded/cooldown value. -- The old fallback counted down a single configurable guess, which was wrong -- on every ship whose cannons have their own timing, so it is gone rather -- than showing a number that cannot be trusted. local liveValue, isLive = ShipHUD.readLiveReload() if not isLive then segments.Reload.Text = "Reload n/a" segments.Reload.TextColor3 = Color3.fromRGB(150, 160, 178) elseif liveValue == -1 then segments.Reload.Text = "RELOADING" segments.Reload.TextColor3 = Color3.fromRGB(255, 175, 80) elseif liveValue <= 0.05 then segments.Reload.Text = "READY!" segments.Reload.TextColor3 = Color3.fromRGB(120, 235, 150) else segments.Reload.Text = string.format("%.1fs", liveValue) segments.Reload.TextColor3 = Color3.fromRGB(255, 200, 100) end local function flag(label, on) return label .. (on and "+" or "-") end segments.AutoFire.Text = flag("L", State.isSpammingLeft) .. " " .. flag("R", State.isSpammingRight) .. " " .. flag("All", State.isSpammingAll) .. " " .. flag("Broadside", State.isAutoFireActive) local anyAuto = State.isSpammingLeft or State.isSpammingRight or State.isSpammingAll or State.isAutoFireActive segments.AutoFire.TextColor3 = anyAuto and Color3.fromRGB(120, 235, 150) or Color3.fromRGB(160, 172, 190) local currentParts, maxParts = getShipPartStats(ship) local lost = math.max(0, maxParts - currentParts) local hullText = string.format("Hull %d/%d", currentParts, maxParts) if lost > 0 then hullText = hullText .. string.format(" (-%d)", lost) end -- When the ship exposes a real health value it is worth showing alongside the -- part census; when it does not, getShipHealthData just echoes the part count. local _, maxHp, pct = getShipHealthData(ship) if maxHp > 0 and maxHp ~= maxParts then hullText = hullText .. string.format(" | HP %.0f%%", pct * 100) end segments.Hull.Text = hullText if lost > 0 and maxParts > 0 and (lost / maxParts) > 0.25 then segments.Hull.TextColor3 = Color3.fromRGB(240, 120, 120) elseif lost > 0 then segments.Hull.TextColor3 = Color3.fromRGB(255, 200, 100) else segments.Hull.TextColor3 = Color3.fromRGB(200, 215, 240) end local root = ship.PrimaryPart or ship:FindFirstChildWhichIsA("BasePart") local speed = root and root.AssemblyLinearVelocity.Magnitude or 0 segments.Speed.Text = string.format("%.0f sps", speed) end function ShipHUD.updateCannonCountLabel() if Runtime.closed or not UI.CannonCountLabel then return end if not CurrentShip or not CurrentShip.Parent then UI.CannonCountLabel.Text = "Not seated on a ship." UI.CannonCountLabel.TextColor3 = Color3.fromRGB(190, 200, 215) return end local extraNote = "" if #Cache.ShipActivators > 0 then extraNote = string.format("\n%d non-cannon activator(s) found (swivels, mortars, triggers). Fire ALL includes them while Include Non-Cannon Activators is ON.", #Cache.ShipActivators) end if (Cache.unplacedCannons or 0) > 0 then extraNote = extraNote .. string.format("\n%d cannon(s) had no resolvable position and are fired by both broadsides.", Cache.unplacedCannons) end local rate = math.clamp(tonumber(Config.FIRE_PER_SECOND) or 8.3, 0.5, 200) local depth = Fire.queueDepth() local queueNote = string.format("\nClick queue: %d waiting (%.1fs to clear) at %.1f clicks/sec.", depth, depth / rate, rate) if Fire.probed and not Fire.signature then queueNote = queueNote .. " No working fireclickdetector was found in this environment." end UI.CannonCountLabel.Text = string.format("%s: %d cannon(s) - %d left, %d right, %d fore, %d aft.%s%s", CurrentShip.Name, #Cache.AllCannons, #Cache.LeftCannons, #Cache.RightCannons, #Cache.FrontCannons, #Cache.RearCannons, extraNote, queueNote) UI.CannonCountLabel.TextColor3 = Color3.fromRGB(190, 200, 215) end task.spawn(function() while ScreenGui.Parent do pcall(ShipHUD.updateCannonCountLabel) if State.isShipHUDActive then ShipHUD.Frame.Visible = true pcall(ShipHUD.update) task.wait(0.15) else if ShipHUD.Frame.Visible then ShipHUD.Frame.Visible = false end task.wait(0.4) end end end) --// AUTO MAP ACTIVATOR LOOP -- Rewritten for even coverage. The old version spawned one coroutine per ship -- every 0.2s, each re-walking GetDescendants from scratch, so several passes ran -- concurrently and all of them restarted at the front of the list: the first few -- activators were fired constantly and the ones late in the tree were rarely -- reached. Now there is a single pass at a time, descendant walks are cached per -- ship, and every in-range activator joins the same paced click queue as the -- cannons instead of being clicked in a burst. -- Weak keys so destroyed ships drop out instead of accumulating for the match. Cache.mapActivatorLists = setmetatable({}, {__mode = "k"}) Cache.mapActivatorStamps = setmetatable({}, {__mode = "k"}) local function getShipMapActivators(ship) local now = tick() local cached = Cache.mapActivatorLists[ship] if cached and now - (Cache.mapActivatorStamps[ship] or 0) < 3 then return cached end local list = {} for _, obj in ipairs(ship:GetDescendants()) do if obj:IsA("ClickDetector") and isValidMapActivator(obj) then table.insert(list, obj) end end Cache.mapActivatorLists[ship] = list Cache.mapActivatorStamps[ship] = now return list end task.spawn(function() while ScreenGui.Parent do if State.isAutoMapFireActive and ShipsFolder then local candidates = {} local succeeded = pcall(function() local myPos = player.Character and player.Character:GetPivot().Position or nil if not myPos then return end local range = tonumber(Config.MAP_ACTIVATOR_RANGE) or 250 for _, ship in ipairs(ShipsFolder:GetChildren()) do if ship:IsA("Model") then for _, detector in ipairs(getShipMapActivators(ship)) do if detector.Parent then local objPos = getInstancePosition(detector.Parent) if objPos and (objPos - myPos).Magnitude <= range then table.insert(candidates, detector) end end end end end end) if succeeded and #candidates > 0 then pcall(Fire.enqueue, candidates) end task.wait(0.2) else table.clear(Cache.mapActivatorLists) table.clear(Cache.mapActivatorStamps) task.wait(0.5) end end end) --// FLAG SCAN UTILITY function FlagCapture.sideFromColor(color) if not color then return nil end local name = string.lower(tostring(color)) if string.find(name, "red", 1, true) or string.find(name, "crimson", 1, true) then return "red" end if string.find(name, "blue", 1, true) then return "blue" end return nil end function FlagCapture.classifyFlagSide(obj) -- Explicit team ownership and the flag's color take priority over island names. local current = obj for depth = 1, 3 do if not current then break end local teamColor = nil pcall(function() teamColor = current.TeamColor end) local side = FlagCapture.sideFromColor(teamColor) if side then return side end current = current.Parent end local colorSide = FlagCapture.sideFromColor(obj.BrickColor) if colorSide then return colorSide end local color = obj.Color if color then if color.R > color.G * 1.3 and color.R > color.B * 1.3 then return "red" end if color.B > color.R * 1.3 and color.B > color.G * 1.1 then return "blue" end end current = obj for depth = 1, 3 do if not current then break end local name = string.lower(current.Name) if depth == 1 or string.find(name, "flag", 1, true) then if string.find(name, "red", 1, true) or string.find(name, "pirate", 1, true) then return "red" end if string.find(name, "blue", 1, true) or string.find(name, "navy", 1, true) or string.find(name, "marine", 1, true) then return "blue" end end current = current.Parent end return "neutral" end function FlagCapture.isFlagPart(instance) if not instance:IsA("BasePart") then return false end if instance:IsA("Flag") or instance:IsA("FlagStand") then return true end local name = string.lower(instance.Name) if string.find(name, "flag", 1, true) then return true end local parent = instance.Parent if parent and string.find(string.lower(parent.Name), "flag", 1, true) then return name == "handle" or name == "cloth" or name == "banner" end return false end local function scanForFlags() local cache = { red = {}, blue = {}, neutral = {} } for _, obj in ipairs(workspace:GetDescendants()) do if FlagCapture.isFlagPart(obj) then if not isFlagHeld(obj) and not Cache.flagSpawns[obj] then Cache.flagSpawns[obj] = obj.CFrame end table.insert(cache[FlagCapture.classifyFlagSide(obj)], obj) end end return cache end local function isFortCapturedByPlayerTeam(fort, isRedTeam) if not fort or not fort.Parent then return true end if player.Team and fort.BrickColor == player.Team.TeamColor then return true end local bcName = string.lower(tostring(fort.BrickColor)) if isRedTeam and string.find(bcName, "red") then return true elseif not isRedTeam and string.find(bcName, "blue") then return true end local teamVal = fort:FindFirstChild("Team") or (fort.Parent and fort.Parent:FindFirstChild("Team")) if teamVal and teamVal:IsA("Team") and player.Team and teamVal.Value == player.Team then return true elseif teamVal and type(teamVal.Value) == "string" and player.Team and string.find(string.lower(teamVal.Value), string.lower(player.Team.Name)) then return true end return false end --// AUTO CTF LOOP FlagCapture.captures = 0 FlagCapture.runTeamKey = nil FlagCapture.capturePoints = {} FlagCapture.homePositions = {} FlagCapture.flagHomes = {} FlagCapture.flags = {red = {}, blue = {}, neutral = {}} FlagCapture.lastScan = -math.huge FlagCapture.phase = "Idle" FlagCapture.phaseStarted = 0 FlagCapture.target = nil FlagCapture.teamKey = nil FlagCapture.character = nil FlagCapture.home = nil FlagCapture.lastNudge = 0 FlagCapture.pickupSeconds = 0.15 FlagCapture.pickupMoves = 0 FlagCapture.lastPickupAt = 0 FlagCapture.captureTimeout = 12 FlagCapture.mode = "Unknown" FlagCapture.modeReason = "No scan yet." FlagCapture.targetSide = nil FlagCapture.baseCache = nil FlagCapture.baseCacheTime = -math.huge function FlagCapture.setStatus(message) if not Runtime.closed and UI.CTFStatusLabel then UI.CTFStatusLabel.Text = message end end function FlagCapture.getTeamKey() if not player.Team or player.Neutral then return nil end return tostring(player.Team.TeamColor.Number) .. ":" .. player.Team.Name end function FlagCapture.getTeamSide() if not player.Team or player.Neutral then return nil end local colorName = string.lower(tostring(player.Team.TeamColor)) local teamName = string.lower(player.Team.Name) if string.find(colorName, "red", 1, true) then return "red" elseif string.find(colorName, "blue", 1, true) then return "blue" elseif string.find(teamName, "red", 1, true) or string.find(teamName, "pirate", 1, true) then return "red" elseif string.find(teamName, "blue", 1, true) or string.find(teamName, "navy", 1, true) or string.find(teamName, "marine", 1, true) then return "blue" end return nil end function FlagCapture.resetCycle() FlagCapture.deliveryAttempt = nil FlagCapture.phase = "Idle" FlagCapture.target = nil FlagCapture.targetSide = nil FlagCapture.home = nil FlagCapture.teamKey = nil FlagCapture.character = nil FlagCapture.carriedSince = nil FlagCapture.deliveryHadFlag = false FlagCapture.reacquireAt = 0 end --// TEAM BASES & MAP CENTRE -- Used to tell a centre flag apart from a base flag, which is the whole -- difference between a neutral round and a two-flag round. function FlagCapture.teamBases() local now = os.clock() if FlagCapture.baseCache and now - FlagCapture.baseCacheTime < 10 then return FlagCapture.baseCache end local sums = {} local counts = {} for _, instance in ipairs(workspace:GetDescendants()) do local side = nil if instance:IsA("SpawnLocation") and not instance.Neutral then local colorName = string.lower(tostring(instance.TeamColor)) local objectName = string.lower(instance.Name) if string.find(colorName, "red", 1, true) or string.find(objectName, "red", 1, true) or string.find(objectName, "pirate", 1, true) then side = "red" elseif string.find(colorName, "blue", 1, true) or string.find(objectName, "blue", 1, true) or string.find(objectName, "navy", 1, true) then side = "blue" end elseif instance:IsA("FlagStand") then local colorName = string.lower(tostring(instance.TeamColor)) if string.find(colorName, "red", 1, true) then side = "red" elseif string.find(colorName, "blue", 1, true) then side = "blue" end end if side then sums[side] = (sums[side] or Vector3.zero) + instance.Position counts[side] = (counts[side] or 0) + 1 end end local bases = {} for side, total in pairs(sums) do bases[side] = total / counts[side] end FlagCapture.baseCache = bases FlagCapture.baseCacheTime = now return bases end function FlagCapture.mapCenter() local bases = FlagCapture.teamBases() if bases.red and bases.blue then return (bases.red + bases.blue) / 2 end return nil end function FlagCapture.clearBaseCache() FlagCapture.baseCache = nil FlagCapture.baseCacheTime = -math.huge end --// MODE DETECTION function FlagCapture.detectMode() if Config.CTF_MODE_OVERRIDE == "TwoFlag" then return "TwoFlag", "Targeting the enemy team's live flag." end if Config.CTF_MODE_OVERRIDE == "Neutral" then return "Neutral", "Targeting the live neutral flag wherever it spawned." end local counts = {red = 0, blue = 0, neutral = 0} for _, flags in pairs(FlagCapture.flags) do for _, flag in ipairs(flags) do if FlagCapture.isCarryableFlag(flag) then local side = FlagCapture.classifyFlagSide(flag) counts[side] = counts[side] + 1 end end end if counts.red > 0 or counts.blue > 0 then return "TwoFlag", "Team-colored flags are present; their location does not determine the mode." end if counts.neutral > 0 then return "Neutral", "An unowned flag is present; searching its current position." end return "Unknown", "No live carryable flag is visible yet." end function FlagCapture.cycleModeOverride() local order = {"Auto", "TwoFlag", "Neutral"} local index = table.find(order, Config.CTF_MODE_OVERRIDE) or 1 Config.CTF_MODE_OVERRIDE = order[(index % #order) + 1] FlagCapture.homePositions = {} FlagCapture.clearBaseCache() FlagCapture.resetCycle() FlagCapture.lastScan = -math.huge if refreshCTFModeUI then refreshCTFModeUI() end return Config.CTF_MODE_OVERRIDE end function refreshCTFModeUI() if Runtime.closed or not UI.CTFModeBtn then return end local labels = {Auto = "Auto", TwoFlag = "Two-Flag (take enemy flag)", Neutral = "Neutral (take live neutral flag)"} UI.CTFModeBtn.Text = "CTF Mode: " .. (labels[Config.CTF_MODE_OVERRIDE] or "Auto") UI.CTFModeBtn.BackgroundColor3 = Config.CTF_MODE_OVERRIDE == "Auto" and Color3.fromRGB(55, 55, 65) or Color3.fromRGB(60, 90, 160) end function FlagCapture.saveCapturePoint() local teamKey = FlagCapture.getTeamKey() if not teamKey then notifyUser("Auto CTF", "Join a team before saving its capture point.", 5) return end local character = player.Character if not character then return end local rootPart = character:FindFirstChild("HumanoidRootPart") if not rootPart then return end FlagCapture.capturePoints[teamKey] = {placeId = game.PlaceId, cframe = rootPart.CFrame} FlagCapture.resetCycle() FlagCapture.setStatus("Exact capture point saved for " .. player.Team.Name .. ".") notifyUser("Auto CTF", "Capture point saved for your team. Save Config Settings to keep it next session.", 6) end function FlagCapture.exportCapturePoints() local savedPoints = {} for teamKey, point in pairs(FlagCapture.capturePoints) do savedPoints[teamKey] = SavedPositions.encode(point) end return savedPoints end function FlagCapture.loadCapturePoints(data) if data == nil then return 0 end if type(data) ~= "table" then return 1 end local invalid = 0 for teamKey, pointData in pairs(data) do local point, valid = SavedPositions.decode(pointData) if type(teamKey) == "string" and valid then FlagCapture.capturePoints[teamKey] = point else invalid = invalid + 1 end end FlagCapture.resetCycle() return invalid end function FlagCapture.isWorldFlag(part) if not part or not part.Parent or not part:IsA("BasePart") then return false end if not part:IsDescendantOf(workspace) then return false end if ShipsFolder and part:IsDescendantOf(ShipsFolder) then return false end return true end -- Is this a flag a player can pick up and run with, as opposed to a stand, pole, -- base marker or scoring zone? Mode detection needs this answer without also -- caring whether the flag happens to be held right now. FlagCapture.nonCarryableWords = { "stand", "pole", "base", "spawn", "capture", "zone", "point", "fort", "podium", "holder", "mount", "marker", "plate", "pad", } function FlagCapture.isCarryableFlag(part) if not FlagCapture.isWorldFlag(part) or part:IsA("FlagStand") then return false end local name = string.lower(part.Name) for _, word in ipairs(FlagCapture.nonCarryableWords) do if string.find(name, word, 1, true) then return false end end return true end -- Relaxed pass. The strict word list above can reject a legitimately carryable -- flag that happens to be called something like "NeutralFlagPole", so when the -- strict pass finds nothing we fall back to "any unheld world flag that is not -- an actual FlagStand" rather than giving up and idling at spawn. function FlagCapture.isLooseFlagCandidate(part) return FlagCapture.isWorldFlag(part) and not part:IsA("FlagStand") and not isFlagHeld(part) end function FlagCapture.isPickupTarget(part) return FlagCapture.isCarryableFlag(part) and not isFlagHeld(part) end function FlagCapture.refreshFlags(now) if now - FlagCapture.lastScan < 2 then return end FlagCapture.lastScan = now FlagCapture.flags = scanForFlags() FlagCapture.rememberFlagHomes() -- A disappearing or carried flag must not restart an in-progress pickup. if FlagCapture.phase == "PickUp" or FlagCapture.phase == "ReturnHome" or FlagCapture.phase == "Deliver" or FlagCapture.phase == "Cooldown" then return end local mode, reason = FlagCapture.detectMode() FlagCapture.modeReason = reason if mode ~= FlagCapture.mode then local previous = FlagCapture.mode FlagCapture.mode = mode FlagCapture.target = nil FlagCapture.targetSide = nil -- The scoring spot can differ between rounds, so re-resolve it. FlagCapture.home = nil FlagCapture.homePositions = {} FlagCapture.clearBaseCache() if FlagCapture.phase ~= "Idle" and FlagCapture.phase ~= "Deliver" then FlagCapture.phase = "FindFlag" end if previous ~= "Unknown" or mode ~= "Unknown" then Toast.push("CTF mode: " .. mode, reason, Toast.INFO, 3.5) end end end function FlagCapture.findCarriedFlag(character) if isFlagHeldByMe(FlagCapture.target, character) then return FlagCapture.target end for _, flags in pairs(FlagCapture.flags) do for _, flag in ipairs(flags) do if isFlagHeldByMe(flag, character) then return flag end end end for _, instance in ipairs(character:GetDescendants()) do if instance:IsA("Tool") and string.find(string.lower(instance.Name), "flag", 1, true) then local handle = instance:FindFirstChild("Handle") if handle and handle:IsA("BasePart") then return handle end end end return nil end function FlagCapture.standingCFrame(originalCFrame, rootPart, humanoid) local ignored = {player.Character, espContainer} for _, flags in pairs(FlagCapture.flags) do for _, candidate in ipairs(flags) do table.insert(ignored, candidate) end end local parameters = RaycastParams.new() parameters.FilterType = Enum.RaycastFilterType.Exclude parameters.FilterDescendantsInstances = ignored local origin = originalCFrame.Position + Vector3.new(0, 8, 0) local ground = workspace:Raycast(origin, Vector3.new(0, -80, 0), parameters) if ground then local standingHeight = humanoid.HipHeight + rootPart.Size.Y / 2 local leftLeg = player.Character:FindFirstChild("Left Leg") if leftLeg and leftLeg:IsA("BasePart") then standingHeight = standingHeight + leftLeg.Size.Y end return CFrame.new(ground.Position + Vector3.new(0, standingHeight + 0.1, 0)) * rootPart.CFrame.Rotation end return CFrame.new(originalCFrame.Position) * rootPart.CFrame.Rotation end function FlagCapture.rememberFlagHomes() local spawns = {red = {}, blue = {}} for _, instance in ipairs(workspace:GetDescendants()) do if instance:IsA("SpawnLocation") and not instance.Neutral then local side = FlagCapture.sideFromColor(instance.TeamColor) if side then table.insert(spawns[side], instance.Position) end end end local candidates = {} for _, flags in pairs(FlagCapture.flags) do for _, flag in ipairs(flags) do local side = FlagCapture.classifyFlagSide(flag) if side ~= "neutral" and FlagCapture.isWorldFlag(flag) then local originalCFrame = Cache.flagSpawns[flag] if not originalCFrame and not isFlagHeld(flag) then originalCFrame = flag.CFrame Cache.flagSpawns[flag] = originalCFrame end local priority = 0 if flag:IsA("FlagStand") then priority = 2 elseif FlagCapture.isCarryableFlag(flag) then priority = 1 end if originalCFrame and priority > 0 then local distance = math.huge for _, spawnPosition in ipairs(spawns[side]) do distance = math.min(distance, (originalCFrame.Position - spawnPosition).Magnitude) end local previous = candidates[side] if not previous or priority > previous.priority or (priority == previous.priority and distance < previous.distance) then candidates[side] = { cframe = originalCFrame, part = flag, priority = priority, distance = distance, } end end end end end for side, candidate in pairs(candidates) do local previous = FlagCapture.flagHomes[side] if not previous or candidate.priority > previous.priority then FlagCapture.flagHomes[side] = candidate table.clear(FlagCapture.homePositions) FlagCapture.home = nil end end end function FlagCapture.resolveHome(teamKey, teamSide, rootPart, humanoid) local savedPoint = FlagCapture.capturePoints[teamKey] if savedPoint and savedPoint.placeId == game.PlaceId then return {cframe = savedPoint.cframe, exact = true, source = "your saved capture point"} end if FlagCapture.homePositions[teamKey] then return FlagCapture.homePositions[teamKey] end FlagCapture.rememberFlagHomes() local flagHome = FlagCapture.flagHomes[teamSide] if not flagHome then return nil end local source = "your team's flag starting position" if flagHome.priority == 2 then source = "your team's flag stand" end local home = { cframe = FlagCapture.standingCFrame(flagHome.cframe, rootPart, humanoid), part = flagHome.part, source = source, } FlagCapture.homePositions[teamKey] = home return home end -- Picks what to go and grab. In Neutral mode it will ONLY consider unowned -- flags, so it can no longer run at the enemy base flag / capture point. function FlagCapture.chooseTarget(teamSide, rootPart, mode) local enemySide = "red" if teamSide == "red" then enemySide = "blue" end for _, strict in ipairs({true, false}) do local target = nil local targetSide = nil local nearestDistance = math.huge for _, flags in pairs(FlagCapture.flags) do for _, flag in ipairs(flags) do local accepted if strict then accepted = FlagCapture.isPickupTarget(flag) else accepted = FlagCapture.isLooseFlagCandidate(flag) end if accepted then local side = FlagCapture.classifyFlagSide(flag) local wanted = side == enemySide if mode == "Neutral" then wanted = side == "neutral" elseif mode == "Unknown" then wanted = side == enemySide or side == "neutral" end if wanted then local distance = (flag.Position - rootPart.Position).Magnitude if distance < nearestDistance then target = flag targetSide = side nearestDistance = distance end end end end end if target then return target, targetSide, not strict end end return nil, nil, false end -- Who is holding the flag we want? Used for the status text and the optional -- "chase the carrier" behaviour. function FlagCapture.findFlagCarrier(mode, teamSide) local enemySide = teamSide == "red" and "blue" or "red" local sides = mode == "Neutral" and {"neutral"} or {enemySide, "neutral"} for _, side in ipairs(sides) do for _, flag in ipairs(FlagCapture.flags[side] or {}) do if FlagCapture.isCarryableFlag(flag) and isFlagHeld(flag) then for _, other in ipairs(Players:GetPlayers()) do if other ~= player and other.Character and isFlagHeldByMe(flag, other.Character) then local otherRoot = other.Character:FindFirstChild("HumanoidRootPart") if otherRoot then return other, otherRoot, flag end end end end end end return nil, nil, nil end function FlagCapture.moveRoot(rootPart, targetCFrame) rootPart.CFrame = targetCFrame rootPart.AssemblyLinearVelocity = Vector3.zero rootPart.AssemblyAngularVelocity = Vector3.zero end function FlagCapture.touch(part, target) if typeof(firetouchinterest) ~= "function" or not part or not target or not target.Parent then return end pcall(function() firetouchinterest(part, target, 0) firetouchinterest(part, target, 1) end) end function FlagCapture.cleanText(value) local text = tostring(value or "") text = string.gsub(text, "<[^>]*>", "") return string.lower(text) end function FlagCapture.textSide(text) local red = string.find(text, "red", 1, true) or string.find(text, "pirate", 1, true) local blue = string.find(text, "blue", 1, true) or string.find(text, "navy", 1, true) or string.find(text, "marine", 1, true) if red and not blue then return "red" end if blue and not red then return "blue" end return nil end function FlagCapture.readScore(source) local succeeded, value = pcall(function() if not source.object.Parent then return nil end local raw = source.object[source.property] if type(raw) == "number" then return raw end local text = FlagCapture.cleanText(raw) text = string.gsub(text, "(%d),(%d)", "%1%2") local number = tonumber(text) if number then return number end local words = {"red", "pirates"} if source.side == "blue" then words = {"blue", "navy", "marines"} end for _, word in ipairs(words) do local _, last = string.find(text, word, 1, true) if last then local digits = string.match(string.sub(text, last + 1), "^[%s:%-]*(%d+)") if digits then return tonumber(digits) end end end if string.find(text, "score", 1, true) or string.find(text, "flag", 1, true) or string.find(text, "captur", 1, true) then return tonumber(string.match(text, "%d+")) end return nil end) if succeeded and type(value) == "number" and value >= 0 and value < 100000000 and value == math.floor(value) then return value end return nil end function FlagCapture.scoreCandidate(instance, ownTeam) local isText = instance:IsA("TextLabel") or instance:IsA("TextButton") local isValue = instance:IsA("IntValue") or instance:IsA("NumberValue") or instance:IsA("StringValue") if not isText and not isValue then return nil end local side = nil local scoreName = false local current = instance for depth = 1, 7 do if not current then break end local name = FlagCapture.cleanText(current.Name) for _, excluded in ipairs({"debugutility", "chat", "leaderboard", "playerlist", "health", "gold", "cash", "kills", "level", "timer"}) do if string.find(name, excluded, 1, true) then return nil end end if not side then side = FlagCapture.textSide(name) end if string.find(name, "score", 1, true) or string.find(name, "flag", 1, true) or string.find(name, "captur", 1, true) then scoreName = true end if current:IsA("GuiObject") and not current.Visible then return nil end if current:IsA("ScreenGui") and not current.Enabled then return nil end current = current.Parent end local teamSide = FlagCapture.getTeamSide() local priority = 0 if ownTeam then if not scoreName then return nil end side = teamSide priority = 30 elseif isText then local text = FlagCapture.cleanText(instance.Text) if string.find(text, "score", 1, true) or string.find(text, "flag", 1, true) or string.find(text, "captur", 1, true) then scoreName = true end if not side then side = FlagCapture.textSide(text) end local camera = workspace.CurrentCamera local topBar = camera and instance.AbsolutePosition.Y <= camera.ViewportSize.Y * 0.3 if not side and topBar then current = instance for depth = 1, 3 do if not current or not current:IsA("GuiObject") then break end local color = current.BackgroundColor3 if depth == 1 then color = instance.TextColor3 end if color.R > color.G * 1.4 and color.R > color.B * 1.4 then side = "red" break end if color.B > color.R * 1.4 and color.B > color.G * 1.1 then side = "blue" break end current = current.Parent end end if not scoreName and not topBar then return nil end priority = 10 if scoreName then priority = 20 end else return nil end if side ~= teamSide then return nil end local property = "Value" if isText then property = "Text" end local source = {object = instance, property = property, side = side, priority = priority} if FlagCapture.readScore(source) == nil then return nil end return source end function FlagCapture.findScoreSource() local best = nil local roots = {{object = player:FindFirstChildOfClass("PlayerGui"), ownTeam = false}, {object = player.Team, ownTeam = true}} for _, root in ipairs(roots) do if root.object then local succeeded, descendants = pcall(function() return root.object:GetDescendants() end) if succeeded then for _, instance in ipairs(descendants) do local accepted, source = pcall(FlagCapture.scoreCandidate, instance, root.ownTeam) if accepted and source and (not best or source.priority > best.priority) then best = source end end end end end return best end function FlagCapture.resetCaptureTracking() FlagCapture.captureAttempts = {} FlagCapture.deliveryAttempt = nil FlagCapture.scoreSource = nil FlagCapture.scoreLastValue = nil FlagCapture.lastScoreScan = -math.huge FlagCapture.otherScoreDebt = 0 end function FlagCapture.recordCapture(attempt, source) if not attempt or attempt.counted then return end attempt.counted = true attempt.confirmedBy = source FlagCapture.captures = FlagCapture.captures + 1 FlagCapture.refreshProgress() if UI.CTFCounterStatus then UI.CTFCounterStatus.Text = "Last capture confirmed by " .. source .. "." end end function FlagCapture.pollCaptureScore(now) if not FlagCapture.captureAttempts then FlagCapture.resetCaptureTracking() end local source = FlagCapture.scoreSource local value = nil if source then value = FlagCapture.readScore(source) end if value == nil then if now - FlagCapture.lastScoreScan < 2 then return end FlagCapture.lastScoreScan = now source = FlagCapture.findScoreSource() FlagCapture.scoreSource = source FlagCapture.scoreLastValue = nil if not source then if UI.CTFCounterStatus then UI.CTFCounterStatus.Text = "Listening for capture messages; waiting to find the top-bar team score." end return end value = FlagCapture.readScore(source) if UI.CTFCounterStatus then UI.CTFCounterStatus.Text = "Watching capture messages and your team's score." end end local previous = FlagCapture.scoreLastValue FlagCapture.scoreLastValue = value if previous == nil or value == nil or value <= previous then if previous and value and value < previous then FlagCapture.otherScoreDebt = 0 end return end local increase = value - previous local otherCaptures = math.min(increase, FlagCapture.otherScoreDebt) FlagCapture.otherScoreDebt = FlagCapture.otherScoreDebt - otherCaptures increase = increase - otherCaptures for _, attempt in ipairs(FlagCapture.captureAttempts) do if increase <= 0 then break end if attempt.counted and not attempt.scoreConfirmed and now - attempt.startedAt < 8 then attempt.scoreConfirmed = true increase = increase - 1 end end local attempt = FlagCapture.deliveryAttempt if increase > 0 and attempt and attempt.returnedAt and not attempt.counted and FlagCapture.phase == "Deliver" then attempt.scoreConfirmed = true FlagCapture.recordCapture(attempt, "the team score") end end function FlagCapture.beginCaptureAttempt(now) FlagCapture.pollCaptureScore(now) local attempt = {startedAt = now, counted = false} FlagCapture.deliveryAttempt = attempt table.insert(FlagCapture.captureAttempts, attempt) while #FlagCapture.captureAttempts > 30 do table.remove(FlagCapture.captureAttempts, 1) end end function FlagCapture.containsPlayerName(text, name) if not name or name == "" then return false end name = string.lower(name) local first, last = string.find(text, name, 1, true) while first do local before = string.sub(text, first - 1, first - 1) local after = string.sub(text, last + 1, last + 1) if not string.match(before, "[%w_]") and not string.match(after, "[%w_]") then return true end first, last = string.find(text, name, last + 1, true) end return false end function FlagCapture.onCaptureMessage(rawMessage) if Runtime.closed or not State.isAutoCTFActive or State.isCTFHogFlagActive then return end local text = FlagCapture.cleanText(rawMessage) if not string.find(text, "flag", 1, true) then return end local verb = string.find(text, "%f[%a]captured%f[%A]") or string.find(text, "%f[%a]captures%f[%A]") or string.find(text, "%f[%a]scored%f[%A]") or string.find(text, "%f[%a]capped%f[%A]") if not verb then return end local actor = string.sub(text, 1, verb - 1) local by = string.find(text, " by ", verb, true) if by then actor = actor .. " " .. string.sub(text, by + 4) end local mine = FlagCapture.containsPlayerName(actor, player.Name) or FlagCapture.containsPlayerName(actor, player.DisplayName) local otherPlayer = false if not mine then for _, candidate in ipairs(Players:GetPlayers()) do if candidate ~= player and (FlagCapture.containsPlayerName(actor, candidate.Name) or FlagCapture.containsPlayerName(actor, candidate.DisplayName)) then otherPlayer = true if candidate.Team == player.Team then FlagCapture.otherScoreDebt = (FlagCapture.otherScoreDebt or 0) + 1 end break end end end if otherPlayer then return end if not mine and FlagCapture.textSide(actor) ~= FlagCapture.getTeamSide() then return end local now = os.clock() for _, attempt in ipairs(FlagCapture.captureAttempts or {}) do if attempt.counted and not attempt.chatConfirmed and attempt.returnedAt and now - attempt.startedAt < 8 then attempt.chatConfirmed = true return end end local attempt = FlagCapture.deliveryAttempt if attempt and (mine or attempt.returnedAt) and not attempt.chatConfirmed and now - attempt.startedAt < 8 then attempt.chatConfirmed = true FlagCapture.recordCapture(attempt, "the capture message") end end function FlagCapture.refreshProgress() if Runtime.closed or not UI.CTFCapturedLabel then return end local limitText = "Unlimited" if Config.CTF_CAPTURE_LIMIT > 0 then limitText = tostring(Config.CTF_CAPTURE_LIMIT) end UI.CTFCapturedLabel.Text = "Captured this run: " .. tostring(FlagCapture.captures) .. " / " .. limitText end function FlagCapture.resetRun() FlagCapture.resetCaptureTracking() FlagCapture.captures = 0 FlagCapture.runTeamKey = nil FlagCapture.resetCycle() FlagCapture.refreshProgress() end function FlagCapture.limitReached() return Config.CTF_CAPTURE_LIMIT > 0 and FlagCapture.captures >= Config.CTF_CAPTURE_LIMIT end function FlagCapture.shouldHold() if State.isCTFHogFlagActive then return true end return State.isAutoCTFActive and State.isCTFLockAtLimitActive and FlagCapture.limitReached() end function FlagCapture.setEnabled(active) FlagCapture.returnFromHoldTrip() State.isAutoCTFActive = active == true if State.isAutoCTFActive then State.isCTFHogFlagActive = false end FlagCapture.resetCycle() FlagCapture.resetHolding() if State.isAutoCTFActive then FlagCapture.resetRun() local character = player.Character local rootPart = character and character:FindFirstChild("HumanoidRootPart") FlagCapture.runReturnCharacter = character FlagCapture.runReturnCFrame = nil if rootPart then FlagCapture.runReturnCFrame = rootPart.CFrame end FlagCapture.lastScan = -math.huge FlagCapture.setStatus("Finding the live flag and your friendly flag's starting position.") else FlagCapture.setStatus("Auto CTF stopped at your current position.") end FlagCapture.refreshToggleButtons() end function FlagCapture.stop(message) State.isCTFHogFlagActive = false FlagCapture.setEnabled(false) FlagCapture.setStatus(message) notifyUser("Auto CTF", message, 8) end function FlagCapture.resetHolding() FlagCapture.hold = {phase = "Ready", nextGrabAt = 0} end function FlagCapture.refreshToggleButtons() for _, entry in ipairs({ {UI.AutoCTFBtn, "Auto CTF", State.isAutoCTFActive}, {UI.CTFHogFlagBtn, "Hold Flag / No Capture", State.isCTFHogFlagActive}, }) do local status = "OFF" local color = Color3.fromRGB(170, 50, 50) if entry[3] then status = "ON" color = Color3.fromRGB(0, 170, 0) end entry[1].Text = entry[2] .. ": " .. status entry[1].BackgroundColor3 = color end end function FlagCapture.returnFromHoldTrip() local hold = FlagCapture.hold if not hold or not hold.returnCFrame or hold.character ~= player.Character then return end local rootPart = hold.character:FindFirstChild("HumanoidRootPart") local humanoid = hold.character:FindFirstChildOfClass("Humanoid") if rootPart and humanoid and humanoid.Health > 0 then FlagCapture.moveRoot(rootPart, hold.returnCFrame) end hold.returnCFrame = nil end function FlagCapture.setHoldEnabled(active) FlagCapture.returnFromHoldTrip() State.isCTFHogFlagActive = active == true if State.isCTFHogFlagActive then State.isAutoCTFActive = false end FlagCapture.resetCycle() FlagCapture.resetHolding() FlagCapture.refreshToggleButtons() if State.isCTFHogFlagActive then FlagCapture.lastScan = -math.huge FlagCapture.setStatus("Taking the live flag once, then returning to your position and waiting for the hold timer.") else FlagCapture.setStatus("Hold Flag stopped. Your position is free to move normally.") end end function FlagCapture.holdReturnPoint(rootPart, humanoid, locked) local returnCFrame = rootPart.CFrame if not locked then return returnCFrame end if not FlagCapture.hold.returnedOnce and FlagCapture.runReturnCharacter == player.Character and FlagCapture.runReturnCFrame then returnCFrame = FlagCapture.runReturnCFrame end local home = FlagCapture.resolveHome(FlagCapture.getTeamKey(), FlagCapture.getTeamSide(), rootPart, humanoid) if home then local difference = returnCFrame.Position - home.cframe.Position local horizontal = Vector3.new(difference.X, 0, difference.Z) if horizontal.Magnitude < Config.CTF_LOCK_HOLD_DISTANCE then local direction = Vector3.new(1, 0, 0) if horizontal.Magnitude > 1 then direction = horizontal.Unit end local position = home.cframe.Position + direction * Config.CTF_LOCK_HOLD_DISTANCE returnCFrame = FlagCapture.standingCFrame(CFrame.new(position), rootPart, humanoid) end end return returnCFrame end function FlagCapture.finishHoldTrip(now) FlagCapture.returnFromHoldTrip() local hold = FlagCapture.hold hold.phase = "Wait" hold.returnedOnce = true hold.target = nil hold.nextGrabAt = now + Config.CTF_HOLD_REACQUIRE_SECONDS FlagCapture.setStatus(string.format("Returned to your position. Next flag pickup in %.0f seconds; you can move normally.", Config.CTF_HOLD_REACQUIRE_SECONDS)) end function FlagCapture.stepHolding(now) if not FlagCapture.hold then FlagCapture.resetHolding() end local hold = FlagCapture.hold if hold.phase == "Wait" then if now < hold.nextGrabAt then FlagCapture.setStatus(string.format("Hold Flag: next pickup in %.0f seconds. You can move normally.", hold.nextGrabAt - now)) return end hold.phase = "Ready" FlagCapture.lastScan = -math.huge end local character = player.Character local rootPart = character and character:FindFirstChild("HumanoidRootPart") local humanoid = character and character:FindFirstChildOfClass("Humanoid") local teamKey = FlagCapture.getTeamKey() local teamSide = FlagCapture.getTeamSide() if hold.phase == "PickUp" and (hold.character ~= character or hold.teamKey ~= teamKey or not humanoid or humanoid.Health <= 0) then hold.returnCFrame = nil FlagCapture.finishHoldTrip(now) return end if not rootPart or not humanoid or humanoid.Health <= 0 or not teamKey or not teamSide then FlagCapture.setStatus("Hold Flag is waiting for your character and team.") return end if rootPart.Anchored then FlagCapture.setStatus("Hold Flag is waiting for your character to be movable.") return end if humanoid.SeatPart or humanoid.Sit then humanoid.Sit = false humanoid.Jump = true return end if hold.phase == "PickUp" then local carried = FlagCapture.findCarriedFlag(character) if carried or not hold.target or not hold.target.Parent then FlagCapture.finishHoldTrip(now) return end if now - hold.lastPickupAt < FlagCapture.pickupSeconds then return end local attempts = math.clamp(math.floor(Config.CTF_PICKUP_ATTEMPTS), 1, 2) if hold.pickupMoves >= attempts or not FlagCapture.isLooseFlagCandidate(hold.target) then FlagCapture.finishHoldTrip(now) return end FlagCapture.moveRoot(rootPart, CFrame.new(hold.target.Position)) FlagCapture.touch(rootPart, hold.target) hold.pickupMoves = hold.pickupMoves + 1 hold.lastPickupAt = now return end FlagCapture.refreshFlags(now) if FlagCapture.findCarriedFlag(character) then hold.phase = "Wait" hold.nextGrabAt = now + Config.CTF_HOLD_REACQUIRE_SECONDS FlagCapture.setStatus("Already carrying a flag. Waiting for the hold timer without moving you.") return end local target, side = FlagCapture.chooseTarget(teamSide, rootPart, FlagCapture.mode) if not target then FlagCapture.setStatus("Hold Flag is waiting for the live flag to reset or become visible.") return end local locked = State.isAutoCTFActive and State.isCTFLockAtLimitActive and FlagCapture.limitReached() hold.returnCFrame = FlagCapture.holdReturnPoint(rootPart, humanoid, locked) hold.character = character hold.teamKey = teamKey hold.target = target hold.phase = "PickUp" hold.pickupMoves = 1 hold.lastPickupAt = now FlagCapture.moveRoot(rootPart, CFrame.new(target.Position)) FlagCapture.touch(rootPart, target) FlagCapture.setStatus("Taking the live " .. tostring(side) .. " flag, then returning to your position.") end function FlagCapture.completeDelivery(now) local attempt = FlagCapture.deliveryAttempt local confirmed = attempt and attempt.counted FlagCapture.deliveryHadFlag = false FlagCapture.carriedSince = nil FlagCapture.target = nil FlagCapture.targetSide = nil FlagCapture.lastScan = -math.huge if FlagCapture.limitReached() then if State.isCTFLockAtLimitActive then FlagCapture.phase = "FindFlag" FlagCapture.setStatus("Capture limit reached. Taking the next flag to hold it without scoring.") else FlagCapture.stop("Capture limit reached: " .. tostring(FlagCapture.captures) .. " flags. Auto CTF stopped.") end return end FlagCapture.phase = "Cooldown" FlagCapture.phaseStarted = now if confirmed then FlagCapture.setStatus(string.format("Capture confirmed. Waiting %.2fs before the next pickup.", Config.CTF_CAPTURE_WAIT)) else FlagCapture.setStatus("No capture message or score increase was confirmed. Retrying without adding to the count.") end end function FlagCapture.step(now) if ColaDelivery.active then return end if Runtime.closed or (not State.isAutoCTFActive and not State.isCTFHogFlagActive) then FlagCapture.resetHolding() if FlagCapture.phase ~= "Idle" then FlagCapture.resetCycle() FlagCapture.setStatus("Auto CTF stopped at your current position.") end return end if State.isCTFHogFlagActive or (FlagCapture.shouldHold() and FlagCapture.runTeamKey == FlagCapture.getTeamKey()) then FlagCapture.stepHolding(now) return end local character = player.Character local rootPart = character and character:FindFirstChild("HumanoidRootPart") local humanoid = character and character:FindFirstChildOfClass("Humanoid") if not character or not rootPart or not humanoid or humanoid.Health <= 0 then FlagCapture.resetCycle() return end if humanoid.SeatPart or humanoid.Sit then humanoid.Sit = false FlagCapture.setStatus("Leaving the seat before starting CTF.") return end if rootPart.Anchored then FlagCapture.setStatus("Waiting for your character to be movable. Exit freecam if it is enabled.") return end local teamKey = FlagCapture.getTeamKey() local teamSide = FlagCapture.getTeamSide() if not teamKey or not teamSide then FlagCapture.resetCycle() FlagCapture.setStatus("Waiting for a red/pirate or blue/navy team.") return end if FlagCapture.runTeamKey ~= teamKey then FlagCapture.captures = 0 FlagCapture.resetCaptureTracking() FlagCapture.runTeamKey = teamKey FlagCapture.refreshProgress() end FlagCapture.refreshFlags(now) if FlagCapture.teamKey ~= teamKey or FlagCapture.character ~= character then FlagCapture.resetCycle() FlagCapture.teamKey = teamKey FlagCapture.character = character end if FlagCapture.shouldHold() then FlagCapture.stepHolding(now) return end if FlagCapture.hold and FlagCapture.hold.phase ~= "Ready" then FlagCapture.returnFromHoldTrip() FlagCapture.resetHolding() FlagCapture.resetCycle() return end if FlagCapture.limitReached() then FlagCapture.stop("Capture limit reached: " .. tostring(FlagCapture.captures) .. " flags. Auto CTF stopped.") return end local mode = FlagCapture.mode if not FlagCapture.home then FlagCapture.home = FlagCapture.resolveHome(teamKey, teamSide, rootPart, humanoid) if not FlagCapture.home then FlagCapture.setStatus("Your friendly flag's starting position is not visible yet. Go near that flag or use Set My Capture Point on the scoring spot.") return end end FlagCapture.pollCaptureScore(now) local carriedFlag = FlagCapture.findCarriedFlag(character) if carriedFlag and not FlagCapture.carriedSince then FlagCapture.carriedSince = now end if FlagCapture.phase == "Cooldown" then if carriedFlag then FlagCapture.phase = "ReturnHome" elseif now - FlagCapture.phaseStarted >= Config.CTF_CAPTURE_WAIT then FlagCapture.phase = "FindFlag" FlagCapture.lastScan = -math.huge FlagCapture.refreshFlags(now) mode = FlagCapture.mode if not FlagCapture.home then return end else return end end if FlagCapture.phase == "Idle" or FlagCapture.phase == "FindFlag" then if carriedFlag then FlagCapture.target = carriedFlag FlagCapture.beginCaptureAttempt(now) FlagCapture.phase = "ReturnHome" else local relaxed FlagCapture.target, FlagCapture.targetSide, relaxed = FlagCapture.chooseTarget(teamSide, rootPart, mode) if not FlagCapture.target then FlagCapture.phase = "FindFlag" local carrier, carrierRoot = FlagCapture.findFlagCarrier(mode, teamSide) if carrier and carrierRoot and State.isCTFChaseCarrier then FlagCapture.moveRoot(rootPart, carrierRoot.CFrame * CFrame.new(0, 0, 3.5)) FlagCapture.setStatus("Following " .. carrier.Name .. ", who is holding the flag.") elseif carrier then FlagCapture.setStatus(carrier.Name .. " is holding the flag. Waiting for it to become available.") else FlagCapture.setStatus("No live flag for this mode is visible. Waiting for its actual part to respawn or stream in.") end return end FlagCapture.beginCaptureAttempt(now) FlagCapture.carriedSince = nil FlagCapture.deliveryHadFlag = false FlagCapture.moveRoot(rootPart, CFrame.new(FlagCapture.target.Position)) FlagCapture.touch(rootPart, FlagCapture.target) FlagCapture.phase = "PickUp" FlagCapture.phaseStarted = now FlagCapture.pickupMoves = 1 FlagCapture.lastPickupAt = now FlagCapture.setStatus("Taking the live " .. tostring(FlagCapture.targetSide) .. " flag: " .. FlagCapture.target.Name .. ".") return end end if FlagCapture.phase == "PickUp" then local attempts = math.clamp(math.floor(Config.CTF_PICKUP_ATTEMPTS), 1, 2) if carriedFlag or not FlagCapture.target or not FlagCapture.target.Parent then FlagCapture.phase = "ReturnHome" elseif now - FlagCapture.lastPickupAt >= FlagCapture.pickupSeconds then if FlagCapture.pickupMoves >= attempts or not FlagCapture.isLooseFlagCandidate(FlagCapture.target) then FlagCapture.phase = "ReturnHome" else FlagCapture.moveRoot(rootPart, CFrame.new(FlagCapture.target.Position)) FlagCapture.touch(rootPart, FlagCapture.target) FlagCapture.pickupMoves = FlagCapture.pickupMoves + 1 FlagCapture.lastPickupAt = now return end else return end end if FlagCapture.phase == "ReturnHome" then if not FlagCapture.deliveryAttempt then FlagCapture.beginCaptureAttempt(now) end FlagCapture.deliveryAttempt.returnedAt = now FlagCapture.deliveryHadFlag = carriedFlag ~= nil FlagCapture.moveRoot(rootPart, FlagCapture.home.cframe) FlagCapture.phase = "Deliver" FlagCapture.phaseStarted = now FlagCapture.lastNudge = now - 1 FlagCapture.setStatus("At your capture point. Waiting for the carried flag to be delivered.") return end if FlagCapture.phase == "Deliver" then local homePosition = FlagCapture.home.cframe.Position if carriedFlag then FlagCapture.deliveryHadFlag = true end if (rootPart.Position - homePosition).Magnitude > 6 then -- Do not count a flag lost somewhere else as a delivery at home. if not carriedFlag then FlagCapture.deliveryHadFlag = false end FlagCapture.moveRoot(rootPart, FlagCapture.home.cframe) end if now - FlagCapture.lastNudge >= 0.5 then FlagCapture.lastNudge = now local offset = Vector3.zero if math.floor((now - FlagCapture.phaseStarted) * 2) % 2 == 0 then offset = Vector3.new(0.75, 0, 0) end humanoid:MoveTo(homePosition + offset) local homePart = FlagCapture.home.part if homePart and homePart.Parent and not isFlagHeld(homePart) and (homePart.Position - homePosition).Magnitude < 15 then FlagCapture.touch(rootPart, homePart) if carriedFlag then FlagCapture.touch(carriedFlag, homePart) end end end local elapsed = now - FlagCapture.phaseStarted local attempt = FlagCapture.deliveryAttempt if attempt and attempt.counted then FlagCapture.completeDelivery(now) elseif elapsed >= 0.1 and not carriedFlag and FlagCapture.deliveryHadFlag and FlagCapture.carriedSince and now - FlagCapture.carriedSince < 175 and not FlagCapture.scoreSource then FlagCapture.recordCapture(attempt, "the flag delivery") FlagCapture.completeDelivery(now) elseif elapsed >= Config.CTF_SCORE_CONFIRM_WAIT and not carriedFlag then FlagCapture.completeDelivery(now) elseif elapsed >= FlagCapture.captureTimeout then FlagCapture.stop("Still carrying the flag at this capture point. Stand on the exact scoring spot, press Set My Capture Point, and enable Auto CTF again.") end end end table.insert(Connections, workspace.DescendantAdded:Connect(function(instance) if FlagCapture.isFlagPart(instance) then FlagCapture.lastScan = -math.huge end end)) table.insert(Connections, workspace.DescendantRemoving:Connect(function(instance) if FlagCapture.isFlagPart(instance) then FlagCapture.lastScan = -math.huge end end)) task.spawn(function() pcall(FlagCapture.refreshFlags, os.clock()) while ScreenGui.Parent and not Runtime.closed do local succeeded, failure = pcall(FlagCapture.step, os.clock()) if not succeeded and not Runtime.closed then FlagCapture.stop("CTF stopped: " .. tostring(failure)) end task.wait(0.05) end end) --// AUTO FORT CAPTURE LOOP task.spawn(function() local savedPosFort = nil while ScreenGui.Parent do if State.isAutoFortActive and not State.isAutoCTFActive and not ColaDelivery.active then local char = player.Character local hrp = char and char:FindFirstChild("HumanoidRootPart") local hum = char and char:FindFirstChildOfClass("Humanoid") if hrp and hum and hum.Health > 0 then if not savedPosFort then savedPosFort = hrp.CFrame end local flagCache = scanForFlags() local myTeamName = player.Team and string.lower(player.Team.Name) or "" local myTeamColorStr = player.Team and string.lower(tostring(player.Team.TeamColor)) or "" local isRedTeam = false if string.find(myTeamColorStr, "red") or string.find(myTeamName, "red") or string.find(myTeamName, "pirate") then isRedTeam = true end local targetFort = nil for _, fort in ipairs(flagCache.neutral) do if fort.Parent and not isFortCapturedByPlayerTeam(fort, isRedTeam) and not isFlagHeld(fort) then targetFort = fort break end end if not targetFort then local enemyFlags = isRedTeam and flagCache.blue or flagCache.red for _, fort in ipairs(enemyFlags) do if fort.Parent and not isFortCapturedByPlayerTeam(fort, isRedTeam) and not isFlagHeld(fort) then targetFort = fort break end end end if targetFort then local halfHeight = targetFort.Size.Y / 2 local basePos = targetFort.Position - Vector3.new(0, halfHeight - 2.5, 0) hrp.CFrame = CFrame.new(basePos) hrp.AssemblyLinearVelocity = Vector3.zero while State.isAutoFortActive and not State.isAutoCTFActive and not ColaDelivery.active do task.wait(0.3) char = player.Character hrp = char and char:FindFirstChild("HumanoidRootPart") hum = char and char:FindFirstChildOfClass("Humanoid") if not hrp or not hum or hum.Health <= 0 or not targetFort.Parent or isFlagHeld(targetFort) then break end if isFortCapturedByPlayerTeam(targetFort, isRedTeam) then break end end if targetFort.Parent and isFortCapturedByPlayerTeam(targetFort, isRedTeam) and State.isAutoFortActive then if savedPosFort and hrp then hrp.CFrame = savedPosFort hrp.AssemblyLinearVelocity = Vector3.zero savedPosFort = nil end task.wait(1) else if hrp then hrp.AssemblyLinearVelocity = Vector3.zero end task.wait(0.5) end else if savedPosFort and hrp then hrp.CFrame = savedPosFort hrp.AssemblyLinearVelocity = Vector3.zero savedPosFort = nil end task.wait(1) end else task.wait(0.5) end else if savedPosFort then local char = player.Character local hrp = char and char:FindFirstChild("HumanoidRootPart") if hrp then hrp.CFrame = savedPosFort hrp.AssemblyLinearVelocity = Vector3.zero end savedPosFort = nil end task.wait(0.5) end end end) task.spawn(function() while ScreenGui.Parent and not Runtime.closed do local succeeded, failure = pcall(LoopKillEnemies.step, os.clock()) if not succeeded then LoopKillEnemies.setEnabled(false) if not Runtime.closed then notifyUser("Loop Kill All Enemies", "Stopped: " .. tostring(failure), 6) end end task.wait(0.05) end end) task.spawn(function() while ScreenGui.Parent and not Runtime.closed do local succeeded, failure = pcall(ShipBring.step, os.clock()) if not succeeded then ShipBring.setEnabled(false) if not Runtime.closed then notifyUser("Bring Open Ships", "Stopped: " .. tostring(failure), 6) end end task.wait(0.05) end end) --// HITBOX & COMBAT AURA LOOPS task.spawn(function() while ScreenGui.Parent do if State.isHitboxExpanderActive then pcall(updateHitboxes) end if State.isTreasureHitboxActive then pcall(updateTreasureHitboxes) end task.wait(0.3) end end) task.spawn(function() while ScreenGui.Parent do if State.isKillAuraActive then pcall(function() local char = player.Character if char then local tool = char:FindFirstChildOfClass("Tool"); local handle = tool and tool:FindFirstChild("Handle") if handle and typeof(firetouchinterest) == "function" then local hrp = char:FindFirstChild("HumanoidRootPart") if hrp then for _, enemy in ipairs(Players:GetPlayers()) do if enemy ~= player and enemy.Character then local isEnemy = (player.Team == nil) or (enemy.Team == nil) or (enemy.Team ~= player.Team) if isEnemy then local enemyHrp = enemy.Character:FindFirstChild("HumanoidRootPart") local enemyHum = enemy.Character:FindFirstChildOfClass("Humanoid") if enemyHrp and enemyHum and enemyHum.Health > 0 then local dist = (hrp.Position - enemyHrp.Position).Magnitude if dist <= Config.KILL_AURA_RANGE then firetouchinterest(handle, enemyHrp, 0); firetouchinterest(handle, enemyHrp, 1) end end end end end end end end end) end task.wait(0.1) end end) task.spawn(function() while ScreenGui.Parent do if State.isReachActive then pcall(function() local char = player.Character if char then local tool = char:FindFirstChildOfClass("Tool") if tool then local handle = tool:FindFirstChild("Handle") if handle and handle:IsA("BasePart") then if not Cache.originalToolProps[tool] then Cache.originalToolProps[tool] = {Size = handle.Size, Transparency = handle.Transparency, Massless = handle.Massless} end local desiredSize = Vector3.new(Config.MELEE_REACH_SIZE, Config.MELEE_REACH_SIZE, Config.MELEE_REACH_SIZE) if handle.Size ~= desiredSize then handle.Size = desiredSize; handle.Massless = true; handle.Transparency = 1 local selection = handle:FindFirstChild("ReachVisual") if not selection then selection = Instance.new("SelectionBox") selection.Name = "ReachVisual"; selection.Adornee = handle selection.Color3 = Color3.fromRGB(255, 30, 30); selection.LineThickness = 0.05 selection.Transparency = 0.5; selection.Parent = handle end end end end end end) end task.wait(0.2) end end) task.spawn(function() while ScreenGui.Parent do if State.isSpectating then local targetPlr = getFlingTargetPlayer(State.flingTargetStr) if targetPlr and targetPlr.Character and targetPlr.Character:FindFirstChild("Humanoid") then if workspace.CurrentCamera.CameraSubject ~= targetPlr.Character.Humanoid then workspace.CurrentCamera.CameraSubject = targetPlr.Character.Humanoid end end end task.wait(0.5) end end) --// AUTO RETURN ON LOW HP LOOP local lastAutoHPReturnTime = 0 task.spawn(function() while ScreenGui.Parent do if State.isAutoHPReturnActive then pcall(function() local char = player.Character local hum = char and char:FindFirstChildOfClass("Humanoid") local hrp = char and char:FindFirstChild("HumanoidRootPart") if hum and hrp and hum.MaxHealth > 0 and hum.Health > 0 then local hpPercent = (hum.Health / hum.MaxHealth) * 100 if hpPercent <= Config.AUTO_RETURN_HP_PCT then local now = tick() if now - lastAutoHPReturnTime > 5 then lastAutoHPReturnTime = now if hum.Sit then hum.Sit = false task.wait(0.05) end hrp.CFrame = getSafeSpawnLocation() hrp.AssemblyLinearVelocity = Vector3.zero end end end end) end task.wait(0.3) end end) task.spawn(function() while ScreenGui.Parent do if State.isAutoReturnActive and State.isFlingAttachActive and State.flingStartData and State.flingTargetStr ~= "" then local targetPlr = getFlingTargetPlayer(State.flingTargetStr) if targetPlr and targetPlr.Character then local tHrp = targetPlr.Character:FindFirstChild("HumanoidRootPart") if tHrp then local vel = tHrp.AssemblyLinearVelocity.Magnitude; local posY = tHrp.Position.Y if vel > 350 or posY > 1500 or posY < -500 then State.isFlingAttachActive = false UI.FlingAttachBtn.Text = "Attach to Target: OFF"; UI.FlingAttachBtn.BackgroundColor3 = Color3.fromRGB(170, 50, 50) if State.flingStartData.Type == "Vehicle" and State.flingStartData.Root then if State.flingStartData.Model then State.flingStartData.Model:PivotTo(State.flingStartData.CFrame) else State.flingStartData.Root.CFrame = State.flingStartData.CFrame end State.flingStartData.Root.AssemblyLinearVelocity = Vector3.zero; State.flingStartData.Root.AssemblyAngularVelocity = Vector3.zero elseif State.flingStartData.Type == "Character" and State.flingStartData.Root then local hum = player.Character and player.Character:FindFirstChildOfClass("Humanoid") if hum and hum.Sit then hum.Sit = false task.wait(0.05) end State.flingStartData.Root.CFrame = State.flingStartData.CFrame State.flingStartData.Root.AssemblyLinearVelocity = Vector3.zero; State.flingStartData.Root.AssemblyAngularVelocity = Vector3.zero end State.flingStartData = nil end end end end task.wait(0.2) end end) --// FLING AURA THREAD task.spawn(function() while ScreenGui.Parent do if State.isFlingAuraActive and not State.isFlingAttachActive then pcall(function() local char = player.Character local hrp = char and char:FindFirstChild("HumanoidRootPart") local hum = char and char:FindFirstChildOfClass("Humanoid") if hrp and hum then local bestTarget = nil local bestDist = Config.FLING_AURA_RANGE local refPos = hrp.Position if hum.Sit and hum.SeatPart then local vehicleRoot = hum.SeatPart.AssemblyRootPart or hum.SeatPart refPos = vehicleRoot.Position end for _, p in ipairs(Players:GetPlayers()) do if p ~= player and p.Character then local eHrp = p.Character:FindFirstChild("HumanoidRootPart") local eHum = p.Character:FindFirstChildOfClass("Humanoid") if eHrp and eHum and eHum.Health > 0 and not eHum.Sit then local isEnemy = (player.Team == nil) or (p.Team == nil) or (p.Team ~= player.Team) if isEnemy or State.isFlingAuraTeamActive then local dist = (refPos - eHrp.Position).Magnitude if dist <= bestDist then bestDist = dist bestTarget = p end end end end end if bestTarget then State.flingTargetStr = bestTarget.Name UI.TargetInputBox.Text = bestTarget.Name UI.FlingDropdownBtn.Text = "Target: " .. bestTarget.Name if not State.isAutoReturnActive then State.isAutoReturnActive = true UI.AutoReturnBtn.Text = "Auto-Return on Success: ON" UI.AutoReturnBtn.BackgroundColor3 = Color3.fromRGB(0, 170, 0) end if not State.isFlingActive then State.isFlingActive = true UI.FlingToggleBtn.Text = "Ultimate Fling: ON" UI.FlingToggleBtn.BackgroundColor3 = Color3.fromRGB(0, 170, 0) end local vehicleRoot = hum.Sit and hum.SeatPart and (hum.SeatPart.AssemblyRootPart or hum.SeatPart) if vehicleRoot then State.flingStartData = {Type = "Vehicle", Root = vehicleRoot, CFrame = vehicleRoot.CFrame, Model = hum.SeatPart:FindFirstAncestorOfClass("Model")} else State.flingStartData = {Type = "Character", Root = hrp, CFrame = hrp.CFrame} end State.isFlingAttachActive = true UI.FlingAttachBtn.Text = "Attach to Target: ON" UI.FlingAttachBtn.BackgroundColor3 = Color3.fromRGB(0, 170, 0) end end end) end task.wait(0.2) end end) --// FLING ENGINE LOOP task.spawn(function() local movel = 0.1; local angle = 0; local tickCounter = 0 while ScreenGui.Parent do RunService.Heartbeat:Wait() if State.isFlingActive then local char = player.Character; local humanoid = char and char:FindFirstChildOfClass("Humanoid"); local hrp = char and char:FindFirstChild("HumanoidRootPart") local seat = humanoid and humanoid.SeatPart; local vehicleRoot = seat and (seat.AssemblyRootPart or seat); local camera = workspace.CurrentCamera local targetParts = {}; local activeMode = flingModes[Config.FLING_MODE_INDEX] or "Vehicle" if State.isFlingAttachActive and State.flingTargetStr ~= "" then local targetPlr = getFlingTargetPlayer(State.flingTargetStr) if targetPlr and targetPlr.Character then local tHrp = targetPlr.Character:FindFirstChild("HumanoidRootPart") if tHrp then if activeMode == "Vehicle" and vehicleRoot and seat then local vehicleModel = seat:FindFirstAncestorOfClass("Model") local dist = (vehicleRoot.Position - tHrp.Position).Magnitude if dist > 4 then local attachCF = tHrp.CFrame * CFrame.new(math.random(-1,1), 2, math.random(-1,1)) if vehicleModel then vehicleModel:PivotTo(attachCF) else vehicleRoot.CFrame = attachCF end end elseif hrp then if humanoid.Sit then humanoid.Sit = false task.wait(0.05) end local dist = (hrp.Position - tHrp.Position).Magnitude if dist > 3 then hrp.CFrame = tHrp.CFrame * CFrame.new(math.random(-1,1), 2, math.random(-1,1)) end end end end end if (activeMode == "Vehicle" or activeMode == "Super Combo") and vehicleRoot and seat then local vehicleModel = seat:FindFirstAncestorOfClass("Model") if vehicleModel then for _, part in ipairs(vehicleModel:GetDescendants()) do if part:IsA("BasePart") then if not Cache.originalFlingStates[part] then Cache.originalFlingStates[part] = {CanCollide = part.CanCollide, Massless = part.Massless} end if not string.find(string.lower(part.Name), "wheel") and part ~= seat and part ~= vehicleRoot then part.CanCollide = false end end end end end local noCollideModes = {["Vehicle"] = true, ["Limbs Safe"] = true, ["Spin Fling"] = true, ["Spiral Vortex"] = true, ["Seizure Shake"] = true, ["Chaos Jitter"] = true, ["Super Combo"] = true} if noCollideModes[activeMode] and char then for _, part in ipairs(char:GetChildren()) do if part:IsA("BasePart") and part ~= hrp then part.CanCollide = false end end end local characterModes = {"Character", "All", "Limbs Safe", "Spin Fling", "Aim Direction", "Up Launch", "Bounce Wave", "Orbit Pulser", "Spiral Vortex", "Black Hole", "Seizure Shake", "Tremor Slam", "Chaos Jitter", "Hyper Drive", "Helicopter", "Quantum Shift", "Super Combo"} if table.find(characterModes, activeMode) and hrp then table.insert(targetParts, hrp) end if (activeMode == "Vehicle" or activeMode == "All" or activeMode == "Super Combo") and vehicleRoot then table.insert(targetParts, vehicleRoot) end if (activeMode == "Held Tool" or activeMode == "Super Combo") and char then local tool = char:FindFirstChildOfClass("Tool") if tool then for _, part in ipairs(tool:GetDescendants()) do if part:IsA("BasePart") and not part.Anchored then table.insert(targetParts, part) end end end end if (activeMode == "Unanchored" or activeMode == "Super Combo") and hrp then local connected = hrp:GetConnectedParts(true) for _, part in ipairs(connected) do if not part.Anchored and not part:IsDescendantOf(char) then table.insert(targetParts, part) end end end if #targetParts > 0 then local originalVelocities = {}; local originalAngulars = {}; local pwr = tonumber(Config.FLING_POWER) or 2500; tickCounter = tickCounter + 1 for _, part in ipairs(targetParts) do if part and part.Parent then originalVelocities[part] = part.AssemblyLinearVelocity; originalAngulars[part] = part.AssemblyAngularVelocity if activeMode == "Vehicle" then local spinY = (tickCounter % 2 == 0) and pwr or -pwr part.AssemblyAngularVelocity = Vector3.new(0, spinY, 0); part.AssemblyLinearVelocity = originalVelocities[part] + Vector3.new(0, 1, 0) elseif activeMode == "Spin Fling" then part.AssemblyAngularVelocity = Vector3.new(0, pwr, 0); part.AssemblyLinearVelocity = originalVelocities[part] * 100 + Vector3.new(0, 100, 0) elseif activeMode == "Aim Direction" and camera then part.AssemblyLinearVelocity = camera.CFrame.LookVector * pwr elseif activeMode == "Up Launch" then part.AssemblyLinearVelocity = Vector3.new(0, pwr, 0) elseif activeMode == "Bounce Wave" then part.AssemblyLinearVelocity = Vector3.new(0, pwr * (movel > 0 and 1 or -1), 0) elseif activeMode == "Orbit Pulser" then angle = angle + 0.5; part.AssemblyLinearVelocity = Vector3.new(math.cos(angle), 0, math.sin(angle)) * pwr elseif activeMode == "Spiral Vortex" then angle = angle + 0.8; local radius = math.sin(tickCounter * 0.1) * pwr part.AssemblyAngularVelocity = Vector3.new(0, pwr, 0); part.AssemblyLinearVelocity = Vector3.new(math.cos(angle) * radius, pwr * 0.2, math.sin(angle) * radius) elseif activeMode == "Black Hole" then local pullDirection = (tickCounter % 2 == 0) and -1 or 1 part.AssemblyLinearVelocity = (hrp and hrp.CFrame.LookVector or Vector3.new(0,1,0)) * (pwr * pullDirection) elseif activeMode == "Seizure Shake" then local rx = math.random(-pwr, pwr); local ry = math.random(-pwr, pwr); local rz = math.random(-pwr, pwr) part.AssemblyLinearVelocity = Vector3.new(rx, ry, rz); part.AssemblyAngularVelocity = Vector3.new(rx, ry, rz) elseif activeMode == "Tremor Slam" then local slamY = (tickCounter % 3 == 0) and pwr or -pwr; part.AssemblyLinearVelocity = Vector3.new(0, slamY, 0) elseif activeMode == "Chaos Jitter" then local jitter = (tickCounter % 2 == 0) and pwr or -pwr part.AssemblyLinearVelocity = Vector3.new(jitter, 0, jitter); part.AssemblyAngularVelocity = Vector3.new(0, jitter, 0) elseif activeMode == "Hyper Drive" then local moveDir = humanoid and humanoid.MoveDirection or Vector3.new(0, 0, 0) if moveDir.Magnitude > 0 then part.AssemblyLinearVelocity = moveDir * pwr else part.AssemblyLinearVelocity = Vector3.new(0, pwr, 0) end elseif activeMode == "Helicopter" then part.AssemblyAngularVelocity = Vector3.new(0, pwr, 0); part.AssemblyLinearVelocity = Vector3.new(0, 50, 0) elseif activeMode == "Quantum Shift" then local factor = (tickCounter % 2 == 0) and 1 or -1 part.AssemblyLinearVelocity = Vector3.new(pwr * factor, pwr * -factor, pwr * factor) else part.AssemblyLinearVelocity = originalVelocities[part] * pwr + Vector3.new(0, pwr, 0) if activeMode == "Super Combo" then part.AssemblyAngularVelocity = Vector3.new(pwr, pwr, pwr) end end end end RunService.RenderStepped:Wait() for _, part in ipairs(targetParts) do if part and part.Parent and originalVelocities[part] then part.AssemblyLinearVelocity = originalVelocities[part] if originalAngulars[part] then part.AssemblyAngularVelocity = originalAngulars[part] end end end RunService.Stepped:Wait() for _, part in ipairs(targetParts) do if part and part.Parent and originalVelocities[part] then part.AssemblyLinearVelocity = originalVelocities[part] + Vector3.new(0, movel, 0) end end movel = movel * -1 end else if next(Cache.originalFlingStates) then restoreFlingStates() end end end end) --// RUNTIME CONNECTIONS if ShipsFolder then for _, child in ipairs(ShipsFolder:GetChildren()) do if child:IsA("Model") then task.spawn(initShipCache, child) end end table.insert(Connections, ShipsFolder.ChildAdded:Connect(function(child) if child:IsA("Model") then task.spawn(initShipCache, child) if not State.isStealPaused and not State.isAntiSitActive and (State.isStealEnemyActive or State.isStealFriendlyActive) then tryStealNewlySpawnedShip(child) end end end)) table.insert(Connections, ShipsFolder.ChildRemoved:Connect(function(child) Cache.shipAffiliation[child] = nil; Cache.shipOwner[child] = nil; Cache.shipInitialParts[child] = nil; Cache.shipPartCount[child] = nil; Cache.lastPartCountUpdate[child] = nil; Cache.shipSeats[child] = nil; cleanupESP(child) end)) end table.insert(Connections, RunService.Heartbeat:Connect(function(deltaTime) if State.isAutoFireActive then checkAutoBroadside() end end)) --// FREECAM RENDERSTEPPED LOOP & MOUSE LISTENER table.insert(Connections, RunService.RenderStepped:Connect(function(deltaTime) if State.isFreecamActive then local cam = workspace.CurrentCamera if not cam then return end cam.CameraType = Enum.CameraType.Scriptable if Keybindings.isHeld("FreecamLook") then UserInputService.MouseBehavior = Enum.MouseBehavior.LockCurrentPosition local delta = UserInputService:GetMouseDelta() local sens = 0.003 freecamRot = Vector2.new( math.clamp(freecamRot.X - delta.Y * sens, -math.rad(89), math.rad(89)), freecamRot.Y - delta.X * sens ) else UserInputService.MouseBehavior = Enum.MouseBehavior.Default end local rotCF = CFrame.Angles(0, freecamRot.Y, 0) * CFrame.Angles(freecamRot.X, 0, 0) local speed = (tonumber(Config.FREECAM_SPEED) or 50) * deltaTime if Keybindings.isHeld("FreecamFast") then speed = speed * 2.5 end local moveDir = Vector3.zero if Keybindings.isHeld("FreecamForward") then moveDir = moveDir + rotCF.LookVector end if Keybindings.isHeld("FreecamBackward") then moveDir = moveDir - rotCF.LookVector end if Keybindings.isHeld("FreecamRight") then moveDir = moveDir + rotCF.RightVector end if Keybindings.isHeld("FreecamLeft") then moveDir = moveDir - rotCF.RightVector end if Keybindings.isHeld("FreecamUp") then moveDir = moveDir + Vector3.new(0, 1, 0) end if Keybindings.isHeld("FreecamDown") then moveDir = moveDir - Vector3.new(0, 1, 0) end if moveDir.Magnitude > 0 then moveDir = moveDir.Unit * speed end if not freecamCF then freecamCF = cam.CFrame end freecamCF = CFrame.new(freecamCF.Position + moveDir) * rotCF cam.CFrame = freecamCF end end)) --// ANTI-AFK IDLED LISTENER table.insert(Connections, player.Idled:Connect(function() if State.isAntiAfkActive then pcall(function() VirtualUser:CaptureController() VirtualUser:ClickButton2(Vector2.zero) end) end end)) --// STEPPED TICK LOOPS & VISUALIZER UPDATES table.insert(Connections, RunService.Stepped:Connect(function(_, deltaTime) pcall(function() if not ScreenGui.Parent or not player.Character then return end local char = player.Character; local hum = char:FindFirstChildOfClass("Humanoid"); local hrp = char:FindFirstChild("HumanoidRootPart") if hum then if State.isCharSpeedActive then hum.WalkSpeed = Config.PLAYER_WALKSPEED end if State.isCharJumpActive then hum.UseJumpPower = true; hum.JumpPower = Config.PLAYER_JUMPPOWER end if State.isAntiSitActive and not ColaDelivery.active then hum:SetStateEnabled(Enum.HumanoidStateType.Seated, false) if hum.Sit then hum.Sit = false hum:ChangeState(Enum.HumanoidStateType.Jumping) end end if State.isAntiRagdollActive then hum:SetStateEnabled(Enum.HumanoidStateType.Ragdoll, false) hum:SetStateEnabled(Enum.HumanoidStateType.FallingDown, false) local currentState = hum:GetState() if currentState == Enum.HumanoidStateType.Ragdoll or currentState == Enum.HumanoidStateType.FallingDown then hum:ChangeState(Enum.HumanoidStateType.GettingUp) end end end if State.isMapFireVisualizerActive and State.mapFireVisualizer and hrp then State.mapFireVisualizer:Update(hrp.Position, Config.MAP_ACTIVATOR_RANGE, hrp) end if State.isFlingAuraVisActive and State.flingAuraVisualizer and hrp then State.flingAuraVisualizer:Update(hrp.Position, Config.FLING_AURA_RANGE, hrp) end if State.isAntiVoidActive and hrp then if hrp.Position.Y < Config.ANTI_VOID_HEIGHT then if hum and hum.Sit then hum.Sit = false end hrp.CFrame = getSafeSpawnLocation() hrp.AssemblyLinearVelocity = Vector3.zero end end if State.isWaterWalkActive and hrp and hum then if hum.Sit or hum.SeatPart then if waterPlatform then waterPlatform.CanCollide = false waterPlatform.CFrame = CFrame.new(0, -500, 0) end else if not waterPlatform or not waterPlatform.Parent then ensureWaterPlatform() end if waterPlatform then waterPlatform.CanCollide = true local targetY = (tonumber(Config.WATER_WALK_HEIGHT) or 10) - 0.5 waterPlatform.CFrame = CFrame.new(hrp.Position.X, targetY, hrp.Position.Z) end end end if State.isPlayerFlyActive and char and hrp then local cam = workspace.CurrentCamera local flyDir = Vector3.zero if Keybindings.isHeld("PlayerFlyForward") then flyDir = flyDir + cam.CFrame.LookVector end if Keybindings.isHeld("PlayerFlyBackward") then flyDir = flyDir - cam.CFrame.LookVector end if Keybindings.isHeld("PlayerFlyRight") then flyDir = flyDir + cam.CFrame.RightVector end if Keybindings.isHeld("PlayerFlyLeft") then flyDir = flyDir - cam.CFrame.RightVector end if Keybindings.isHeld("PlayerFlyUp") then flyDir = flyDir + Vector3.new(0, 1, 0) end if Keybindings.isHeld("PlayerFlyDown") then flyDir = flyDir - Vector3.new(0, 1, 0) end if flyDir.Magnitude > 0 then flyDir = flyDir.Unit end if not playerFlyBrake or playerFlyBrake.Parent ~= hrp then if playerFlyBrake then playerFlyBrake:Destroy() end playerFlyBrake = Instance.new("BodyVelocity") playerFlyBrake.Name = "PlayerFlyBrake" playerFlyBrake.MaxForce = Vector3.new(1000000, 1000000, 1000000) playerFlyBrake.Parent = hrp end playerFlyBrake.Velocity = flyDir * Config.PLAYER_FLY_SPEED if hum then hum.PlatformStand = true end if not playerFlyGyro or playerFlyGyro.Parent ~= hrp then if playerFlyGyro then playerFlyGyro:Destroy() end playerFlyGyro = Instance.new("BodyGyro") playerFlyGyro.Name = "PlayerFlyGyro" playerFlyGyro.MaxTorque = Vector3.new(9e9, 9e9, 9e9) playerFlyGyro.P = 10000 playerFlyGyro.Parent = hrp end playerFlyGyro.CFrame = CFrame.lookAt(hrp.Position, hrp.Position + cam.CFrame.LookVector) else if playerFlyBrake then playerFlyBrake:Destroy(); playerFlyBrake = nil end if playerFlyGyro then playerFlyGyro:Destroy(); playerFlyGyro = nil end end if State.isPlayerNoClipActive then local now = tick() if now - Cache.lastCharNoClipUpdate > 1 then Cache.charNoClipParts = {} for _, part in ipairs(char:GetDescendants()) do if part:IsA("BasePart") then table.insert(Cache.charNoClipParts, part) end end Cache.lastCharNoClipUpdate = now end for _, part in ipairs(Cache.charNoClipParts) do if part.CanCollide then Cache.originalPlayerCollisions[part] = true; part.CanCollide = false end end end if State.isHitboxExpanderActive then for part in pairs(Cache.originalHitboxProps) do if part and part.Parent then part.CanCollide = false; part.Massless = true end end end if State.isZoomUncapActive then player.CameraMaxZoomDistance = 9999 end if State.isFullbrightActive then Lighting.Ambient = Color3.new(1, 1, 1); Lighting.ColorShift_Bottom = Color3.new(1, 1, 1); Lighting.ColorShift_Top = Color3.new(1, 1, 1) Lighting.FogEnd = 100000; Lighting.GlobalShadows = false; Lighting.ClockTime = 14 end if State.isAntiSinkActive and CurrentShip then local currentPivot = CurrentShip:GetPivot() if currentPivot.Position.Y < Config.MIN_SINK_HEIGHT then local newPivot = CFrame.new(currentPivot.Position.X, Config.MIN_SINK_HEIGHT, currentPivot.Position.Z) * currentPivot.Rotation CurrentShip:PivotTo(newPivot) local rootPart = CurrentShip.PrimaryPart or CurrentShip:FindFirstChildWhichIsA("BasePart") if rootPart and rootPart.AssemblyLinearVelocity.Y < 0 then rootPart.AssemblyLinearVelocity = Vector3.new(rootPart.AssemblyLinearVelocity.X, 0, rootPart.AssemblyLinearVelocity.Z) end end end if State.isShipNoClipActive and CurrentShip then local now = tick() if now - Cache.lastShipNoClipUpdate > 1 then Cache.shipNoClipParts = {} for _, part in ipairs(CurrentShip:GetDescendants()) do if part:IsA("BasePart") and not part:IsA("VehicleSeat") and not part:IsA("Seat") then table.insert(Cache.shipNoClipParts, part) end end Cache.lastShipNoClipUpdate = now end for _, part in ipairs(Cache.shipNoClipParts) do if part.CanCollide then Cache.originalShipCollisions[part] = true; part.CanCollide = false end end end if hum and hum.SeatPart and hum.SeatPart:IsA("VehicleSeat") and CurrentShip and not State.isBringShipsActive and not ColaDelivery.active then local seat = hum.SeatPart; local rootPart = seat.AssemblyRootPart or seat local currentPivot = CurrentShip:GetPivot(); local newPivot = currentPivot; local forceUpdate = false if State.isSpeedBoosting then local look = seat.CFrame.LookVector; local flatLook = Vector3.new(look.X, 0, look.Z).Unit if flatLook.Magnitude > 0 then if Keybindings.isHeld("ShipBoostForward") then newPivot = newPivot + (flatLook * (Config.BOOST_SPEED * deltaTime)); forceUpdate = true elseif Keybindings.isHeld("ShipBoostBackward") then newPivot = newPivot - (flatLook * (Config.BOOST_SPEED * deltaTime)); forceUpdate = true end end end if State.isFlying then if not lockedAltitude then lockedAltitude = currentPivot.Position.Y end if Keybindings.isHeld("ShipFlightUp") then lockedAltitude = lockedAltitude + (Config.FLY_SPEED * deltaTime) elseif Keybindings.isHeld("ShipFlightDown") then lockedAltitude = lockedAltitude - (Config.FLY_SPEED * deltaTime) end if State.isAntiSinkActive and lockedAltitude < Config.MIN_SINK_HEIGHT then lockedAltitude = Config.MIN_SINK_HEIGHT end if not activeFlyBrake or activeFlyBrake.Parent ~= rootPart then if activeFlyBrake then activeFlyBrake:Destroy() end activeFlyBrake = Instance.new("BodyVelocity"); activeFlyBrake.Name = "FlyBrake"; activeFlyBrake.MaxForce = Vector3.new(0, 1000000, 0) activeFlyBrake.Velocity = Vector3.zero; activeFlyBrake.Parent = rootPart end newPivot = CFrame.new(newPivot.Position.X, lockedAltitude, newPivot.Position.Z) * newPivot.Rotation; forceUpdate = true else if activeFlyBrake then activeFlyBrake:Destroy(); activeFlyBrake = nil end end if forceUpdate then CurrentShip:PivotTo(newPivot) for _, part in ipairs(CurrentShip:GetDescendants()) do if part:IsA("BasePart") then part.AssemblyLinearVelocity = Vector3.zero; part.AssemblyAngularVelocity = Vector3.zero end end end else if activeFlyBrake then activeFlyBrake:Destroy(); activeFlyBrake = nil end; lockedAltitude = nil end end) end)) --// BUTTON BINDINGS Keybindings.connectButton("AutoFireBtn", UI.AutoFireBtn, function() State.isAutoFireActive = not State.isAutoFireActive UI.AutoFireBtn.Text = "Broadside Auto-Fire: " .. (State.isAutoFireActive and "ON" or "OFF") UI.AutoFireBtn.BackgroundColor3 = State.isAutoFireActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("LeftBtn", UI.LeftBtn, function() State.isSpammingLeft = not State.isSpammingLeft UI.LeftBtn.Text = "Fire Left Cannons: " .. (State.isSpammingLeft and "ON" or "OFF") UI.LeftBtn.BackgroundColor3 = State.isSpammingLeft and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("RightBtn", UI.RightBtn, function() State.isSpammingRight = not State.isSpammingRight UI.RightBtn.Text = "Fire Right Cannons: " .. (State.isSpammingRight and "ON" or "OFF") UI.RightBtn.BackgroundColor3 = State.isSpammingRight and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("AntiAfkBtn", UI.AntiAfkBtn, function() State.isAntiAfkActive = not State.isAntiAfkActive UI.AntiAfkBtn.Text = "Anti-AFK: " .. (State.isAntiAfkActive and "ON" or "OFF") UI.AntiAfkBtn.BackgroundColor3 = State.isAntiAfkActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("FireAllOnceBtn", UI.FireAllOnceBtn, function() if not CurrentShip then Toast.push("Fire ALL Cannons", "Not seated on a ship", Toast.OFF, 2.5) return end local cannonCount, extraCount, passes = fireAllShipWeaponsRepeated() if cannonCount + extraCount == 0 then Toast.push("Fire ALL Cannons", "Nothing fireable found on " .. CurrentShip.Name, Toast.WARN, 3) else local rate = math.clamp(tonumber(Config.FIRE_PER_SECOND) or 8.3, 0.5, 200) local detail = string.format("%d cannon(s)", cannonCount) if extraCount > 0 then detail = detail .. string.format(" + %d activator(s)", extraCount) end detail = detail .. string.format(" over %.1fs", ((cannonCount + extraCount) * passes) / rate) Toast.push("Firing ALL on " .. CurrentShip.Name, detail, Toast.ON, 2.6) end end) Keybindings.connectButton("FireAllBtn", UI.FireAllBtn, function() State.isSpammingAll = not State.isSpammingAll UI.FireAllBtn.Text = "Fire ALL Cannons (Hold): " .. (State.isSpammingAll and "ON" or "OFF") UI.FireAllBtn.BackgroundColor3 = State.isSpammingAll and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("FireAllBroadBtn", UI.FireAllBroadBtn, function() State.isFireAllBroad = not State.isFireAllBroad UI.FireAllBroadBtn.Text = "Include Non-Cannon Activators: " .. (State.isFireAllBroad and "ON" or "OFF") UI.FireAllBroadBtn.BackgroundColor3 = State.isFireAllBroad and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("TurnLeftBtn", UI.TurnLeftBtn, function() ShipTurn.start(-(tonumber(Config.QUICK_TURN_DEGREES) or 45)) end) Keybindings.connectButton("TurnRightBtn", UI.TurnRightBtn, function() ShipTurn.start(tonumber(Config.QUICK_TURN_DEGREES) or 45) end) Keybindings.connectButton("QuickTurnInvertBtn", UI.QuickTurnInvertBtn, function() ShipTurn.cancel() Config.QUICK_TURN_INVERT_STEER = not (Config.QUICK_TURN_INVERT_STEER == true) refreshQuickTurnUI() ShipTurn.setStatus("Steer direction " .. (Config.QUICK_TURN_INVERT_STEER and "inverted" or "normal") .. ". This is detected automatically on the first steered turn; set it here if the auto-detection guesses wrong.") end) Keybindings.connectButton("ShipHUDBtn", UI.ShipHUDBtn, function() State.isShipHUDActive = not State.isShipHUDActive UI.ShipHUDBtn.Text = "Ship Status HUD: " .. (State.isShipHUDActive and "ON" or "OFF") UI.ShipHUDBtn.BackgroundColor3 = State.isShipHUDActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) ShipHUD.Frame.Visible = State.isShipHUDActive if State.isShipHUDActive then ShipHUD.applyPosition() ShipHUD.applyTransparency() pcall(ShipHUD.update) end end) Keybindings.connectButton("ShipHUDResetBtn", UI.ShipHUDResetBtn, function() ShipHUD.resetPosition() Toast.push("Ship Status HUD", "Position reset", Toast.INFO, 2) end) Keybindings.connectButton("ActionPingBtn", UI.ActionPingBtn, function() State.isActionPingActive = not State.isActionPingActive UI.ActionPingBtn.Text = "Action Ping Sound: " .. (State.isActionPingActive and "ON" or "OFF") UI.ActionPingBtn.BackgroundColor3 = State.isActionPingActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if State.isActionPingActive then -- Clear the "not available" latch so the next attempt can report again. ActionPing.unavailableWarned = false ActionPing.lastPlayed = 0 playActionPing() end end) Keybindings.connectButton("KeybindToastBtn", UI.KeybindToastBtn, function() State.isKeybindToastActive = not State.isKeybindToastActive UI.KeybindToastBtn.Text = "Keybind Toast Popups: " .. (State.isKeybindToastActive and "ON" or "OFF") UI.KeybindToastBtn.BackgroundColor3 = State.isKeybindToastActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if State.isKeybindToastActive then Toast.push("Keybind Toast Popups", "ON", Toast.ON, 2) end end) Keybindings.connectButton("BoardCaptainBtn", UI.BoardCaptainBtn, boardEnemyCaptain) Keybindings.connectButton("WaterWalkBtn", UI.WaterWalkBtn, toggleWaterWalk) Keybindings.connectButton("MapFireBtn", UI.MapFireBtn, function() State.isAutoMapFireActive = not State.isAutoMapFireActive UI.MapFireBtn.Text = "Auto-Fire Map Activators: " .. (State.isAutoMapFireActive and "ON" or "OFF") UI.MapFireBtn.BackgroundColor3 = State.isAutoMapFireActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("MapFireVisBtn", UI.MapFireVisBtn, function() State.isMapFireVisualizerActive = not State.isMapFireVisualizerActive UI.MapFireVisBtn.Text = "Visualize Activator Range: " .. (State.isMapFireVisualizerActive and "ON" or "OFF") UI.MapFireVisBtn.BackgroundColor3 = State.isMapFireVisualizerActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if State.isMapFireVisualizerActive then if not State.mapFireVisualizer then State.mapFireVisualizer = createDualSidedSphere("MapFireVisualizer", Color3.fromRGB(255, 100, 100)) end else if State.mapFireVisualizer then State.mapFireVisualizer:Destroy() State.mapFireVisualizer = nil end end end) Keybindings.connectButton("HitboxToggleBtn", UI.HitboxToggleBtn, function() State.isHitboxExpanderActive = not State.isHitboxExpanderActive UI.HitboxToggleBtn.Text = "Enemy Hitbox Expander: " .. (State.isHitboxExpanderActive and "ON" or "OFF") UI.HitboxToggleBtn.BackgroundColor3 = State.isHitboxExpanderActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if not State.isHitboxExpanderActive then restoreHitboxes() else task.spawn(updateHitboxes) end end) Keybindings.connectButton("HitboxPartDropdown", UI.HitboxPartDropdown, function() restoreHitboxes() local idx = table.find(HITBOX_PARTS, Config.HITBOX_TARGET_PART) or 1 idx = (idx % #HITBOX_PARTS) + 1; Config.HITBOX_TARGET_PART = HITBOX_PARTS[idx] UI.HitboxPartDropdown.Text = "Target Part: " .. Config.HITBOX_TARGET_PART if State.isHitboxExpanderActive then task.spawn(updateHitboxes) end end) Keybindings.connectButton("HitboxVisualBtn", UI.HitboxVisualBtn, function() State.isHitboxVisualsActive = not State.isHitboxVisualsActive UI.HitboxVisualBtn.Text = "Visualize Hitbox: " .. (State.isHitboxVisualsActive and "ON" or "OFF") UI.HitboxVisualBtn.BackgroundColor3 = State.isHitboxVisualsActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if not State.isHitboxVisualsActive then for part, adornment in pairs(Cache.hitboxAdornments) do if adornment then adornment:Destroy() end end table.clear(Cache.hitboxAdornments) elseif State.isHitboxExpanderActive then task.spawn(updateHitboxes) end end) Keybindings.connectButton("LoopKillTargetBtn", UI.LoopKillTargetBtn, function() LoopKillEnemies.setEnabled(not State.isLoopKillTargetActive, "Target") end) Keybindings.connectButton("BringShipsBtn", UI.BringShipsBtn, function() ShipBring.setEnabled(not State.isBringShipsActive) end) Keybindings.connectButton("BringShipsHereBtn", UI.BringShipsHereBtn, function() ShipBring.savePoint(false) end) Keybindings.connectButton("BringShipsMouseBtn", UI.BringShipsMouseBtn, function() ShipBring.savePoint(true) end) Keybindings.connectButton("BringShipsDestinationBtn", UI.BringShipsDestinationBtn, function() if Config.BRING_SHIPS_DESTINATION == "Point" then Config.BRING_SHIPS_DESTINATION = "Player" else Config.BRING_SHIPS_DESTINATION = "Point" end ShipBring.refreshUI() end) Keybindings.connectButton("BringShipsModeBtn", UI.BringShipsModeBtn, function() if Config.BRING_SHIPS_MODE == "Auto" then Config.BRING_SHIPS_MODE = "Teleport" elseif Config.BRING_SHIPS_MODE == "Teleport" then Config.BRING_SHIPS_MODE = "Sail" else Config.BRING_SHIPS_MODE = "Auto" end ShipBring.refreshUI() end) Keybindings.connectButton("LoopKillEnemiesBtn", UI.LoopKillEnemiesBtn, function() LoopKillEnemies.setEnabled(not State.isLoopKillEnemiesActive) end) Keybindings.connectButton("KillAuraBtn", UI.KillAuraBtn, function() State.isKillAuraActive = not State.isKillAuraActive UI.KillAuraBtn.Text = "Melee Kill Aura: " .. (State.isKillAuraActive and "ON" or "OFF") UI.KillAuraBtn.BackgroundColor3 = State.isKillAuraActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("ReachBtn", UI.ReachBtn, function() State.isReachActive = not State.isReachActive UI.ReachBtn.Text = "Melee Range Extender: " .. (State.isReachActive and "ON" or "OFF") UI.ReachBtn.BackgroundColor3 = State.isReachActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if not State.isReachActive then for tool, props in pairs(Cache.originalToolProps) do if tool and tool.Parent then local handle = tool:FindFirstChild("Handle") if handle then handle.Size = props.Size; handle.Transparency = props.Transparency; handle.Massless = props.Massless local vis = handle:FindFirstChild("ReachVisual"); if vis then vis:Destroy() end end end end table.clear(Cache.originalToolProps) end end) Keybindings.connectButton("StealEnemyBtn", UI.StealEnemyBtn, function() State.isStealEnemyActive = not State.isStealEnemyActive UI.StealEnemyBtn.Text = "Steal Enemy Ships: " .. (State.isStealEnemyActive and "ON" or "OFF") UI.StealEnemyBtn.BackgroundColor3 = State.isStealEnemyActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if State.isStealEnemyActive then setStealPause(false) end if State.isStealEnemyActive and State.isStealFriendlyActive then State.isStealFriendlyActive = false; UI.StealFriendlyBtn.Text = "Steal Friendly Ships: OFF" UI.StealFriendlyBtn.BackgroundColor3 = Color3.fromRGB(170, 50, 50) end end) Keybindings.connectButton("StealFriendlyBtn", UI.StealFriendlyBtn, function() State.isStealFriendlyActive = not State.isStealFriendlyActive UI.StealFriendlyBtn.Text = "Steal Friendly Ships: " .. (State.isStealFriendlyActive and "ON" or "OFF") UI.StealFriendlyBtn.BackgroundColor3 = State.isStealFriendlyActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if State.isStealFriendlyActive then setStealPause(false) end if State.isStealFriendlyActive and State.isStealEnemyActive then State.isStealEnemyActive = false; UI.StealEnemyBtn.Text = "Steal Enemy Ships: OFF" UI.StealEnemyBtn.BackgroundColor3 = Color3.fromRGB(170, 50, 50) end end) Keybindings.connectButton("PauseStealBtn", UI.PauseStealBtn, function() setStealPause() end) Keybindings.connectButton("StealOpenBtn", UI.StealOpenBtn, tpToNextOpenShip) Keybindings.connectButton("SpeedBtn", UI.SpeedBtn, function() State.isSpeedBoosting = not State.isSpeedBoosting UI.SpeedBtn.Text = "Ship Speed Boost: " .. (State.isSpeedBoosting and "ON" or "OFF") UI.SpeedBtn.BackgroundColor3 = State.isSpeedBoosting and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("FlyBtn", UI.FlyBtn, function() State.isFlying = not State.isFlying UI.FlyBtn.Text = "Ship Flight: " .. (State.isFlying and "ON" or "OFF") UI.FlyBtn.BackgroundColor3 = State.isFlying and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if not State.isFlying then lockedAltitude = nil; if activeFlyBrake then activeFlyBrake:Destroy(); activeFlyBrake = nil end end end) Keybindings.connectButton("AntiSinkBtn", UI.AntiSinkBtn, function() State.isAntiSinkActive = not State.isAntiSinkActive UI.AntiSinkBtn.Text = "Ship Anti-Sink: " .. (State.isAntiSinkActive and "ON" or "OFF") UI.AntiSinkBtn.BackgroundColor3 = State.isAntiSinkActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("ShipNoClipBtn", UI.ShipNoClipBtn, function() State.isShipNoClipActive = not State.isShipNoClipActive UI.ShipNoClipBtn.Text = "Ship No-Clip: " .. (State.isShipNoClipActive and "ON" or "OFF") UI.ShipNoClipBtn.BackgroundColor3 = State.isShipNoClipActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if not State.isShipNoClipActive then for part in pairs(Cache.originalShipCollisions) do if part and part.Parent then part.CanCollide = true end end table.clear(Cache.originalShipCollisions) end end) Keybindings.connectButton("ESPBtn", UI.ESPBtn, function() State.isESPActive = not State.isESPActive UI.ESPBtn.Text = "Enemy Ship ESP: " .. (State.isESPActive and "ON" or "OFF") UI.ESPBtn.BackgroundColor3 = State.isESPActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("AllyESPBtn", UI.AllyESPBtn, function() State.isAllyESPActive = not State.isAllyESPActive UI.AllyESPBtn.Text = "Ally Ship Highlight: " .. (State.isAllyESPActive and "ON" or "OFF") UI.AllyESPBtn.BackgroundColor3 = State.isAllyESPActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("PlayerESPBtn", UI.PlayerESPBtn, function() State.isPlayerESPActive = not State.isPlayerESPActive UI.PlayerESPBtn.Text = "Enemy Player ESP: " .. (State.isPlayerESPActive and "ON" or "OFF") UI.PlayerESPBtn.BackgroundColor3 = State.isPlayerESPActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("AllyPlayerESPBtn", UI.AllyPlayerESPBtn, function() State.isAllyPlayerESPActive = not State.isAllyPlayerESPActive UI.AllyPlayerESPBtn.Text = "Ally Player Highlight: " .. (State.isAllyPlayerESPActive and "ON" or "OFF") UI.AllyPlayerESPBtn.BackgroundColor3 = State.isAllyPlayerESPActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("FullbrightBtn", UI.FullbrightBtn, function() State.isFullbrightActive = not State.isFullbrightActive UI.FullbrightBtn.Text = "Fullbright / Clear Fog: " .. (State.isFullbrightActive and "ON" or "OFF") UI.FullbrightBtn.BackgroundColor3 = State.isFullbrightActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if not State.isFullbrightActive then Lighting.Ambient = origLighting.Ambient; Lighting.ColorShift_Bottom = origLighting.ColorShift_Bottom; Lighting.ColorShift_Top = origLighting.ColorShift_Top Lighting.FogEnd = origLighting.FogEnd; Lighting.GlobalShadows = origLighting.GlobalShadows; Lighting.ClockTime = origLighting.ClockTime end end) Keybindings.connectButton("ModDetectorBtn", UI.ModDetectorBtn, function() State.isModDetectorActive = not State.isModDetectorActive UI.ModDetectorBtn.Text = "Mod/Staff Detector: " .. (State.isModDetectorActive and "ON" or "OFF") UI.ModDetectorBtn.BackgroundColor3 = State.isModDetectorActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) updateModWarning() end) Keybindings.connectButton("ModPanicToggleBtn", UI.ModPanicToggleBtn, function() State.isModPanicActive = not State.isModPanicActive UI.ModPanicToggleBtn.Text = "Auto Panic on Staff: " .. (State.isModPanicActive and "ON" or "OFF") UI.ModPanicToggleBtn.BackgroundColor3 = State.isModPanicActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if State.isModPanicActive then for _, p in ipairs(Players:GetPlayers()) do if p ~= player and Cache.knownMods[p.UserId] then triggerPanic(p.Name) break end end end end) Keybindings.connectButton("FreecamBtn", UI.FreecamBtn, toggleFreecam) Keybindings.connectButton("FreecamTPBtn", UI.FreecamTPBtn, teleportToFreecam) Keybindings.connectButton("RetryIgnoredChestsBtn", UI.RetryIgnoredChestsBtn, ChestFarm.resetIgnored) Keybindings.connectButton("FarmBtn", UI.FarmBtn, function() State.isFarming = not State.isFarming UI.FarmBtn.Text = State.isFarming and "Stop Chest Farm" or "Instant Chest Farm" UI.FarmBtn.BackgroundColor3 = State.isFarming and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if State.isFarming then farmChests() end end) --// ACHIEVEMENT UNLOCKER EXECUTION local isUnlockingAchievements = false Keybindings.connectButton("UnlockAchievementsBtn", UI.UnlockAchievementsBtn, function() if isUnlockingAchievements then return end isUnlockingAchievements = true local char = player.Character local hrp = char and char:FindFirstChild("HumanoidRootPart") local hum = char and char:FindFirstChildOfClass("Humanoid") if not hrp or not hum then isUnlockingAchievements = false return end local originalCFrame = hrp.CFrame if hum.Sit then hum.Sit = false task.wait(0.05) end local achievements = { { Name = "Banana", CF = CFrame.new(-340.56, 14.95, 1422.37) }, { Name = "Lava Spike", CF = CFrame.new(135.78, 51.63, 286.59) }, { Name = "Pudinzo", CF = CFrame.new(-2196.76, 5.00, 1415.21) }, { Name = "Sand Castle", CF = CFrame.new(-1539.94, 16.89, 1376.99) }, { Name = "Cola", CF = CFrame.new(-42.42, 16.61, 1069.76) }, { Name = "Nat's Shop", CF = CFrame.new(-1038.60, 15.80, 1050.10) }, { Name = "Sam's Jail", CF = CFrame.new(176.74, 61.51, 266.57) }, { Name = "Ship in Bottle", CF = CFrame.new(-996.97, 16.32, 1023.13) } } task.spawn(function() for i, ach in ipairs(achievements) do UI.UnlockAchievementsBtn.Text = string.format("[%d/8] %s...", i, ach.Name) UI.UnlockAchievementsBtn.BackgroundColor3 = Color3.fromRGB(180, 130, 0) char = player.Character hrp = char and char:FindFirstChild("HumanoidRootPart") if hrp then hrp.CFrame = ach.CF hrp.AssemblyLinearVelocity = Vector3.zero if typeof(firetouchinterest) == "function" then for _, part in ipairs(workspace:GetPartBoundsInBox(ach.CF, Vector3.new(15, 15, 15))) do if part ~= hrp and not part:IsDescendantOf(char) then firetouchinterest(hrp, part, 0) firetouchinterest(hrp, part, 1) end end end end task.wait(0.35) end char = player.Character hrp = char and char:FindFirstChild("HumanoidRootPart") if hrp and originalCFrame then hrp.CFrame = originalCFrame hrp.AssemblyLinearVelocity = Vector3.zero end UI.UnlockAchievementsBtn.Text = "All 8 Achievements Unlocked!" UI.UnlockAchievementsBtn.BackgroundColor3 = Color3.fromRGB(0, 170, 0) task.delay(2.5, function() UI.UnlockAchievementsBtn.Text = "Unlock All Achievement Boats" UI.UnlockAchievementsBtn.BackgroundColor3 = Color3.fromRGB(0, 140, 70) isUnlockingAchievements = false end) end) end) Keybindings.connectButton("TreasureHitboxBtn", UI.TreasureHitboxBtn, function() State.isTreasureHitboxActive = not State.isTreasureHitboxActive UI.TreasureHitboxBtn.Text = "Expand Gold Hitbox: " .. (State.isTreasureHitboxActive and "ON" or "OFF") UI.TreasureHitboxBtn.BackgroundColor3 = State.isTreasureHitboxActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if not State.isTreasureHitboxActive then restoreTreasureHitboxes() else task.spawn(updateTreasureHitboxes) end end) Keybindings.connectButton("ClickTPBtn", UI.ClickTPBtn, function() State.isClickTPActive = not State.isClickTPActive UI.ClickTPBtn.Text = "Mouse Teleport: " .. (State.isClickTPActive and "ON" or "OFF") UI.ClickTPBtn.BackgroundColor3 = State.isClickTPActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("CharSpeedBtn", UI.CharSpeedBtn, function() State.isCharSpeedActive = not State.isCharSpeedActive UI.CharSpeedBtn.Text = "Player WalkSpeed Override: " .. (State.isCharSpeedActive and "ON" or "OFF") UI.CharSpeedBtn.BackgroundColor3 = State.isCharSpeedActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if not State.isCharSpeedActive and player.Character and player.Character:FindFirstChildOfClass("Humanoid") then player.Character:FindFirstChildOfClass("Humanoid").WalkSpeed = 16 end end) Keybindings.connectButton("CharJumpBtn", UI.CharJumpBtn, function() State.isCharJumpActive = not State.isCharJumpActive UI.CharJumpBtn.Text = "Player JumpPower Override: " .. (State.isCharJumpActive and "ON" or "OFF") UI.CharJumpBtn.BackgroundColor3 = State.isCharJumpActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if not State.isCharJumpActive and player.Character and player.Character:FindFirstChildOfClass("Humanoid") then player.Character:FindFirstChildOfClass("Humanoid").JumpPower = 50 end end) Keybindings.connectButton("InfJumpBtn", UI.InfJumpBtn, function() State.isInfJumpActive = not State.isInfJumpActive UI.InfJumpBtn.Text = "Infinite Jump: " .. (State.isInfJumpActive and "ON" or "OFF") UI.InfJumpBtn.BackgroundColor3 = State.isInfJumpActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("PlayerNoClipBtn", UI.PlayerNoClipBtn, TogglePlayerNoClip) Keybindings.connectButton("ZoomUncapBtn", UI.ZoomUncapBtn, function() State.isZoomUncapActive = not State.isZoomUncapActive if State.isZoomUncapActive then UI.ZoomUncapBtn.Text = "Uncap Camera Zoom: ON" UI.ZoomUncapBtn.BackgroundColor3 = Color3.fromRGB(0, 170, 0) player.CameraMaxZoomDistance = 9999 else UI.ZoomUncapBtn.Text = "Uncap Camera Zoom: OFF" UI.ZoomUncapBtn.BackgroundColor3 = Color3.fromRGB(170, 50, 50) player.CameraMaxZoomDistance = originalMaxZoomDistance end end) Keybindings.connectButton("AntiRagdollBtn", UI.AntiRagdollBtn, function() State.isAntiRagdollActive = not State.isAntiRagdollActive UI.AntiRagdollBtn.Text = "Anti-Ragdoll / Stun: " .. (State.isAntiRagdollActive and "ON" or "OFF") UI.AntiRagdollBtn.BackgroundColor3 = State.isAntiRagdollActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("AntiSitBtn", UI.AntiSitBtn, function() State.isAntiSitActive = not State.isAntiSitActive UI.AntiSitBtn.Text = "Anti-Sit / Anti-Seat: " .. (State.isAntiSitActive and "ON" or "OFF") UI.AntiSitBtn.BackgroundColor3 = State.isAntiSitActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) local char = player.Character local hum = char and char:FindFirstChildOfClass("Humanoid") if hum then if State.isAntiSitActive then hum:SetStateEnabled(Enum.HumanoidStateType.Seated, false) if hum.Sit then hum.Sit = false hum:ChangeState(Enum.HumanoidStateType.Jumping) end else hum:SetStateEnabled(Enum.HumanoidStateType.Seated, true) end end end) Keybindings.connectButton("AntiVoidBtn", UI.AntiVoidBtn, function() State.isAntiVoidActive = not State.isAntiVoidActive UI.AntiVoidBtn.Text = "Anti-Void/Water Death: " .. (State.isAntiVoidActive and "ON" or "OFF") UI.AntiVoidBtn.BackgroundColor3 = State.isAntiVoidActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("AutoHPReturnBtn", UI.AutoHPReturnBtn, function() State.isAutoHPReturnActive = not State.isAutoHPReturnActive if State.isAutoHPReturnActive then UI.AutoHPReturnBtn.Text = "Auto Return on Low HP: ON" UI.AutoHPReturnBtn.BackgroundColor3 = Color3.fromRGB(0, 170, 0) else UI.AutoHPReturnBtn.Text = "Auto Return on Low HP: OFF" UI.AutoHPReturnBtn.BackgroundColor3 = Color3.fromRGB(170, 50, 50) end end) Keybindings.connectButton("SetCTFCapturePointBtn", UI.SetCTFCapturePointBtn, FlagCapture.saveCapturePoint) Keybindings.connectButton("CTFModeBtn", UI.CTFModeBtn, function() local mode = FlagCapture.cycleModeOverride() local detected, reason = FlagCapture.detectMode() FlagCapture.mode = detected FlagCapture.modeReason = reason FlagCapture.setStatus("CTF Mode override: " .. mode .. "\nActive mode: " .. detected .. " - " .. reason) Toast.push("CTF Mode: " .. mode, detected .. " active", Toast.INFO, 3) end) Keybindings.connectButton("CTFRescanBtn", UI.CTFRescanBtn, function() FlagCapture.homePositions = {} FlagCapture.clearBaseCache() FlagCapture.home = nil FlagCapture.target = nil FlagCapture.targetSide = nil FlagCapture.lastScan = -math.huge FlagCapture.flags = scanForFlags() local detected, reason = FlagCapture.detectMode() FlagCapture.mode = detected FlagCapture.modeReason = reason local carryable = {red = 0, blue = 0, neutral = 0} local totals = {red = 0, blue = 0, neutral = 0} for side, flags in pairs(FlagCapture.flags) do totals[side] = #flags for _, flag in ipairs(flags) do if FlagCapture.isCarryableFlag(flag) then carryable[side] = carryable[side] + 1 end end end local bases = FlagCapture.teamBases() local baseText = "no team bases found" if bases.red and bases.blue then baseText = string.format("bases %.0f studs apart", (bases.red - bases.blue).Magnitude) elseif bases.red or bases.blue then baseText = "only one team base found" end FlagCapture.setStatus(string.format( "Rescan done. Mode: %s - %s\nFlags found: red %d (%d carryable), blue %d (%d carryable), neutral %d (%d carryable). %s.", detected, reason, totals.red, carryable.red, totals.blue, carryable.blue, totals.neutral, carryable.neutral, baseText)) Toast.push("CTF rescan", detected .. " mode", Toast.INFO, 3) end) Keybindings.connectButton("CTFChaseBtn", UI.CTFChaseBtn, function() State.isCTFChaseCarrier = not State.isCTFChaseCarrier UI.CTFChaseBtn.Text = "Chase Flag Carrier: " .. (State.isCTFChaseCarrier and "ON" or "OFF") UI.CTFChaseBtn.BackgroundColor3 = State.isCTFChaseCarrier and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("SaveLocationBtn", UI.SaveLocationBtn, SavedPositions.saveCurrentLocation) Keybindings.connectButton("TeleportSavedLocationBtn", UI.TeleportSavedLocationBtn, SavedPositions.teleportToSavedLocation) Keybindings.connectButton("AutoCTFBtn", UI.AutoCTFBtn, function() FlagCapture.setEnabled(not State.isAutoCTFActive) end) Keybindings.connectButton("CTFHogFlagBtn", UI.CTFHogFlagBtn, function() FlagCapture.setHoldEnabled(not State.isCTFHogFlagActive) end) Keybindings.connectButton("CTFLockAtLimitBtn", UI.CTFLockAtLimitBtn, function() State.isCTFLockAtLimitActive = not State.isCTFLockAtLimitActive if State.isCTFLockAtLimitActive then UI.CTFLockAtLimitBtn.Text = "Hold Flag After Capture Limit: ON" UI.CTFLockAtLimitBtn.BackgroundColor3 = Color3.fromRGB(0, 170, 0) else UI.CTFLockAtLimitBtn.Text = "Hold Flag After Capture Limit: OFF" UI.CTFLockAtLimitBtn.BackgroundColor3 = Color3.fromRGB(170, 50, 50) end end) Keybindings.connectButton("CTFRescanCounterBtn", UI.CTFRescanCounterBtn, function() FlagCapture.scoreSource = nil FlagCapture.scoreLastValue = nil FlagCapture.lastScoreScan = -math.huge FlagCapture.pollCaptureScore(os.clock()) end) Keybindings.connectButton("CTFResetCountBtn", UI.CTFResetCountBtn, function() FlagCapture.resetRun() FlagCapture.setStatus("Capture count reset to zero.") end) Keybindings.connectButton("AutoFortBtn", UI.AutoFortBtn, function() State.isAutoFortActive = not State.isAutoFortActive UI.AutoFortBtn.Text = "Auto Fort (Hold Flags): " .. (State.isAutoFortActive and "ON" or "OFF") UI.AutoFortBtn.BackgroundColor3 = State.isAutoFortActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("PlayerFlyBtn", UI.PlayerFlyBtn, function() State.isPlayerFlyActive = not State.isPlayerFlyActive UI.PlayerFlyBtn.Text = "Player Fly: " .. (State.isPlayerFlyActive and "ON" or "OFF") UI.PlayerFlyBtn.BackgroundColor3 = State.isPlayerFlyActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if not State.isPlayerFlyActive then if playerFlyBrake then playerFlyBrake:Destroy(); playerFlyBrake = nil end if playerFlyGyro then playerFlyGyro:Destroy(); playerFlyGyro = nil end local char = player.Character if char then local hum = char:FindFirstChildOfClass("Humanoid") if hum then hum.PlatformStand = false end end end end) Keybindings.connectButton("FlingToggleBtn", UI.FlingToggleBtn, function() State.isFlingActive = not State.isFlingActive UI.FlingToggleBtn.Text = "Ultimate Fling: " .. (State.isFlingActive and "ON" or "OFF") UI.FlingToggleBtn.BackgroundColor3 = State.isFlingActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if not State.isFlingActive then restoreFlingStates() end end) Keybindings.connectButton("FlingModeBtn", UI.FlingModeBtn, function() Config.FLING_MODE_INDEX = (Config.FLING_MODE_INDEX % #flingModes) + 1 UI.FlingModeBtn.Text = "Mode: " .. flingModes[Config.FLING_MODE_INDEX] end) Keybindings.connectButton("FlingAttachBtn", UI.FlingAttachBtn, function() State.isFlingAttachActive = not State.isFlingAttachActive UI.FlingAttachBtn.Text = "Attach to Target: " .. (State.isFlingAttachActive and "ON" or "OFF") UI.FlingAttachBtn.BackgroundColor3 = State.isFlingAttachActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if State.isFlingAttachActive then local char = player.Character; local hum = char and char:FindFirstChildOfClass("Humanoid") local hrp = char and char:FindFirstChild("HumanoidRootPart"); local seat = hum and hum.SeatPart local vehicleRoot = seat and (seat.AssemblyRootPart or seat) if vehicleRoot then State.flingStartData = {Type = "Vehicle", Root = vehicleRoot, CFrame = vehicleRoot.CFrame, Model = seat:FindFirstAncestorOfClass("Model")} elseif hrp then State.flingStartData = {Type = "Character", Root = hrp, CFrame = hrp.CFrame} end else State.flingStartData = nil end end) Keybindings.connectButton("SpectateBtn", UI.SpectateBtn, function() State.isSpectating = not State.isSpectating if State.isSpectating then local targetPlr = getFlingTargetPlayer(State.flingTargetStr) if targetPlr and targetPlr.Character and targetPlr.Character:FindFirstChild("Humanoid") then UI.SpectateBtn.Text = "Spectate Target: ON"; UI.SpectateBtn.BackgroundColor3 = Color3.fromRGB(0, 170, 0) workspace.CurrentCamera.CameraSubject = targetPlr.Character.Humanoid else State.isSpectating = false UI.SpectateBtn.Text = "Target Not Found!"; task.wait(1.5); UI.SpectateBtn.Text = "Spectate Target: OFF" end else UI.SpectateBtn.Text = "Spectate Target: OFF"; UI.SpectateBtn.BackgroundColor3 = Color3.fromRGB(170, 50, 50) if player.Character and player.Character:FindFirstChild("Humanoid") then workspace.CurrentCamera.CameraSubject = player.Character.Humanoid end end end) Keybindings.connectButton("AutoReturnBtn", UI.AutoReturnBtn, function() State.isAutoReturnActive = not State.isAutoReturnActive UI.AutoReturnBtn.Text = "Auto-Return on Success: " .. (State.isAutoReturnActive and "ON" or "OFF") UI.AutoReturnBtn.BackgroundColor3 = State.isAutoReturnActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("TPToTargetBtn", UI.TPToTargetBtn, function() teleportToTargetPlayer() end) Keybindings.connectButton("TPSafetyBtn", UI.TPSafetyBtn, function() local char = player.Character; local hrp = char and char:FindFirstChild("HumanoidRootPart") local hum = char and char:FindFirstChildOfClass("Humanoid") if not hrp then return end if State.isFlingAttachActive then State.isFlingAttachActive = false UI.FlingAttachBtn.Text = "Attach to Target: OFF"; UI.FlingAttachBtn.BackgroundColor3 = Color3.fromRGB(170, 50, 50) end if hum and hum.Sit then hum.Sit = false task.wait(0.05) end hrp.CFrame = getSafeSpawnLocation() hrp.AssemblyLinearVelocity = Vector3.zero end) Keybindings.connectButton("FlingAuraBtn", UI.FlingAuraBtn, function() State.isFlingAuraActive = not State.isFlingAuraActive UI.FlingAuraBtn.Text = "Fling Aura: " .. (State.isFlingAuraActive and "ON" or "OFF") UI.FlingAuraBtn.BackgroundColor3 = State.isFlingAuraActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) Keybindings.connectButton("FlingAuraVisBtn", UI.FlingAuraVisBtn, function() State.isFlingAuraVisActive = not State.isFlingAuraVisActive UI.FlingAuraVisBtn.Text = "Visualize Aura Range: " .. (State.isFlingAuraVisActive and "ON" or "OFF") UI.FlingAuraVisBtn.BackgroundColor3 = State.isFlingAuraVisActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) if State.isFlingAuraVisActive then if not State.flingAuraVisualizer then State.flingAuraVisualizer = createDualSidedSphere("FlingAuraVisualizer", Color3.fromRGB(200, 50, 255)) end else if State.flingAuraVisualizer then State.flingAuraVisualizer:Destroy(); State.flingAuraVisualizer = nil end end end) Keybindings.connectButton("FlingAuraTeamBtn", UI.FlingAuraTeamBtn, function() State.isFlingAuraTeamActive = not State.isFlingAuraTeamActive UI.FlingAuraTeamBtn.Text = "Aura Includes Teammates: " .. (State.isFlingAuraTeamActive and "ON" or "OFF") UI.FlingAuraTeamBtn.BackgroundColor3 = State.isFlingAuraTeamActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) end) -- TELEPORT BUTTON BINDINGS Keybindings.connectButton("TPBlueSpawnBtn", UI.TPBlueSpawnBtn, function() local cf = getTeamSpawnLocation("blue") if cf then teleportToLocation(cf) else pcall(function() game:GetService("StarterGui"):SetCore("SendNotification", { Title = "Teleport", Text = "Blue Spawn location not found.", Duration = 3 }) end) end end) Keybindings.connectButton("TPRedSpawnBtn", UI.TPRedSpawnBtn, function() local cf = getTeamSpawnLocation("red") if cf then teleportToLocation(cf) else pcall(function() game:GetService("StarterGui"):SetCore("SendNotification", { Title = "Teleport", Text = "Red Spawn location not found.", Duration = 3 }) end) end end) Keybindings.connectButton("TPCenterFortBtn", UI.TPCenterFortBtn, function() teleportToLocation(CFrame.new(-1013.25, 20.55, 1049.49)) end) Keybindings.connectButton("TPNatsShopBtn", UI.TPNatsShopBtn, function() teleportToLocation(CFrame.new(-1038.60, 15.80, 1050.10)) end) Keybindings.connectButton("TPSamsJailBtn", UI.TPSamsJailBtn, function() teleportToLocation(CFrame.new(176.74, 61.51, 266.57)) end) Keybindings.connectButton("TPLavaSpikeBtn", UI.TPLavaSpikeBtn, function() teleportToLocation(CFrame.new(135.78, 51.63, 286.59)) end) Keybindings.connectButton("TPBananaBtn", UI.TPBananaBtn, function() teleportToLocation(CFrame.new(-340.56, 14.95, 1422.37)) end) Keybindings.connectButton("TPPudinzoBtn", UI.TPPudinzoBtn, function() teleportToLocation(CFrame.new(-2196.76, 5.00, 1415.21)) end) Keybindings.connectButton("TPSandCastleBtn", UI.TPSandCastleBtn, function() teleportToLocation(CFrame.new(-1539.94, 16.89, 1376.99)) end) Keybindings.connectButton("TPColaBtn", UI.TPColaBtn, function() teleportToLocation(CFrame.new(-42.42, 16.61, 1069.76)) end) Keybindings.connectButton("TPShipBottleBtn", UI.TPShipBottleBtn, function() teleportToLocation(CFrame.new(-996.97, 16.32, 1023.13)) end) Keybindings.connectButton("CustomChatBtn", UI.CustomChatBtn, function() State.isCustomChatActive = not State.isCustomChatActive UI.CustomChatBtn.Text = "Custom Chat Display: " .. (State.isCustomChatActive and "ON" or "OFF") UI.CustomChatBtn.BackgroundColor3 = State.isCustomChatActive and Color3.fromRGB(0, 170, 0) or Color3.fromRGB(170, 50, 50) ChatGui.Visible = State.isCustomChatActive end) Keybindings.connectButton("SaveConfigBtn", UI.SaveConfigBtn, function() if type(writefile) ~= "function" then notifyUser("Config", "This environment does not provide writefile. Settings were not saved.", 6) return end local cleanState = {} for name, value in pairs(State) do if type(value) == "boolean" or type(value) == "number" or type(value) == "string" then cleanState[name] = value end end local saveTable = { ConfigVersion = 9, Config = Config, State = cleanState, Keybinds = Keybindings.exportBindings(), SavedLocation = SavedPositions.encode(SavedPositions.location), CTFCapturePoints = FlagCapture.exportCapturePoints(), BringShipsPoint = SavedPositions.encode(ShipBring.point), } local saved, failure = pcall(function() writefile(CONFIG_FILENAME, HttpService:JSONEncode(saveTable)) end) if saved then notifyUser("Config", "Settings, key bindings, and saved locations saved.", 4) Keybindings.setStatus("Settings, key bindings, and saved locations saved.") else notifyUser("Config", "Save failed: " .. tostring(failure), 6) end end) Keybindings.connectButton("LoadConfigBtn", UI.LoadConfigBtn, function() if type(readfile) ~= "function" or type(isfile) ~= "function" then notifyUser("Config", "This environment does not provide readfile/isfile. Settings were not loaded.", 6) return end local loaded, result = pcall(function() if not isfile(CONFIG_FILENAME) then error("Config file not found.") end return HttpService:JSONDecode(readfile(CONFIG_FILENAME)) end) if not loaded or type(result) ~= "table" then notifyUser("Config", "Could not load config: " .. tostring(result), 6) return end local loadedBindings, invalidBindings = Keybindings.loadBindings(result.Keybinds) local invalidLocations = FlagCapture.loadCapturePoints(result.CTFCapturePoints) if result.BringShipsPoint ~= nil then local point, valid = SavedPositions.decode(result.BringShipsPoint) if valid then ShipBring.point = point else invalidLocations = invalidLocations + 1 end end if result.SavedLocation ~= nil then local location, valid = SavedPositions.decode(result.SavedLocation) if valid then SavedPositions.location = location else invalidLocations = invalidLocations + 1 end end SavedPositions.refreshLabel() if type(result.Config) == "table" then for name, value in pairs(result.Config) do local valid = Config[name] ~= nil and type(Config[name]) == type(value) if type(value) == "number" and (value ~= value or math.abs(value) == math.huge) then valid = false end if valid then Config[name] = value if UI.Inputs[name] then UI.Inputs[name].Text = tostring(value) end end end end if (tonumber(result.ConfigVersion) or 0) < 7 and type(result.Config) == "table" then if result.Config.CHEST_RETRY_EXTRA_WAIT == 0.15 then Config.CHEST_RETRY_EXTRA_WAIT = 0.75 UI.Inputs.CHEST_RETRY_EXTRA_WAIT.Text = tostring(Config.CHEST_RETRY_EXTRA_WAIT) end if result.Config.CHEST_MAX_PICKUP_WAIT == 3 then Config.CHEST_MAX_PICKUP_WAIT = 8 UI.Inputs.CHEST_MAX_PICKUP_WAIT.Text = tostring(Config.CHEST_MAX_PICKUP_WAIT) end end if type(result.State) == "table" then for name, value in pairs(result.State) do local valid = State[name] ~= nil and type(State[name]) == type(value) if type(value) == "number" and (value ~= value or math.abs(value) == math.huge) then valid = false end if valid then State[name] = value if ToggleUI[name] and type(value) == "boolean" then local statusText = "OFF" local statusColor = Color3.fromRGB(170, 50, 50) if value then statusText = "ON" statusColor = Color3.fromRGB(0, 170, 0) end ToggleUI[name].Btn.Text = ToggleUI[name].Text .. ": " .. statusText ToggleUI[name].Btn.BackgroundColor3 = statusColor end end end end local applied, failure = pcall(ApplyStateSideEffects) local message = "Settings loaded. Key bindings restored: " .. tostring(loadedBindings) .. "." if result.Keybinds == nil then message = "Settings loaded. This older config has no key bindings; your current bindings were kept." end if invalidBindings > 0 then message = message .. " Invalid bindings skipped: " .. tostring(invalidBindings) .. "." end if invalidLocations > 0 then message = message .. " Invalid saved locations skipped: " .. tostring(invalidLocations) .. "." end if not applied then message = message .. " Some settings could not be applied: " .. tostring(failure) end notifyUser("Config", message, 7) Keybindings.setStatus(message) end) -- CLOSE BUTTON local function closeUtility() if Runtime.closed then return end Runtime.closed = true ChestFarm.stopMovement() ColaDelivery.cancel() ShipBring.setEnabled(false) LoopKillEnemies.setEnabled(false) FlagCapture.resetCycle() ShipTurn.cancel() Keybindings.cancelCapture() table.clear(Cache.knownMods) table.clear(StaffDetector.playerRecords) for k, v in pairs(State) do if type(v) == "boolean" then State[k] = false end end State.flingStartData = nil; lockedAltitude = nil local cam = workspace.CurrentCamera if cam then cam.CameraType = Enum.CameraType.Custom if player.Character and player.Character:FindFirstChild("Humanoid") then cam.CameraSubject = player.Character.Humanoid end end UserInputService.MouseBehavior = Enum.MouseBehavior.Default restoreHitboxes(); restoreFlingStates(); restoreTreasureHitboxes() for tool, props in pairs(Cache.originalToolProps) do if tool and tool.Parent then local handle = tool:FindFirstChild("Handle") if handle then handle.Size = props.Size; handle.Transparency = props.Transparency; handle.Massless = props.Massless local vis = handle:FindFirstChild("ReachVisual"); if vis then vis:Destroy() end end end end table.clear(Cache.originalToolProps) if activeFlyBrake then activeFlyBrake:Destroy(); activeFlyBrake = nil end if playerFlyBrake then playerFlyBrake:Destroy(); playerFlyBrake = nil end if playerFlyGyro then playerFlyGyro:Destroy(); playerFlyGyro = nil end if waterPlatform then waterPlatform:Destroy(); waterPlatform = nil end if State.mapFireVisualizer then State.mapFireVisualizer:Destroy(); State.mapFireVisualizer = nil end if State.flingAuraVisualizer then State.flingAuraVisualizer:Destroy(); State.flingAuraVisualizer = nil end Lighting.Ambient = origLighting.Ambient; Lighting.ColorShift_Bottom = origLighting.ColorShift_Bottom; Lighting.ColorShift_Top = origLighting.ColorShift_Top Lighting.FogEnd = origLighting.FogEnd; Lighting.GlobalShadows = origLighting.GlobalShadows; Lighting.ClockTime = origLighting.ClockTime if player.Character and player.Character:FindFirstChildOfClass("Humanoid") then local hum = player.Character:FindFirstChildOfClass("Humanoid") hum.WalkSpeed = 16 hum.JumpPower = 50 hum.PlatformStand = false hum:SetStateEnabled(Enum.HumanoidStateType.Seated, true) player.CameraMaxZoomDistance = originalMaxZoomDistance end if player.Character and player.Character:FindFirstChild("HumanoidRootPart") then player.Character.HumanoidRootPart.Anchored = false end for part in pairs(Cache.originalPlayerCollisions) do if part and part.Parent then part.CanCollide = true end end table.clear(Cache.originalPlayerCollisions) for part in pairs(Cache.originalShipCollisions) do if part and part.Parent then part.CanCollide = true end end table.clear(Cache.originalShipCollisions) table.clear(Cache.shipAffiliation); table.clear(Cache.shipOwner); table.clear(Cache.shipInitialParts) for p, hl in pairs(Cache.playerEspHighlights) do hl:Destroy() end for p, lbl in pairs(Cache.playerEspLabels) do lbl:Destroy() end for ship, hl in pairs(Cache.espHighlights) do hl:Destroy() end for ship, lbl in pairs(Cache.espLabels) do lbl:Destroy() end table.clear(Cache.espHighlights); table.clear(Cache.espLabels) table.clear(Cache.playerEspHighlights); table.clear(Cache.playerEspLabels) if ModWarningLabel then ModWarningLabel:Destroy() end if espContainer then espContainer:Destroy() end if ChatGui then ChatGui:Destroy() end table.clear(Toast.active) Fire.clear() if OverlayGui then OverlayGui:Destroy() end for _, conn in ipairs(Connections) do if typeof(conn) == "RBXScriptConnection" then conn:Disconnect() end end if ScreenGui and ScreenGui.Parent then ScreenGui:Destroy() end end Keybindings.connectButton("CloseBtn", UI.CloseBtn, closeUtility) table.insert(Connections, ScreenGui.Destroying:Connect(closeUtility)) do local shutdownFunction = Instance.new("BindableFunction") shutdownFunction.Name = "ShutdownUtility" shutdownFunction.OnInvoke = closeUtility shutdownFunction.Parent = ScreenGui end Keybindings.registerAction("ShowHideMenu", "Menu", "Show or hide menu", "F4", function() guiVisible = not guiVisible ScreenGui.Enabled = guiVisible end, false) -- The overlay (toasts + ship HUD) stays up when the menu is hidden, so report -- the menu's own visibility rather than a button caption. Keybindings.actions["ShowHideMenu"].stateProbe = function() return guiVisible end Keybindings.connectButton("ManualPanic", UI.ManualPanicBtn, function() triggerPanic(nil) end) -- Existing shortcuts for merged tabs still open their original controls. Keybindings.registerAction("Tab_Hitboxes", "Menu", "Open player hitboxes in Combat", false, function() selectTab("Combat", UI.HitboxSection) end, false) Keybindings.registerAction("Tab_Travel", "Menu", "Open team management in Player", false, function() selectTab("Player", UI.TeamSection) end, false) Keybindings.registerAction("TeleportToMouse", "Teleport", "Teleport to mouse (enable Mouse Teleport first)", { Key = "MouseButton1", Control = true, }, function() if not State.isClickTPActive then return end local character = player.Character if not character then return end local rootPart = character:FindFirstChild("HumanoidRootPart") if not rootPart then return end local parameters = RaycastParams.new() parameters.FilterType = Enum.RaycastFilterType.Exclude local ignoredInstances = {character, espContainer} if State.isHitboxExpanderActive then for part in pairs(Cache.originalHitboxProps) do if part.Parent then table.insert(ignoredInstances, part) end end end if State.isTreasureHitboxActive then for part in pairs(Cache.originalTreasureProps) do if part.Parent then table.insert(ignoredInstances, part) end end end parameters.FilterDescendantsInstances = ignoredInstances local mouseRay = mouse.UnitRay local result = workspace:Raycast(mouseRay.Origin, mouseRay.Direction * 1000, parameters) local targetPosition = mouseRay.Origin + mouseRay.Direction * 1000 if result then targetPosition = result.Position end teleportToLocation(CFrame.new(targetPosition + Vector3.new(0, 4, 0))) end, false) Keybindings.registerAction("InfiniteJump", "Player", "Jump again (enable Infinite Jump first)", "Space", function() if not State.isInfJumpActive or not player.Character then return end local humanoid = player.Character:FindFirstChildOfClass("Humanoid") if humanoid then humanoid:ChangeState(Enum.HumanoidStateType.Jumping) end end, false) do local heldActions = { {"FreecamForward", "Freecam", "Move forward", "W"}, {"FreecamBackward", "Freecam", "Move backward", "S"}, {"FreecamLeft", "Freecam", "Move left", "A"}, {"FreecamRight", "Freecam", "Move right", "D"}, {"FreecamUp", "Freecam", "Move up", "Space"}, {"FreecamDown", "Freecam", "Move down", "LeftControl"}, {"FreecamFast", "Freecam", "Move faster", "LeftShift"}, {"FreecamLook", "Freecam", "Look around with mouse", "MouseButton2"}, {"PlayerFlyForward", "Player flight", "Fly forward", "W"}, {"PlayerFlyBackward", "Player flight", "Fly backward", "S"}, {"PlayerFlyLeft", "Player flight", "Fly left", "A"}, {"PlayerFlyRight", "Player flight", "Fly right", "D"}, {"PlayerFlyUp", "Player flight", "Fly up", "Space"}, {"PlayerFlyDown", "Player flight", "Fly down", "LeftControl"}, {"ShipBoostForward", "Ships", "Boost forward", "W"}, {"ShipBoostBackward", "Ships", "Boost backward", "S"}, {"ShipFlightUp", "Ships", "Raise ship", "LeftShift"}, {"ShipFlightDown", "Ships", "Lower ship", "LeftControl"}, } for _, entry in ipairs(heldActions) do Keybindings.registerAction(entry[1], entry[2], entry[3] .. " (hold)", entry[4], nil, true) end end table.insert(Connections, UserInputService.InputBegan:Connect(Keybindings.handleInputBegan)) table.insert(Connections, UserInputService.InputEnded:Connect(Keybindings.handleInputEnded)) table.insert(Connections, UserInputService.InputChanged:Connect(function(input, gameProcessed) if input.UserInputType == Enum.UserInputType.MouseWheel then Keybindings.handleWheel(input, gameProcessed) elseif Keybindings.thumbstickKeys[input.KeyCode] then Keybindings.handleThumbstick(input, gameProcessed) end end)) -- A disconnected controller must not leave its buttons stuck in the down table. table.insert(Connections, UserInputService.GamepadDisconnected:Connect(function() for keyName in pairs(Keybindings.downInputs) do if Keybindings.deviceOf(keyName) == "Gamepad" then Keybindings.downInputs[keyName] = nil end end end)) table.insert(Connections, UserInputService.TextBoxFocused:Connect(function() table.clear(Keybindings.downInputs) end)) table.insert(Connections, UserInputService.WindowFocusReleased:Connect(function() Keybindings.windowFocused = false Keybindings.cancelCapture() end)) table.insert(Connections, UserInputService.WindowFocused:Connect(function() Keybindings.windowFocused = true end)) table.insert(Connections, UserInputService.JumpRequest:Connect(function() if UserInputService:GetLastInputType() == Enum.UserInputType.Touch then Keybindings.runAction("InfiniteJump") end end)) function Keybindings.filterRows() if not UI.KeybindSearch then return end local searchText = string.lower(UI.KeybindSearch.Text) for actionId, row in pairs(Keybindings.rows) do local action = Keybindings.actions[actionId] local binding = Keybindings.bindings[actionId] local searchable = string.lower(action.category .. " " .. action.label .. " " .. Keybindings.formatBinding(binding) .. " " .. (binding and binding.Key or "unbound") .. " " .. Keybindings.deviceOf(binding and binding.Key)) row.frame.Visible = string.find(searchable, searchText, 1, true) ~= nil end end do createSectionHeader(KeybindsTab, "Key Bindings", 1) local instructions = Instance.new("TextLabel") instructions.Size = UDim2.new(0.96, 0, 0, 80) instructions.AutomaticSize = Enum.AutomaticSize.Y instructions.BackgroundTransparency = 1 instructions.TextColor3 = Color3.fromRGB(210, 215, 225) instructions.Font = Enum.Font.SourceSans instructions.TextSize = 15 instructions.TextWrapped = true instructions.TextXAlignment = Enum.TextXAlignment.Left instructions.TextYAlignment = Enum.TextYAlignment.Top instructions.Text = "Click a binding, then press a key, mouse button, mouse wheel notch, gamepad button, gamepad stick direction, or Ctrl/Shift/Alt combination. Release a modifier by itself to bind it alone.\n" .. "Roblox exposes only three mouse buttons to a client, so thumb buttons 4/5 must be mapped to a keyboard key in your mouse software first. Sticks bind as eight directions with a deadzone.\n" .. "Escape and Backspace can be bound. Click the same binding again to cancel; Clear removes it. Capture pauses this tool's shortcuts.\n" .. "Conflicts are flagged as you bind: a red '(!) conflict' means another press-once action uses the identical key combination and both will run. " .. "An amber '(!) shared key' means a hold action uses the same physical key; hold checks ignore extra modifiers, so both can react. " .. "Two hold actions sharing a key are not flagged - the W/A/S/D defaults are shared by freecam, player flight and ship boost on purpose.\n" .. "Changes apply immediately. Save and load them with Settings > Save/Load Config Settings." instructions.LayoutOrder = 2 instructions.Parent = KeybindsTab UI.KeybindConflictLabel = Instance.new("TextLabel") UI.KeybindConflictLabel.Size = UDim2.new(0.96, 0, 0, 24) UI.KeybindConflictLabel.AutomaticSize = Enum.AutomaticSize.Y UI.KeybindConflictLabel.BackgroundTransparency = 1 UI.KeybindConflictLabel.TextColor3 = Color3.fromRGB(120, 210, 145) UI.KeybindConflictLabel.Font = Enum.Font.SourceSansBold UI.KeybindConflictLabel.TextSize = 15 UI.KeybindConflictLabel.TextWrapped = true UI.KeybindConflictLabel.TextXAlignment = Enum.TextXAlignment.Left UI.KeybindConflictLabel.TextYAlignment = Enum.TextYAlignment.Top UI.KeybindConflictLabel.Text = "No conflicting bindings." UI.KeybindConflictLabel.LayoutOrder = 4 UI.KeybindConflictLabel.Parent = KeybindsTab UI.KeybindStatus = Instance.new("TextLabel") UI.KeybindStatus.Size = UDim2.new(0.96, 0, 0, 50) UI.KeybindStatus.AutomaticSize = Enum.AutomaticSize.Y UI.KeybindStatus.BackgroundTransparency = 1 UI.KeybindStatus.TextColor3 = Color3.fromRGB(250, 200, 105) UI.KeybindStatus.Font = Enum.Font.SourceSans UI.KeybindStatus.TextSize = 15 UI.KeybindStatus.TextWrapped = true UI.KeybindStatus.TextXAlignment = Enum.TextXAlignment.Left UI.KeybindStatus.Text = "Choose an action below. Existing shortcuts are the defaults." UI.KeybindStatus.LayoutOrder = 3 UI.KeybindStatus.Parent = KeybindsTab UI.KeybindSearch = Instance.new("TextBox") UI.KeybindSearch.Size = UDim2.new(0.96, 0, 0, 30) UI.KeybindSearch.BackgroundColor3 = Color3.fromRGB(38, 38, 42) UI.KeybindSearch.TextColor3 = Color3.new(1, 1, 1) UI.KeybindSearch.Font = Enum.Font.SourceSans UI.KeybindSearch.TextSize = 16 UI.KeybindSearch.Text = "" UI.KeybindSearch.PlaceholderText = "Search actions, tabs, or assigned keys" UI.KeybindSearch.ClearTextOnFocus = false UI.KeybindSearch.LayoutOrder = 5 UI.KeybindSearch.Parent = KeybindsTab UI.KeybindSearch:GetPropertyChangedSignal("Text"):Connect(Keybindings.filterRows) UI.KeybindSearch.Focused:Connect(Keybindings.cancelCapture) local sortedActions = table.clone(Keybindings.actionOrder) table.sort(sortedActions, function(firstId, secondId) local first = Keybindings.actions[firstId] local second = Keybindings.actions[secondId] return first.category .. first.label < second.category .. second.label end) for order, actionId in ipairs(sortedActions) do local action = Keybindings.actions[actionId] local row = Instance.new("Frame") row.Size = UDim2.new(0.96, 0, 0, 94) row.BackgroundColor3 = Color3.fromRGB(33, 35, 41) row.LayoutOrder = order + 10 row.Parent = KeybindsTab Instance.new("UICorner", row).CornerRadius = UDim.new(0, 5) local label = Instance.new("TextLabel") label.Size = UDim2.new(1, -12, 0, 34) label.Position = UDim2.new(0, 6, 0, 3) label.BackgroundTransparency = 1 label.TextColor3 = Color3.fromRGB(225, 230, 240) label.Font = Enum.Font.SourceSans label.TextSize = 15 label.TextScaled = true label.TextWrapped = true label.TextXAlignment = Enum.TextXAlignment.Left label.Text = action.category .. " / " .. action.label label.Parent = row local labelSize = Instance.new("UITextSizeConstraint") labelSize.MinTextSize = 10 labelSize.MaxTextSize = 15 labelSize.Parent = label local bindButton = Instance.new("TextButton") bindButton.Size = UDim2.new(1, -78, 0, 28) bindButton.Position = UDim2.new(0, 6, 0, 39) bindButton.TextColor3 = Color3.new(1, 1, 1) bindButton.Font = Enum.Font.SourceSansBold bindButton.TextSize = 14 bindButton.Parent = row Instance.new("UICorner", bindButton).CornerRadius = UDim.new(0, 4) local clearButton = Instance.new("TextButton") clearButton.Size = UDim2.new(0, 60, 0, 28) clearButton.Position = UDim2.new(1, -66, 0, 39) clearButton.BackgroundColor3 = Color3.fromRGB(105, 50, 50) clearButton.TextColor3 = Color3.new(1, 1, 1) clearButton.Font = Enum.Font.SourceSansBold clearButton.TextSize = 14 clearButton.Text = "Clear" clearButton.Parent = row Instance.new("UICorner", clearButton).CornerRadius = UDim.new(0, 4) local conflictLabel = Instance.new("TextLabel") conflictLabel.Size = UDim2.new(1, -12, 0, 20) conflictLabel.Position = UDim2.new(0, 6, 0, 70) conflictLabel.BackgroundTransparency = 1 conflictLabel.TextColor3 = Color3.fromRGB(255, 135, 135) conflictLabel.Font = Enum.Font.SourceSans conflictLabel.TextSize = 13 conflictLabel.TextWrapped = true conflictLabel.TextXAlignment = Enum.TextXAlignment.Left conflictLabel.TextYAlignment = Enum.TextYAlignment.Top conflictLabel.Text = "" conflictLabel.Visible = false conflictLabel.Parent = row bindButton.MouseButton1Click:Connect(function() Keybindings.beginCapture(actionId) end) clearButton.MouseButton1Click:Connect(function() Keybindings.setBinding(actionId, false) end) Keybindings.rows[actionId] = {frame = row, bindButton = bindButton, conflictLabel = conflictLabel} Keybindings.refreshAction(actionId) end Keybindings.refreshConflicts() end refreshQuickTurnUI() refreshCTFModeUI() FlagCapture.refreshProgress() ShipHUD.applyPosition() ShipHUD.applyTransparency() Keybindings.refreshConflicts() notifyUser("Pirate Wars Admin Utility", LOADED_GREETING, 8, "BottomRight") if (Keybindings.hardConflictCount or 0) > 0 then Toast.push("Keybind conflicts", Keybindings.hardConflictCount .. " shared binding(s) - see Keybinds tab", Toast.WARN, 6) end