Capture Progress Bar not working

Hi, I’m making a military game where the purpose of it is that there 2 nations (teams) and each of them have to attack the enemy’s cities and capture them so that the enemy can’t spawn in that specific city and defend it. The capturing system works, however, the progress bar does not. I want people to be able to see the progress bar so that they’ll know how long it’ll take for them to capture it and the enemy to see that their city is being captured. Here’s the code:

-- Server (this is the script for each city)
local module = require(game.ServerScriptService.Modules.TownCaptureModule)
local townhall = script.Parent

--// Town Essentials \\--
local townname = townhall.TownName
local towncapturepart = townhall.TownCapturePart

local debounce = false
local yellowstonecolor = Color3.fromRGB(255, 225, 0)
local lakewoodcolor = Color3.fromRGB(4, 175, 236)

towncapturepart.Touched:Connect(function(hit)
	if hit.Parent:FindFirstChild("Humanoid") and hit.Parent:FindFirstChild("SoldierStats") then
        -- so this script for the towns of Yellowstone, 
        --if the player is on the Yellowstone team/nation and the progress bar is at max, then nothing happens. 
        --If the player is on the Lakewood team/nation (the enemy team), then the progress bar decreases for Yellowstone and as soon as it hits 0, it starts increasing for Lakewood and as soon as it hits 300 (max bar size), then the enemy gains full control of the the town.
		if hit.Parent.SoldierStats.NationName.Value == "Yellowstone" then
			if capturegui.ProgressBar.Filler.Size == UDim2.new(0, 300, 0, 50) then
				
			elseif capturegui.ProgressBar.Filler.Size < UDim2.new(0, 300, 0, 50) then
				if capturegui.ProgressBar.Filler.BackgroundColor3 == yellowstonecolor then
					game.ReplicatedStorage.Remotes.Events.IncreaseCapture:FireAllClients(yellowstonecolor)
					
					towncapturepart.TouchEnded:Connect(function()
						game.ReplicatedStorage.Remotes.Events.PauseCapture:FireAllClients()
					end)
					
				elseif capturegui.ProgressBar.Filler.BackgroundColor3 == lakewoodcolor then
					game.ReplicatedStorage.Remotes.Events.DecreaseCapture:FireAllClients(lakewoodcolor)
					
					towncapturepart.TouchEnded:Connect(function()
						game.ReplicatedStorage.Remotes.Events.PauseCapture:FireAllClients()
					end)
					
					if capturegui.ProgressBar.Filler.Size == UDim2.new(0, 0, 0, 50) then
						game.ReplicatedStorage.Remotes.Events.IncreaseCapture:FireAllClients(yellowstonecolor)
						
						towncapturepart.TouchEnded:Connect(function()
							game.ReplicatedStorage.Remotes.Events.PauseCapture:FireAllClients()
						end)
						
						if capturegui.ProgressBar.Filler.Size == UDim2.new(0, 300, 0, 50) then
							module.CaptureTown(townhall, townname.Value, hit.Parent.SoldierStats.FlagID.Value, hit.Parent.SoldierStats.NationName.Value)
						end
					end
				end
			end
		
		else
			if capturegui.ProgressBar.Filler.Size == UDim2.new(0, 300, 0, 50) then

			elseif capturegui.ProgressBar.Filler.Size < UDim2.new(0, 300, 0, 50) then
				if capturegui.ProgressBar.Filler.BackgroundColor3 == lakewoodcolor then
					game.ReplicatedStorage.Remotes.Events.IncreaseCapture:FireAllClients(lakewoodcolor)
					
					towncapturepart.TouchEnded:Connect(function()
						game.ReplicatedStorage.Remotes.Events.PauseCapture:FireAllClients()
					end)

				elseif capturegui.ProgressBar.Filler.BackgroundColor3 == yellowstonecolor then
					game.ReplicatedStorage.Remotes.Events.DecreaseCapture:FireAllClients(yellowstonecolor)
					
					towncapturepart.TouchEnded:Connect(function()
						game.ReplicatedStorage.Remotes.Events.PauseCapture:FireAllClients()
					end)

					if capturegui.ProgressBar.Filler.Size == UDim2.new(0, 0, 0, 50) then
						game.ReplicatedStorage.Remotes.Events.IncreaseCapture:FireAllClients(lakewoodcolor)
						
						towncapturepart.TouchEnded:Connect(function()
							game.ReplicatedStorage.Remotes.Events.PauseCapture:FireAllClients()
						end)

						if capturegui.ProgressBar.Filler.Size == UDim2.new(0, 300, 0, 50) then
							module.CaptureTown(townhall, townname.Value, hit.Parent.SoldierStats.FlagID.Value, hit.Parent.SoldierStats.NationName.Value)
						end
					end
				end
			end
		end
	end
end)
-- ModuleScript (this is the module for capturing towns)
local module = {}

