This a module script:
-- CaptureFlagService [v2]
-- Scripted by SkySpell
---------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Variables
local Rep = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local System = {
--------------------------------------------------
SystemEnabled = true,
TeamScoreActive = true,
FlagType = "Accessory",
ScoreName = "Score",
PadBehaviorType = "Touch", -- Types: Touch (requires touch) and Radial (requires to be in a certain range)
PadRadial = 10,
--------------------------------------------------
ShowPlayerLocation = true,
LocationImage = 4506490665,
ChangeLocationImageColor = true,
LocationImageSize = UDim2.new(1, 0, 1, 0),
--------------------------------------------------
SoundEnabled = false,
ScoreSound = nil,
--------------------------------------------------
Droppable = true,
DropType = "Stay", -- Types: Stay (waits until the ReturnTime reaches 0), TeamGrab (can be grabbed by teammates), TouchReturn (returns when touched)
DropParent = workspace,
DropOffset = Vector3.new(0, 1, 0),
ReturnTime = 20,
--------------------------------------------------
SpectateColors = { ["medium stone grey"] = true },
ScoreBin = nil
--------------------------------------------------
}
local Flags = {}
if System.TeamScoreActive then
System.ScoreBin = Rep:FindFirstChild("Scores") or Instance.new("Folder", Rep)
System.ScoreBin.Name = "Scores"
end
---------------------------------------------------------------------------------------------------------------------------------------------------------------
-- External Functions / Methods
---------------------------------------------------------------------------------------------------------------------------------------------------------------
local function SearchForFlag(model)
local ValidNames = { flaghandle = true, flag = true, handle = true, }
if model then
for _, obj in pairs(model:GetDescendants()) do
if ValidNames[obj.Name:lower()] and obj:IsA("BasePart") then
return obj
end
end
end
return nil
end
local function SearchForPad(model)
local ValidNames = { flagpad = true, pad = true, mainpad = true, }
if model then
for _, obj in pairs(model:GetDescendants()) do
if ValidNames[obj.Name:lower()] and obj:IsA("BasePart") then
return obj
end
end
end
return nil
end
local function FabricateIgnoreList()
local Ignore = {}
for _, obj in pairs(System.DropParent:GetChildren()) do
if obj:IsA("BasePart") and obj.Name:lower() == "droppedflag" then
table.insert(Ignore, 1, obj)
end
end
for _, pl in pairs(Players:GetPlayers()) do
local pChar = pl.Character
if pChar then
table.insert(Ignore, 1, pChar)
end
end
return Ignore
end
local function DetectFloor(rootpos)
if rootpos then
local NewRay = Ray.new(rootpos, Vector3.new(0, -100, 0))
local Part, Position = workspace:FindPartOnRayWithIgnoreList(NewRay, FabricateIgnoreList())
return (Part ~= nil)
end
return false
end
local function OnFlagHit(flagInfo, hit)
if flagInfo and hit and (not flagInfo.Captured or flagInfo.Dropped) and hit.Parent ~= flagInfo.FlagModel and System.SystemEnabled then
local HitHum = hit.Parent:FindFirstChildOfClass("Humanoid") or hit.Parent.Parent:FindFirstChildOfClass("Humanoid")
local HitPlayer = HitHum and Players:GetPlayerFromCharacter(HitHum.Parent)
local HitFlag = HitHum and HitHum.Parent:FindFirstChild("FlagHat")
local HumanoidRoot = HitHum and HitHum.Parent:FindFirstChild("HumanoidRootPart")
if HitHum and HitHum.Health > 0 and HitPlayer and not HitPlayer.Neutral and not System.SpectateColors[HitPlayer.TeamColor.Name:lower()] and HitFlag == nil then
return HitHum.Parent, HitHum, HumanoidRoot -- this means that the capture is valid.
end
end
return nil, nil, nil
end
local function OnPadHit(flagInfo, hit)
if flagInfo and hit and hit.Parent ~= flagInfo.FlagModel and System.SystemEnabled then
local HitHum = hit.Parent:FindFirstChild("Humanoid") or hit.Parent.Parent:FindFirstChild("Humanoid")
local HitPlayer = HitHum and Players:GetPlayerFromCharacter(HitHum.Parent)
if HitHum and HitHum.Health > 0 and HitPlayer and HitPlayer.TeamColor == flagInfo.Team.TeamColor and not System.SpectateColors[HitPlayer.TeamColor.Name:lower()] then
return HitHum.Parent, HitPlayer -- this means that the capture is valid.
end
end
return nil, nil
end
local function GiveUserFlagObject(handle, team, parent)
if handle and handle:IsA("BasePart") and parent and team then
local FinalParent = nil
if System.FlagType:lower() == "accessory" then
FinalParent = Instance.new("Accessory")
FinalParent.Name = "FlagHat"
elseif System.FlagType:lower() == "tool" then
FinalParent = Instance.new("Tool")
FinalParent.Name = team.Name:gsub("%s+", "").."Flag"
FinalParent.CanBeDropped = false
FinalParent.GripPos = Vector3.new(1, 0, 0)
FinalParent.GripRight = Vector3.new(0, 0, -1)
FinalParent.GripUp = Vector3.new(0, 1, 0)
FinalParent.GripForward = Vector3.new(-1, 0, -0)
end
if FinalParent then
local NewHandle = handle:Clone()
NewHandle.Name = "Handle"
NewHandle.Transparency = 0
if System.FlagType:lower() == "accessory" and not NewHandle:FindFirstChildOfClass("Attachment") then
local Attachment = Instance.new("Attachment", NewHandle)
Attachment.Name = "BodyBackAttachment"
Attachment.Orientation = Vector3.new(0, 0, -15)
Attachment.Position = Vector3.new(0.5, 0, -0.22)
end
NewHandle.Parent = FinalParent
NewHandle.Anchored = false
local TeamOwner = Instance.new("ObjectValue", FinalParent)
TeamOwner.Name = "TeamOwner"
TeamOwner.Value = team
FinalParent.Parent = workspace
return FinalParent
end
end
return nil
end
local function ClearFlagObjects(character, backpack)
if character then
for _, obj in pairs(character:GetChildren()) do
if obj.Name:lower() == "flaghat" then
obj:Destroy()
end
end
end
if backpack then
for _, obj in pairs(backpack:GetChildren()) do
if obj:FindFirstChild("TeamOwner") then
obj:Destroy()
end
end
end
end
local function FindFlagObject(character, backpack)
if character then
for _, obj in pairs(character:GetChildren()) do
if obj.Name:lower() == "flaghat" then
return obj
end
end
end
if backpack then
for _, obj in pairs(backpack:GetChildren()) do
if obj:FindFirstChild("TeamOwner") then
return obj
end
end
end
end
local function FindTeamInfo(teamObject)
if teamObject and teamObject:IsA("ObjectValue") and teamObject.Value then
local Logic, _ = System:FindTeam(teamObject.Value)
return Logic
end
end
---------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Module Functions / Methods
---------------------------------------------------------------------------------------------------------------------------------------------------------------
function System.new(teamObject, flagModel)
-------------------------------------------------------------------------------------------------------------
if teamObject == nil or not teamObject:IsA("Team") then
warn("TeamObject is not a valid parameter.")
return nil
else
local Logic, _ = System:FindTeam(teamObject)
if Logic then return Logic end
end
-------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------
-- Internal Variables
-------------------------------------------------------------------------------------------------------------
-- Table Variables
local Logic = {}
local Connections = {} -- value: { ConnectionName = "Test", RawConnection = whatever_lol }
local Internal = {}
local billboard = nil
-- Logic Properties
---------------------------------------------------------------------------------
Logic.Team = teamObject
---------------------------------------------------------------------------------
Logic.FlagModel = nil
Logic.FlagOwner = nil
Logic.FlagHandle = nil
Logic.FlagPad = nil
---------------------------------------------------------------------------------
Logic.Enabled = false
Logic.Captured = false
Logic.Dropped = false
---------------------------------------------------------------------------------
Logic.FlagType = System.FlagType
Logic.PadBehaviorType = System.PadBehaviorType
Logic.PadRadial = System.PadRadial
---------------------------------------------------------------------------------
Logic.ReturnTime = System.ReturnTime
Logic.Droppable = System.Droppable
Logic.DropType = System.DropType
Logic.ShowPlayerLocation = System.ShowPlayerLocation
Logic.ScoreSound = System.ScoreSound
Logic.DropOffset = System.DropOffset
---------------------------------------------------------------------------------
Logic.LocationImage = System.LocationImage
Logic.ChangeLocationImageColor = System.ChangeLocationImageColor
Logic.LocationImageSize = System.LocationImageSize
---------------------------------------------------------------------------------
Logic.Score = nil
if System.TeamScoreActive then
local scoreName = teamObject.Name:gsub("%s+", "").."Score"
local existingScore = System.ScoreBin:FindFirstChild(scoreName)
if not existingScore then
Logic.Score = Instance.new("StringValue", System.ScoreBin)
Logic.Score.Name = scoreName
Logic.Score.Value = "0"
else
Logic.Score = existingScore
end
end
---------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------
-- Internal Functions / Methods
-------------------------------------------------------------------------------------------------------------
function Internal:SearchForConnection(name)
if name then
for i = 1, #Connections do
local ConData = Connections[i]
if ConData and ConData.ConnectionName and ConData.ConnectionName:lower() == tostring(name):lower() then
return i, ConData
end
end
end
return nil, nil
end
function Internal:Disconnect(name)
if name then
local Number, Data = Internal:SearchForConnection(name)
if Number and Data then
if Data.RawConnection then
Data.RawConnection:disconnect()
Data.RawConnection = nil
end
table.remove(Connections, Number)
end
end
end
function Internal:EstablishConnection(name, connection)
if name and connection then
table.insert(Connections, 1, { ConnectionName = tostring(name), RawConnection = connection })
end
end
local function ApplyLocation(object, owner, tcolor)
if object and owner and tcolor and Logic.ShowPlayerLocation then
local Billboard = Instance.new("BillboardGui", object)
Billboard.Name = "LocationBillboard"
Billboard.LightInfluence = 0
Billboard.AlwaysOnTop = true
Billboard.Size = Logic.LocationImageSize
Billboard.Adornee = object
Billboard.PlayerToHideFrom = owner
Billboard.Active = true
local Image = Instance.new("ImageLabel", Billboard)
Image.Name = "LocationImage"
Image.Size = UDim2.new(1, 0, 1, 0)
Image.BackgroundTransparency = 1
Image.Image = "rbxassetid://"..Logic.LocationImage
Image.ImageColor3 = tcolor
return Billboard
end
return nil
end
local function RemoveBillboard(location)
if location then
for _, obj in pairs(location:GetDescendants()) do
if obj:IsA("BillboardGui") and obj.Name:lower() == "locationbillboard" then
obj:Destroy()
end
end
end
end
local function ToggleFlagVisibility(toggle)
if Logic.FlagHandle and typeof(toggle) == "boolean" then
Logic.FlagHandle.Transparency = (toggle == true and 0) or 1
Logic.FlagHandle.Flag.Transparency = (toggle == true and 0) or 1
Logic.FlagHandle.Flag.Front.Transparency = (toggle == true and 0.7) or 1
Logic.FlagHandle.Flag.Back.Transparency = (toggle == true and 0.7) or 1
Logic.FlagHandle.Flag.BillboardGui.ImageLabel.ImageTransparency = (toggle == true and 0) or 1
end
end
local function ClearFlagOwner()
if Logic.FlagOwner then
if Logic.FlagOwner then
ClearFlagObjects(Logic.FlagOwner.Character, Logic.FlagOwner:FindFirstChild("Backpack"))
RemoveBillboard(Logic.FlagOwner.Character)
Logic.FlagOwner = nil
end
Logic.Dropped = false
Logic.Captured = false
end
end
local function SetPlayerToFlagOwner(player, character)
if player and character then
local FinalParent = (Logic.FlagType:lower() == "accessory" and character) or (Logic.FlagType:lower() == "tool" and player:FindFirstChild("Backpack"))
if FinalParent then
Logic.Dropped = false
Logic.Captured = true
Logic.FlagOwner = player
local FlagObject = GiveUserFlagObject(Logic.FlagHandle, Logic.Team, FinalParent)
if FlagObject then
FlagObject.Parent = FinalParent
end
ToggleFlagVisibility(false)
else
warn("FinalParent doesn't exist.")
end
end
end
local function OnHumanoidDied()
if Logic.FlagOwner and Logic.FlagOwner.Character and Logic.Captured and Logic.Droppable and Logic.Enabled then
local HumanoidRoot = Logic.FlagOwner.Character:FindFirstChild("HumanoidRootPart")
local FloorExists = (HumanoidRoot and DetectFloor(HumanoidRoot.Position + Logic.DropOffset)) or false
ClearFlagOwner()
if FloorExists then
Internal:CreateDrop(HumanoidRoot.Position + Logic.DropOffset)
else
ToggleFlagVisibility(true)
end
else
ClearFlagOwner()
ToggleFlagVisibility(true)
Internal:Disconnect("TeamChangeConnection")
end
Internal:Disconnect("DiedConnection")
end
local function OnTeamChanged()
ClearFlagOwner()
ToggleFlagVisibility(true)
Internal:Disconnect("DiedConnection")
Internal:Disconnect("TeamChangeConnection")
end
local function OnFlagHandleTouched(hit, handle)
if hit and hit.Parent and System.SystemEnabled and Logic.Enabled then
local HitChar, HitHum, Root = OnFlagHit(Logic, hit)
local HitPlayer = HitChar and Players:GetPlayerFromCharacter(HitChar)
if HitPlayer and HitChar and HitHum and Root then
if HitPlayer.TeamColor ~= Logic.Team.TeamColor or (HitPlayer.TeamColor == Logic.Team.TeamColor and (Logic.DropType:lower() == "teamgrab" or Logic.DropType:lower() == "touchreturn")) then
if HitPlayer.TeamColor == Logic.Team.TeamColor and Logic.DropType:lower() == "touchreturn" then
Logic:Reset()
else
Internal:Disconnect("DiedConnection")
Internal:Disconnect("TeamChangeConnection")
local NewConnection1, NewConnection2 = nil
SetPlayerToFlagOwner(HitPlayer, HitChar)
NewConnection1 = HitHum.Died:connect(OnHumanoidDied)
NewConnection2 = HitPlayer.Changed:connect(OnTeamChanged)
Internal:EstablishConnection("DiedConnection", NewConnection1)
Internal:EstablishConnection("TeamChangeConnection", NewConnection2)
billboard = ApplyLocation(Root, HitPlayer, Logic.Team.TeamColor.Color)
Logic:OnGrabbed()
end
if handle then handle:Destroy() handle = nil end
end
end
end
end
local function OnPadHandleTouched(hit)
if hit and hit.Parent and Logic.Enabled and System.SystemEnabled then
local HitChar, HitPlayer = OnPadHit(Logic, hit)
if HitPlayer and HitChar and HitPlayer.TeamColor == Logic.Team.TeamColor then
local FlagObject = FindFlagObject(HitChar, HitPlayer:FindFirstChild("Backpack"))
local OtherTeamInfo = FlagObject and FindTeamInfo(FlagObject:FindFirstChild("TeamOwner"))
if OtherTeamInfo then
FlagObject:Destroy()
OtherTeamInfo:Reset()
if OtherTeamInfo.Team.TeamColor ~= HitPlayer.TeamColor then
System:IncreaseScore(HitPlayer, Logic.Team)
if System.SoundEnabled and Logic.ScoreSound then
Logic.ScoreSound:Play()
end
end
end
end
end
end
local function OnPrimaryPartHit(hit)
if hit then
local HitPlayer = hit.Parent and Players:GetPlayerFromCharacter(hit.Parent)
if HitPlayer and HitPlayer.TeamColor ~= Logic.Team.TeamColor then
OnFlagHandleTouched(hit)
end
end
end
function Internal:CreateDrop(position)
if position and Logic.FlagHandle and Logic.Enabled and Logic.Droppable then
Internal:Disconnect("DiedConnection")
Internal:Disconnect("TeamChangeConnection")
if billboard then
billboard:Destroy()
billboard = nil
end
local NewConnection = nil
local NewFlag = Logic.FlagHandle:Clone()
NewFlag.Name = "DroppedFlag"
NewFlag.Transparency = 0
NewFlag.Anchored = true
NewFlag.CFrame = CFrame.new(position)
NewFlag.Parent = System.DropParent
NewConnection = NewFlag.Touched:connect(function(hit) OnFlagHandleTouched(hit, NewFlag) end)
Internal:EstablishConnection("FlagTouched", NewConnection)
Logic:OnDropped(NewFlag)
else
Logic:Reset()
end
end
local function OnPlayerRemoved(player)
if player and player == Logic.FlagOwner then
Logic:Reset()
end
end
local function OnRadiusLoop()
while Logic.Enabled and Logic.Team and Logic.FlagPad do
for _, player in pairs(Players:GetPlayers()) do
if player.TeamColor == Logic.Team.TeamColor then
local Root = player.Character and player.Character:FindFirstChild("HumanoidRootPart")
if Root and (Root.Position - Logic.FlagPad.Position).magnitude <= Logic.PadRadial then
OnPadHandleTouched(Root)
end
end
end
wait()
end
end
-------------------------------------------------------------------------------------------------------------
-- Logic Functions / Methods
-------------------------------------------------------------------------------------------------------------
function Logic:AddModel(newModel)
if newModel then
local NewHandle = (newModel:IsA("Model") and newModel.PrimaryPart) or SearchForFlag(newModel)
local NewPad = SearchForPad(newModel)
if NewHandle and NewPad then
NewHandle.CanCollide = false
NewHandle.Flag.CanCollide = false
NewPad.CanCollide = true
Logic:RemoveModel()
Logic.FlagModel = newModel
Logic.FlagHandle = NewHandle
Logic.FlagPad = NewPad
local FlagConA = NewHandle.Touched:connect(OnPrimaryPartHit)
Internal:EstablishConnection("FlagTouchedConnection", FlagConA)
Logic.Enabled = true
if Logic.PadBehaviorType:lower() == "touch" then
local FlagConB = NewPad.Touched:connect(OnPadHandleTouched)
Internal:EstablishConnection("FlagPadConnection", FlagConB)
elseif Logic.PadBehaviorType:lower() == "radius" then
spawn(OnRadiusLoop)
end
else
warn(newModel.Name.." does not have the necessary compontents.")
end
end
end
function Logic:RemoveModel()
if Logic.FlagModel then
Logic:Reset()
Logic.FlagModel = nil
Internal:Disconnect("FlagTouchedConnection")
Internal:Disconnect("FlagPadConnection")
Logic.Enabled = false
end
end
function Logic:Reset()
ClearFlagOwner()
if billboard then
billboard:Destroy()
billboard = nil
end
ToggleFlagVisibility(true)
Internal:Disconnect("DiedConnection")
Internal:Disconnect("TeamChangeConnection")
end
function Logic:OnGrabbed()
print("Grabbed!")
end
function Logic:OnDropped(handle)
if handle then
print("Dropped!")
Logic.Dropped = true
spawn(function()
local t = tick() + Logic.ReturnTime
repeat wait() until t < tick() or handle == nil or Logic.Dropped == false
if handle and Logic.Dropped then
handle:Destroy()
Logic:Reset()
end
end)
end
end
-------------------------------------------------------------------------------------------------------------
-- Finishing Touches
-------------------------------------------------------------------------------------------------------------
if flagModel then Logic:AddModel(flagModel) end
Players.PlayerRemoving:connect(OnPlayerRemoved)
table.insert(Flags, 1, Logic)
return Logic
end
function System:FindTeam(teamObject)
if teamObject and teamObject:IsA("Team") then
for i = 1, #Flags do
local Data = Flags[i]
if Data and Data.Team and Data.Team == teamObject then
return Data, i
end
end
end
return nil, nil
end
function System:RemoveTeam(teamObject)
if teamObject and teamObject:IsA("Team") then
local Data, Number = System:FindTeam(teamObject)
if Data and Number then
Data:RemoveModel()
Data = nil
table.remove(Flags, Number)
end
end
end
function System:IncreaseScore(player, team)
print (player, team)
if player and team then
local Data, _ = System:FindTeam(team)
print(Data, _)
if System.TeamScoreActive and Data and Data.Score then
Data.Score.Value = Data.Score.Value + 1
end
end
end
function System:SyncData()
local IgnoreProp = { systemenabled = true, teamscoreactive = true, spectatecolors = true, scorebin = true }
for i = 1, #Flags do
local Data = Flags[i]
if Data then
for prop, var in pairs(System) do
if not IgnoreProp[tostring(prop):lower()] then
Data[prop] = var
end
end
end
end
end
return System```