local capturer
local flagColor = flag.BrickColor
local capturers = 0
defenders = 0
flag.Transparency = 0
for _, player in pairs(players:GetPlayers()) do
local character = player.Character
if not character then return end
local humanoidRootPart = character:FindFirstChild('HumanoidRootPart')
if not humanoidRootPart then return end
local humanoid = character:FindFirstChild('Humanoid')
local distance = (humanoidRootPart.CFrame.p - flag.CFrame.p).magnitude
if distance < range and humanoid.Health > 0 then
if firstColor == nil then
firstColor = player.TeamColor
end
if firstColor ~= nil then
if player.TeamColor == firstColor then
if player.TeamColor ~= flagColor then
capturers = capturers + 1
giveCapturing(player)
end
else
defenders = defenders + 1
giveCapturing(player)
end
end
capturer = player
else
giveCapturing(player)
end
end
if capturers > 0 then
if defenders == 0 then
flag.Transparency = 0
local ratio = 2.5
if not reset then
if percent < 100 then
percent = percent + (capturers/ratio)
else
percent = 100
reset = true
end
elseif reset then
if percent > 0 then
percent = percent - (capturers/ratio)
else
local color = flag.BrickColor
percent = 0
reset = false
flag.BrickColor = firstColor
firstColor = nil
for _, player in pairs(players:GetPlayers()) do
if player.TeamColor == flag.BrickColor then
local character = player.Character
if not character then return end
local humanoidRootPart = character:FindFirstChild('HumanoidRootPart')
if not humanoidRootPart then return end
local humanoid = character:FindFirstChild('Humanoid')
local distance = (humanoidRootPart.CFrame.p - flag.CFrame.p).magnitude
if player:FindFirstChild('Capturing') then
local capturing = player.Capturing
debris:AddItem(capturing, 1)
end
if distance < range and humanoid.Health > 0 then
print(1)
-- Players who have captured it
end
end
end
end
end
else
flag.Transparency = 0.5
end
else
firstColor = nil
if percent > 0 then
percent = percent - 1
reset = false
else
reset = false
end
end
flag.CFrame = defaultCFrame - Vector3.new(0, (percent/100)*(pole.Size.Y - flag.Size.Y), 0)
if configuration.BLUE_SCORE == configuration.KOTH_SCORE or configuration.RED_SCORE == configuration.KOTH_SCORE then
timeManager:End()
end
end
So itās basically a flag that players stand near to claim for their team. This is NOT my own code!! Itās taken from a really old copy of one of the original Paintball games. I have made small edits around the place to get it to work, but since this is an old script (around 2013-2014) I was wondering how I could go about making this a LOT cleaner and easier to read. I was thinking of firstly just chucking all the variables in a Module somewhere in RepStorage, so I can easily change the certain values around without having to find the script in multiple maps and change it all.
Trying to break it down to the bare bones would be like
If player gets within a certain range of the flag and they are still alive (no dead body parts flying around the map) then the flag starts to lower and become ācapturedā
If somebody else from your team comes within the capture range, then it speeds up the capture rate.
If a player from the opposing team enters while you are trying to capture, the flag will stop, and wonāt move until one the players has left the area.
If 2 red players and 1 blue player are in the area, the flag will go down for the red team.
Once flag has gone to the bottom of the pole and then back up to the top, it has become captured and changes color to show what team currently has the flag ācapturedā
Not sure if I am missing any points, but thatās kinda the general breakdown. Iām sure I could probably use functions a lot more too? Like checkPlayerDistance() checkIfAlive() checkDefenders(), or something like that etc. to kinda break down the code a lil more
Personally I donāt find anything about that code difficult to understand? Thereās a small amount of repetition what with the two Player for loops, but without knowing how this code is called (you cut off the first line of what I assume is a function) I canāt make any suggestions for that.
Your only issue with this seems to be wanting to modify it in multiple places at once, and moving the configuration to a shared ModuleScript solves that neatly.
If you want to refactor this, I suggest having all of the flags on the currently active map controlled from a single script rather than a separate script for each flag.
Ok, yea each flag will be controlled by a single script (this script however only controls 1, because thereās only 1 flag in the game mode)
Iāve done some editing since the OG post, and I have this now, which works and all, but having 2 while loops seems really bad and not entirely sure how to fix it. Itās incredibly long
function kingOfTheHill:Gamemode()
local map = mapHolder:WaitForChild('Map')
local kingFlag = map:WaitForChild('KingFlag')
local flag = kingFlag:WaitForChild('Flag')
local pole = kingFlag:WaitForChild('Pole')
local percent = 0
local range = 15
local defaultCFrame = flag.CFrame
local paused = false
local reset = false
local firstColor = nil
configuration.BLUE_SCORE = 0
configuration.RED_SCORE = 0
updateScore:FireAllClients(configuration.BLUE_SCORE, configuration.RED_SCORE)
local start = coroutine.create(function()
print('new KOTH')
while configuration.GAME_RUNNING do
local capturer = nil
local flagColor = flag.BrickColor
local capturers = 0
local defenders = 0
flag.Transparency = 0
for _, player in pairs(players:GetPlayers()) do
local character = player.Character
if not character then return end
local humanoidRootPart = character.PrimaryPart
if not humanoidRootPart then return end
local humanoid = character:FindFirstChildWhichIsA('Humanoid')
local distance = (humanoidRootPart.CFrame.Position - flag.CFrame.Position).Magnitude
if distance < range and humanoid.Health > 0 then
if firstColor == nil then
firstColor = player.TeamColor
end
if firstColor ~= nil then
if player.TeamColor == firstColor then
if player.TeamColor ~= flagColor then
capturers = capturers + 1
giveCapturing(player, flag)
end
else
defenders = defenders + 1
giveCapturing(player, flag)
end
end
capturer = player
else
giveCapturing(player, flag)
end
end
if capturers > 0 then
if defenders == 0 then
flag.Transparency = 0
local ratio = 2.5
if not reset then
if percent < 100 then
percent = percent + (capturers/ratio)
else
percent = 100
reset = true
end
elseif reset then
if percent > 0 then
percent = percent - (capturers/ratio)
else
local color = flag.BrickColor
percent = 0
reset = false
flag.BrickColor = firstColor
firstColor = nil
for _, player in pairs(players:GetPlayers()) do
if player.TeamColor == flag.BrickColor then
local character = player.Character
if not character then return end
local humanoidRootPart = character.PrimaryPart
if not humanoidRootPart then return end
local humanoid = character:FindFirstChildWhichIsA('Humanoid')
local distance = (humanoidRootPart.CFrame.Position - flag.CFrame.Position).Magnitude
if player:FindFirstChild('Capturing') then
local capturing = player.Capturing
debris:AddItem(capturing, 1)
end
if distance < range and humanoid.Health > 0 then
updateExp:Fire(player, amounts.Exp.CaptureKOTH, true)
updateGold:Fire(player, amounts.Gold.CaptureKOTH, true)
end
end
end
end
end
else
flag.Transparency = 0.5
end
else
firstColor = nil
if percent > 0 then
percent = percent - 1
reset = false
else
reset = false
end
end
flag.CFrame = defaultCFrame - Vector3.new(0, (percent/100)*(pole.Size.Y - flag.Size.Y), 0)
if configuration.BLUE_SCORE == configuration.KOTH_SCORE or configuration.RED_SCORE == configuration.KOTH_SCORE then
timeManager:End()
break
end
wait()
end
end)
local setScore = coroutine.create(function()
while configuration.GAME_RUNNING do
if flag.BrickColor ~= BrickColor.new('Ghost grey') then
print(flag.BrickColor)
if flag.BrickColor == teams.Blue.TeamColor then
configuration.BLUE_SCORE = configuration.BLUE_SCORE + 1
else
configuration.RED_SCORE = configuration.RED_SCORE + 1
end
updateScore:FireAllClients(configuration.BLUE_SCORE, configuration.RED_SCORE)
end
wait(2)
end
end)
coroutine.resume(start)
coroutine.resume(setScore)
timeManager:StartTimer(configuration.KOTH_TIME, true)
end
Mostly just looking at whatās inside the start and setScore courotuine. If the setScore could be changed in anyway? All itās really doing is giving whichever team owns the flag a point every 2 seconds
You can avoid using two loops here by keeping track of the time delta between each iteration and only adding score or updating defenders when enough time has elapsed.
A skeleton of that would be something like this:
local timeSinceScore = 0
local scoreUpdateInterval = 2
local timeSinceCaptureUpdate = 0
local captureUpdateInterval = 0.5
local last = tick()
while running do
local t, dt = tick()
dt, last = t - last, t -- can't local on this line or `last` is captured!
timeSinceCaptureUpdate = timeSinceCaptureUpdate + dt
if timeSinceCaptureUpdate >= captureUpdateInterval then
timeSinceCaptureUpdate = timeSinceCaptureUpdate - captureUpdateInterval
-- calculate capturerers and defenders, move flag, etc.
end
timeSinceScore = timeSinceScore + dt
if timeSinceScore >= scoreUpdateInterval then
timeSinceScore = timeSinceScore - scoreUpdateInterval
-- give score to team that owns flag
end
wait() -- Or RenderStepped, Heartbeat, etc.
end
If you wanted to update the flagās position smoothly/continously, you can use this same dt variable along with some rates to modify the ācapture percentageā rather than stepping it a fixed amount each update.
E.g. Assuming ratio to be a ācapture efficiencyā modifier, something like this could work:
local captureRate = 5 -- percent per second
percent = math.min(100, math.max(0, percent + captureRate*dt*ratio))
-- max and min used here for clamping
EDIT: P.S. if you only have two teams in this KOTH you might consider having a single ācapturersā value where negative count would mean more defenders than capturers and positive the opposite. Zero would mean nobody is around or the presence of both teams is equal.