function module.CaptureTown(townhall, townname, flagid, currentcountry)
	townhall.CurrentCountry.Value = currentcountry

	for i, player in pairs(game.Players:GetPlayers()) do
		player.PlayerGui.Main.TownCapturedText.Visible = true
		player.PlayerGui.Main.TownCapturedText.Text = townname.." has been captured by "..currentcountry

		wait(3)
		player.PlayerGui.Main.TownCapturedText.Visible = false
		player.PlayerGui.Main.TownCapturedText.Text = ""
	end

	for i, flagpart in pairs(townhall.Flag.Flag:GetChildren()) do
		flagpart.TextureID = flagid
	end
end

return module
-- Client (this is the LocalScript for the progress bar)
local progressbar = script.Parent
local filler = progressbar.Filler

game.ReplicatedStorage.Remotes.Events.IncreaseCapture.OnClientEvent:Connect(function(countrycolor)
	filler.BackgroundColor3 = countrycolor
	filler:TweenSize(UDim2.new(0, 300, 0, 50), Enum.EasingDirection.InOut, Enum.EasingStyle.Sine, 30)
end)

game.ReplicatedStorage.Remotes.Events.DecreaseCapture.OnClientEvent:Connect(function(countrycolor)
	filler.BackgroundColor3 = countrycolor
	filler:TweenSize(UDim2.new(0, 0, 0, 50), Enum.EasingDirection.InOut, Enum.EasingStyle.Sine, 30)
end)

game.ReplicatedStorage.Remotes.Events.PauseCapture.OnClientEvent:Connect(function()
	filler:TweenSize(filler.Size, Enum.EasingDirection.InOut, Enum.EasingStyle.Sine, 0, true)
end)

Alright, I thought I had something that could help, but upon reviewing the code here on my pc rather than my phone I realized you were going about capturing the cities a different way than I had expected.

So instead of starting with what I thought, I’ll ask: By “Not working,” what do you mean? Is it just not displaying at all or doing something unexpected?

I noticed one issue which kind of helped me realize where the issue is. However, I scratched the idea and decided doing this in a different way since the code there was hard to read. So instead, I decided to let everyone know that a certain town is being captured by the enemy and while the enemy player that’s capturing it, has a timer shown on the top of his/her screen which the timer is set to 30 seconds and once the time runs out, the town will be fully captured. The problem though is that every time the player touches the invisible capture part for the town (or moves around it), it removes the timer and cancels the capture when it’s supposed to only do that if the player dies or leaves the town hall.

-- Server Script for each town
local module = require(game.ServerScriptService.Modules.TownCaptureModule)
local townhall = script.Parent

--// Town Essentials \\--
local townname = townhall.TownName
local towncolor = townhall.TownColor
local towncapturepart = townhall.TownCapturePart
local currentcountry = townhall.CurrentCountry

local debounce = false
local timer = 30
local yellowstonecolor = Color3.fromRGB(255, 225, 0)
local lakewoodcolor = Color3.fromRGB(4, 175, 236)

