Capture Point Script is Kinda Buggy, Doesn't Work as Intended

This script is supposed to run a game mode I have in my game, where user(s) from a team has to continuously hold down a point for a few minutes in order to win a cash bounty. If they move away from the capture point, the timer stops and someone else can capture the point.
The code works, but not extremely well.
There are a couple of issues:

  • Jumping, changing weapons, anything that isn’t standing still results in the capture point being “lost”

  • Even when another team is alive and currently at the checkpoint, a different team can walk up and it will register them as capturing it, without eliminating the team that is currently there.

Here’s the code:

---everything has been defined in this code

flag.touch.Touched:Connect(function(hit)
			local player = game.Players:GetPlayerFromCharacter(hit.Parent)
			if player ~= nil and hit.Name == "Torso" then 
				if flag.Holder.Value == nil or player.Team ~= flag.Holder.Value then
					if Defender == nil or Defender ~= player.Team then
						msg = "[MISSION]: "..player.Team.Name.." has captured "..flag.Parent.Parent.Name.."!"
						notif:FireAllClients(msg,Enum.FontSize.Size24,Enum.Font.ArialBold,Color3.fromRGB(255, 255, 255))
						flag.Holder.Value = player.Team
						Defender = player.Team
						script.sec.Value = 180
						flag.BrickColor = player.Team.TeamColor
						flag.touch.SelectionBox.Color3 = player.Team.TeamColor.Color
						flag.notif.flag.Image = ""..player.Team.flag.Texture
					elseif Defender == player.Team and flag.Holder.Value ~= player.Team then
						msg = "[MISSION]: You've recaptured "..flag.Parent.Parent.Name.."!"
						notif:FireClient(player,msg,Enum.FontSize.Size24,Enum.Font.ArialBold,Color3.fromRGB(255, 255, 255))
						flag.Holder.Value = player.Team
					else	
						msg = "[MISSION]: You must eliminate all hostiles before you can capture this flag!"
						notif:FireClient(player,msg,Enum.FontSize.Size24,Enum.Font.ArialBold,Color3.fromRGB(255, 255, 255)) 
                           -----------above "else" statement never happens, it just awards control, even if another team is still controlling it. I want it to be where you would need to eliminate the user(s) holding down the capture point first before you can capture it yourself.
					end	
				end
			end
		end)
		flag.touch.TouchEnded:Connect(function(old) --- not as much of an issue, but want it so that as long as player is touching the big hit box surrounding the flag/capture point, this won't be triggered.
			local player = game.Players:GetPlayerFromCharacter(old.Parent)
			if player ~= nil and old.Name == "Torso" then 
				if Defender == player.Team then
					msg = "[MISSION]: You've lost control of "..flag.Parent.Parent.Name.." by not being in its vicinity!"
					notif:FireAllClients(player,msg,Enum.FontSize.Size24,Enum.Font.ArialBold,Color3.fromRGB(255, 255, 255)) --- another issue besides the random losing control is this being spammed.
					flag.Holder.Value = nil
				end
			end	
		end)

How would I fix these issues? They’re not super bad/game breaking but I’ve been in contact with players of my game and they say it can be annoying.

If your code is broken or otherwise not working as intended, please use the Scripting Support category, not Code Review. The latter category is only for receiving improvement tips on code that works and does so as intended. I have recategorised this post as such.

Don’t use Touched/TouchEnded events for determining if a player is within a zone or standing somewhere important. These are physics-y events and not likely to get the gameplay effect you want; this also might be causing whatever issue you’re having, as doing anything that isn’t standing still may cause Touched/TouchEnded to fire sporadically.

You should instead write code to determine if a player is within a zone and use that for your enter & leaving logic. Here’s a function that you might find useful:

local function isPointInPart(point, part)
	local pt = part.CFrame:PointToObjectSpace(point)
	return math.abs(pt.X) <= part.Size.X/2
	   and math.abs(pt.Y) <= part.Size.Y/2
	   and math.abs(pt.Z) <= part.Size.Z/2
end

Would I need to put that in a while loop that constantly checks, or have it be fired upon an event?

Connect a function to RunService.Stepped and check which players have entered/left the zone (using function isPointInPart or a similar one) each frame or less often if you anticipate performance issues.

1 Like