towncapturepart.Touched:Connect(function(hit)
	local hum = hit.Parent:FindFirstChild("Humanoid")
	local humrootpart = hit.Parent:FindFirstChild("HumanoidRootPart")
	local soldierstats = hit.Parent:FindFirstChild("SoldierStats")
	local player = game.Players:GetPlayerFromCharacter(hit.Parent)
	
	if hum and soldierstats then
		if soldierstats.NationName.Value == "Yellowstone" and currentcountry.Value == "Yellowstone" or soldierstats.NationName.Value == "Lakewood" and currentcountry.Value == "Lakewood" then
			
		elseif soldierstats.NationName.Value == "Yellowstone" and currentcountry.Value ~= "Yellowstone" or soldierstats.NationName.Value == "Lakewood" and currentcountry.Value ~= "Lakewood" then
			if debounce == false then
				debounce = true
				player.PlayerGui.Main.CaptureTimeText.Visible = true
				module.ShowCaptureTownMessage(townname.Value, soldierstats.NationName.Value)
				
				while timer > 0 do
					player.PlayerGui.Main.CaptureTimeText.Text = timer.."s"
					wait(1)
					timer -= 1
					
					if hum.FloorMaterial ~= Enum.Material.Wood or hum.FloorMaterial ~= "" or hum.Died:Wait() then
						player.PlayerGui.Main.CaptureTimeText.Visible = false
						player.PlayerGui.Main.CaptureTimeText.Text = "30s"
						timer = 30
						debounce = false
						break
					end
					
					if timer <= 0 then
						player.PlayerGui.Main.CaptureTimeText.Visible = false
						player.PlayerGui.Main.CaptureTimeText.Text = "30s"
						timer = 30
						module.CaptureTown(townhall, townname.Value, hit.Parent.SoldierStats.FlagID.Value, hit.Parent.SoldierStats.NationName.Value)
						debounce = false
						break
					end
				end
			end
		end
	end
end)
-- the module script, I modified it a bit however it has no issues
local module = {}

function module.ShowCaptureTownMessage(townname, nationname)
	for i, player in pairs(game.Players:GetPlayers()) do
		player.PlayerGui.Main.TownIsBeingCapturedText.Visible = true
		player.PlayerGui.Main.TownIsBeingCapturedText.Text = townname.." is being captured by "..nationname

		wait(3)
		player.PlayerGui.Main.TownIsBeingCapturedText.Visible = false
		player.PlayerGui.Main.TownIsBeingCapturedText.Text = ""
	end
end

function module.CaptureTown(townhall, townname, flagid, currentcountry)
	townhall.CurrentCountry.Value = currentcountry

	for i, player in pairs(game.Players:GetPlayers()) do
		player.PlayerGui.Main.TownCapturedText.Visible = true
		player.PlayerGui.Main.TownCapturedText.Text = townname.." has been captured by "..currentcountry
		
		for i, flagpart in pairs(townhall.Flag.Flag:GetChildren()) do
			flagpart.TextureID = flagid
		end

		wait(3)
		player.PlayerGui.Main.TownCapturedText.Visible = false
		player.PlayerGui.Main.TownCapturedText.Text = ""
	end
	
	--[[
	for i, flagpart in pairs(townhall.Flag.Flag:GetChildren()) do
		flagpart.TextureID = flagid
	end
	]]--
end

return module

First thing I notice: You’re changing the size of what is essentially a progressbar by pixel offsets rather than scale. On UDim2s, the first value of each UDim is a scale value which is a number between 0-1. This number, in the context of a GUI, relates to the GUI’s parent as a fraction. For example, a UDim saying {0.5, 0} means “half of something from the parent”.
For size, you can have a parent gui frame that is the maximum size of your progress bar, then do gui.Size = UDim2.fromScale(0.5, 0.8) and it will set the size of the gui to be 50% the width of its parent, and 80% the height of the parent. It makes progressbars like this much easier.

On to the help;

Are the town parts circular, or square? If you make them circular you could replace them with just a quick check for distance between each player and the part in a Runservice.HeartBeat function. If a player is within x range, increment the value by tickTime, ie:

-- server

local RUN_SERVICE = game:GetService("RunService")
local REPLICATED_STORAGE = game:GetService("ReplicatedStorage")
local PLAYERS  = game:GetService("Players")

local captureEvent = REPLICATED_STORAGE:WaitForChild("Some Remote Event")
local captureBlockPosition = script.Parent.Position
local maxCaptureDistance = 25
local maxCapturePoints = 30

local function isEnemy(player)
  -- you'd need to implement the logic for this
end
local function isTeammate(player)
  -- you'd need to implement the logic for this
end

local capturePoints = 0
local increasing = 0 -- I'll explain this variable below the code block.

-- If you are unaware: the input "ticktime" is time since this function last ran,
-- so you can use it to increment counters like this easily per second.

-- Avoid having ***too much*** logic here, as it runs every tick ingame. Having too much can potentially slow down your game.
RUN_SERVICE.HeartBeat:Connect(function(tickTime)
  local enemyOccupied = false
  local teamOccupied = false -- Assuming you want to pause capture if a teammate and enemy is on the capture area?

  for _, player in ipairs(PLAYERS:GetPlayers()) do -- for each player
    local distanceFromPart = (captureBlockPosition - player.Character.Position).Magnitude -- compute the distance to the capture block
    if distanceFromPart < maxCaptureDistance then -- if the distance is smaller than some specific distance
      if isEnemy(player) then -- and they're an enemy
        enemyOccupied = true -- then an enemy is occupying this capture zone
      elseif isTeammate(player) then -- if they're instead a friendly
        teamOccupied = true -- then a teammate is occupying this capture zone
      end
    end
  end

  if enemyOccupied and not teamOccupied then -- enemy occupied (and no friendlies), increase capture!
    capturePoints = capturePoints + tickTime
    increasing = 1
    if capturePoints > maxCapturePoints then -- if we've maxed the capture points, don't go over!
      capturePoints = maxCapturePoints
      increasing = 0
      -- probably set a variable here that lets something else know that the place has been captured.
    end 
  elseif enemyOccupied and teamOccupied then -- stalemate, both teams on the spot!
    increasing = 0 -- don't increase, but don't decrease either
  elseif not enemyOccupied then -- no enemies on the base, decrease!
    capturePoints = capturePoints - tickTime -- decrease capture points
    increasing = -1
    if capturePoints < 0 then -- if we've gone below zero, reset to zero!
      capturePoints = 0
      increasing = 0
    end 
  end
end)

-- For the client: Tell the client what the current capture points are, and what they're increasing/decreasing by per second.
-- This is also why `increasing` is sent -- the client can smooth the value themselves, rather than spamming hundreds of events per second on what the new capturePoints are.
while true do
  wait(1)
  captureEvent:FireAllClients(capturePoints, increasing)
end

You may have noticed I include the increasing value. That’s how much, per second, the capture is increasing/decreasing by. You can use this in another loop somewhere to broadcast to the players information about current capturePoints, and if they’re increasing/decreasing/not moving. The client would then have another RUN_SERVICE.HeartBeat:Connected function which does similarly to the one above, but instead it increments its internal value by the tickTime multiplied by increasing to get the value smoothed.

-- client

local RUN_SERVICE = game:GetService("RunService")
local REPLICATED_STORAGE = game:GetService("ReplicatedStorage")

local gui = script.Parent
local maxPoints = 30
local currentPoints = 0
local currentRate = 0

local captureEvent = REPLICATED_STORAGE:WaitForChild("Some Remote Event")

-- update the values, and then the GUI
RUN_SERVICE.HeartBeat:Connect(function(tickTime)
  currentPoints = currentPoints + tickTime * currentRate -- increment/decrement
  if currentPoints > maxPoints then currentPoints = maxPoints end -- block going over max
  if currentPoints < 0 then currentPoints = 0 end -- block going below 0

  -- Assuming the progress bar is horizontal.
  -- Set the length of the progress bar to be the percentage the current points are at
  -- Set the height to be the same size as the progress bar's parent.
  gui.Size = UDim2.fromScale(currentPoints / maxPoints, 1)
end)

-- update what the client knows
captureEvent.OnClientEvent:Connect(function(points, rate)
  currentPoints = points
  currentRate = rate
end)

This was kind of a data dump, so if you need any help about any of the stuff I went over here don’t be afraid to ask. I’ve enabled DMs from people in the Aspire server, so you should be able to DM me. I’ll likely respond much faster in DMs than here or on the aspire server.

Unfortunately, the capture parts aren’t circular. Basically, each town has a town hall and each town hall has the invisible capture part inside them. I can show you an example:


Although I could make a circular capture part, make it smaller, put it near the entrance, and use your code. So once I try out your version, ill make yours as the solution. However, do you think there is a possibility for making this work on a non-circular capture part?