Suggestions To Reformat Code To Be More Readable

Hello!
I wrote some code for a race system, but I am having trouble debugging it and adding new features to it due to its unreadablity. I’m looking for some suggestions for how I can reformat this code to be more readable. It is a ModuleScript that is accessed by a few other scripts, creating a front-end/back-end system. Thanks!

The code :skull::

BadgeService = game:GetService("BadgeService")
FadeToBlack = require(game.StarterGui.FadeToBlackGoBrr.Fade)

local RaceManager = {}
RaceManager.firstPlaceLoot = {
	crystals = 3,
	XP = 50,
	aetherDust = 5
}

RaceManager.secondPlaceLoot = {
	crystals = 2,
	XP = 25,
	aetherDust = 3
}

RaceManager.thirdPlaceLoot = {
	crystals = 1,
	XP = 10,
	aetherDust = 2
}

TweenService = game:GetService("TweenService")

RaceManager.playersRequiredForRace = 1

--When a player joins the race:
RaceManager.JoinRace = function(raceName, playerName)
	--Get the neccessary objects (player, race folder, attribute(s)):
	local raceFolder = game.Workspace:WaitForChild(raceName.."Race")
	local playerObject = game.Players:WaitForChild(playerName)
	local raceAttribute = nil
	local progressAttribute = nil
	local startTimeAttribute = nil

	--If isRacing is not true, join the race. If isRacing is true, let the player know they can't join:
	if raceFolder.IsRacing.Value == false then
		--Create the race attribute:
		raceAttribute = playerObject:SetAttribute("RaceName", raceName)

		--Create the race progress attribute:
		progressAttribute = playerObject:SetAttribute("RaceProgress", 0)

		--Create the player time attribute:
		startTimeAttribute = playerObject:SetAttribute("StartTime", 0)


		--Increase the value holding the number of players in the race:
		raceFolder.PlayersInRace.Value += 1

		FadeToBlack.FadeToBlackWithWait(game.Players:FindFirstChild(playerName), .25, 0.75)
		wait(0.25)

		game.ReplicatedStorage.JoinLeaveRace:Fire(playerName, "Joining")
		

		game.Workspace:WaitForChild(playerName):SetPrimaryPartCFrame(raceFolder.StartBlock.CFrame * CFrame.Angles(0, math.rad(180), 0))
		wait(.1)
		game.Workspace:WaitForChild(playerName).PrimaryPart.Anchored = true

		playerObject.PlayerGui.LevelGui.Enabled = false

		return true
	else
		return false
	end
end

--When the player leaves a race:
RaceManager.LeaveRace = function(playerName)
	--Get the neccessary objects (player, race folder):
	local playerObject = game.Players:WaitForChild(playerName)
	local raceName = playerObject:GetAttribute("RaceName")
	local raceFolder = game.Workspace:WaitForChild(raceName.."Race")

	--Remove 1 from the value holding the number of players in the race. If less than 1, stop the race entirely:
	raceFolder.PlayersInRace.Value -= 1
	--raceFolder.PlayersRacing:FindFirstChild(playerName.."Completed"):Destroy()

	if raceFolder.PlayersInRace.Value <= 1 then
		raceFolder.IsRacing.Value = false
		raceFolder.PlayersInRace.Value = 0
	end

	
	FadeToBlack.FadeToBlackWithWait(game.Players:FindFirstChild(playerName), .25, 1.5)
	wait(0.25)

	game.ReplicatedStorage.JoinLeaveRace:Fire(playerName, "Leaving")

	wait(0.125)

	--Delete the player's race attribute to avoid unintentional race joins:
	playerObject:SetAttribute("RaceName", nil)
	playerObject:SetAttribute("RaceProgress", nil)

	if playerObject:FindFirstChild(raceName.."Checkpoints") then
		playerObject:WaitForChild(raceName.."Checkpoints"):Destroy()
	end

	local currentPlayers = 0
	for index, playerLooped in pairs(game.Players:GetPlayers()) do
		if playerLooped:GetAttribute("RaceName") ~= nil then
			currentPlayers += 1
		end
	end

	if currentPlayers == 0 then
		raceFolder.IsRacing.Value = false
		raceFolder.PlayersInRace.Value = 0
	end

	game.Workspace:WaitForChild(playerName):SetPrimaryPartCFrame(raceFolder.ReturnBlock.CFrame)
	game.Workspace:WaitForChild(playerName).PrimaryPart.Anchored = false
	--playerObject.PlayerGui.LevelGui.Enabled = true
	return true
end

--When a player finishes a race:
local playersCompleted = 0

RaceManager.FinishRace = function(playerWhoFinishedName, raceName, isAI)
	if isAI == false then
		local player = game.Players:FindFirstChild(playerWhoFinishedName)
		local raceFolder = game.Workspace:WaitForChild(raceName.."Race")
		local playersRacing = raceFolder.PlayersRacing
		local playerPosition = 0
		local playerTime = os.clock() - player:GetAttribute("StartTime")

		local awardedCrystals
		local awardedAetherDust
		local awardedXP

		for index, playerCompleted in pairs(playersRacing:GetChildren()) do
			if playerCompleted.Value == true then
				playersCompleted += 1
			end
		end


		playersRacing:FindFirstChild(playerWhoFinishedName.."Completed").Value = true

		playerPosition = playersCompleted + 1

		if player.Data.RaceTimes:FindFirstChild(raceName.."Race").Value > playerTime then
			player.Data.RaceTimes:FindFirstChild(raceName.."Race").Value = playerTime
		end

		--Do stuff with the placing:
		if playersCompleted == 0 then
			player:WaitForChild("Data"):WaitForChild("Crystals").Value += RaceManager.firstPlaceLoot.crystals * RaceManager.GetCheckpoints(raceName)
			player:WaitForChild("Data"):WaitForChild("XP").Value += RaceManager.firstPlaceLoot.XP
			player:WaitForChild("Data"):WaitForChild("AetherDust").Value += RaceManager.firstPlaceLoot.aetherDust

			awardedCrystals = RaceManager.firstPlaceLoot.crystals * RaceManager.GetCheckpoints(raceName)
			awardedXP = RaceManager.firstPlaceLoot.XP
			awardedAetherDust = RaceManager.firstPlaceLoot.aetherDust

			--Give Certified Racer badge:
			BadgeService:AwardBadge(player.UserId, 2127816704)

			if player.Data.Settings.ProMode.Value == true then
				BadgeService:AwardBadge(player.UserId, 2127816715)
			end
		elseif playersCompleted == 1 then
			player:WaitForChild("Data"):WaitForChild("Crystals").Value += RaceManager.secondPlaceLoot.crystals * RaceManager.GetCheckpoints(raceName)
			player:WaitForChild("Data"):WaitForChild("XP").Value += RaceManager.secondPlaceLoot.XP
			player:WaitForChild("Data"):WaitForChild("AetherDust").Value += RaceManager.secondPlaceLoot.aetherDust

			awardedCrystals = RaceManager.secondPlaceLoot.crystals * RaceManager.GetCheckpoints(raceName)
			awardedXP = RaceManager.secondPlaceLoot.XP
			awardedAetherDust = RaceManager.secondPlaceLoot.aetherDust
		elseif playersCompleted == 2 then
			player:WaitForChild("Data"):WaitForChild("Crystals").Value += RaceManager.thirdPlaceLoot.crystals * RaceManager.GetCheckpoints(raceName)
			player:WaitForChild("Data"):WaitForChild("XP").Value += RaceManager.thirdPlaceLoot.XP
			player:WaitForChild("Data"):WaitForChild("AetherDust").Value += RaceManager.thirdPlaceLoot.aetherDust

			awardedCrystals = RaceManager.thirdPlaceLoot.crystals * RaceManager.GetCheckpoints(raceName)
			awardedXP = RaceManager.thirdPlaceLoot.XP
			awardedAetherDust = RaceManager.thirdPlaceLoot.aetherDust
		end

		game.ReplicatedStorage.FinishedRace:Fire(playerWhoFinishedName, playerPosition, playerTime, awardedCrystals, awardedXP, awardedAetherDust)
		game.ReplicatedStorage.FinishV2Race:FireClient(player, playerPosition, playerTime, awardedCrystals, awardedXP, awardedAetherDust)

		wait(0.1)

		raceFolder.PlayersInRace.Value += 1

		FadeToBlack.FadeToBlackWithWait(player, .25, 0.25)
		wait(0.25)
		
		RaceManager.LeaveRace(playerWhoFinishedName)

		return playerPosition
	else
		playersCompleted += 1
	end
end


RaceManager.GetCurrentRankings = function(raceName)
	local rankings = {}

	--Players:
	for index, player in pairs(game.Players:GetPlayers()) do
		if player:GetAttribute("RaceName") == raceName then

			local playerProgress = math.floor(player:GetAttribute("RaceProgress") * 100) / 100
			local playerRaceData = {
				player.Name,
				playerProgress
			}

			table.insert(rankings, playerRaceData)
		end
	end

	--AI:
	for index, AI in pairs(game.Workspace:FindFirstChild(raceName.."Race").AI:GetChildren()) do
		local AIProgress = math.floor(AI:GetAttribute("RaceProgress") * 100) / 100
		local AIRaceData = {
			AI.Name,
			AIProgress
		}

		table.insert(rankings, AIRaceData)
	end

	table.sort(rankings, function(a,b)
		return a[2] > b[2]
	end)

	return rankings
end


RaceManager.GetRankingOfPlayer = function(playerName, raceName)
	local player = game.Players:FindFirstChild(playerName)

	local rankingTable = RaceManager.GetCurrentRankings(raceName)

	for index, playerData in pairs(rankingTable) do
		if playerData[1] == playerName then
			return index
		end
	end
end


RaceManager.CheckpointCollection = function(raceName)
	local raceFolder = game.Workspace:WaitForChild(raceName.."Race")
	local numberOfCheckpoints = #raceFolder.Checkpoints:GetChildren() - 2

	for index, checkpoint in raceFolder.Checkpoints:GetChildren() do
		if checkpoint.Name ~= "CloneInto" and checkpoint.Name ~= "CreatePath" then
			checkpoint.Detector.Detector.Touched:Connect(function(hitObject)
				local checkpointNumber = tonumber(string.sub(checkpoint.Name, 11, #checkpoint.Name))

				if hitObject.Name == "HumanoidRootPart" and hitObject.Parent:GetAttribute("IsAI") == nil then
					local hitPlayer = game.Players:FindFirstChild(hitObject.Parent.Name)

					if hitPlayer:GetAttribute("RaceName") == raceName then

						if checkpointNumber == 1 then
							if checkpointNumber == numberOfCheckpoints or hitPlayer:FindFirstChild(raceName.."Checkpoints"):FindFirstChild(checkpointNumber + 1).Value ~= true then
								hitPlayer:FindFirstChild(raceName.."Checkpoints"):FindFirstChild(checkpointNumber).Value = true

								RaceManager.RacePercentage = checkpointNumber / numberOfCheckpoints
								
								game.ReplicatedStorage.CheckpointCollected:Fire(hitPlayer.Name, RaceManager.GetRankingOfPlayer(hitPlayer.Name, raceName), raceName, #raceFolder.AI:GetChildren() + #raceFolder.PlayersRacing:GetChildren())
								game.ReplicatedStorage.CollectCheckpointV2Race:FireClient(hitPlayer, RaceManager.GetRankingOfPlayer(hitPlayer.Name, raceName), raceName, checkpointNumber)

								hitPlayer:SetAttribute("RaceProgress", checkpointNumber / numberOfCheckpoints)
							end
						elseif hitPlayer:FindFirstChild(raceName.."Checkpoints"):FindFirstChild(checkpointNumber - 1).Value == true then
							if checkpointNumber == numberOfCheckpoints or hitPlayer:FindFirstChild(raceName.."Checkpoints"):FindFirstChild(checkpointNumber + 1).Value ~= true then
								hitPlayer:FindFirstChild(raceName.."Checkpoints"):FindFirstChild(checkpointNumber).Value = true

								game.ReplicatedStorage.CheckpointCollected:Fire(hitPlayer.Name, RaceManager.GetRankingOfPlayer(hitPlayer.Name, raceName), raceName, #raceFolder.AI:GetChildren() + #raceFolder.PlayersRacing:GetChildren())
								game.ReplicatedStorage.CollectCheckpointV2Race:FireClient(hitPlayer, RaceManager.GetRankingOfPlayer(hitPlayer.Name, raceName), raceName, checkpointNumber)
								
								if checkpointNumber == numberOfCheckpoints then
									RaceManager.FinishRace(hitPlayer.Name, raceName, false)
								end

								hitPlayer:SetAttribute("RaceProgress", checkpointNumber / numberOfCheckpoints)
							end
						end
					end
				elseif hitObject.Parent:GetAttribute("IsAI") == true then
					hitObject.Parent:SetAttribute("RaceProgress", checkpointNumber / numberOfCheckpoints)

					if checkpointNumber == numberOfCheckpoints then
						RaceManager.FinishRace("lol get rekt it's AI", raceName, true)
						hitObject.Parent:Destroy()
					end
				end
			end)
		end
	end
end


RaceManager.GetCheckpoints = function(raceName)
	--Gets the number of checkpoints in the race:
	local raceFolder = game.Workspace:WaitForChild(raceName.."Race")
	local numberOfCheckpoints = #raceFolder.Checkpoints:GetChildren() - 2

	return numberOfCheckpoints
end


RaceManager.StartRace = function(raceName)
	--Get the neccessary objects (race folder, table of players who are in the race):
	local raceFolder = game.Workspace:WaitForChild(raceName.."Race")
	local playersInRace = {}
	local numberOfCheckpoints = RaceManager.GetCheckpoints(raceName)

	local AIFolder = game.ServerStorage.AIDrones:GetChildren()
	local numberOfAI = 3

	playersCompleted = 0

	--Check if there are enough players in the race to start. If not, stop the function:
	if raceFolder.PlayersInRace.Value < RaceManager.playersRequiredForRace then
		return false
	end


	for index, playerRacingValue in pairs(raceFolder.PlayersRacing:GetChildren()) do
		playerRacingValue:Destroy()
	end


	for index, AI in pairs(raceFolder.AI:GetChildren()) do
		AI:Destroy()
	end


	for index = 1, numberOfAI, 1 do
		local AIDrone = AIFolder[math.random(1, #AIFolder)]:Clone()
		AIDrone.Parent = raceFolder.AI
		AIDrone.PrimaryPart.Anchored = true
		AIDrone:SetPrimaryPartCFrame(raceFolder.StartBlock.CFrame)
		AIDrone.Target.Value = raceFolder.Checkpoints.Checkpoint1.Detector.Detector.Position
		AIDrone:SetAttribute("RaceProgress", 0)

		for partNumber, part in pairs(AIDrone.Struts:GetChildren()) do
			part.Color = Color3.fromRGB(175, 0, 255)
		end
	end


	--Loop through the players to find the ones that have joined the race:
	for index, player in pairs(game.Players:GetPlayers()) do
		if player:GetAttribute("RaceName") then
			if player:GetAttribute("RaceName") == raceName then
				table.insert(playersInRace, player)

				local playerRacingValue = Instance.new("BoolValue")
				playerRacingValue.Name = player.Name.."Completed"
				playerRacingValue.Parent = raceFolder.PlayersRacing
				playerRacingValue.Value = false
			end
		end
	end

	--Set the isRacing value in the race folder to true, preventing more players from joining:
	raceFolder.IsRacing.Value = true

	--For each of the players, start the race:
	for index, player in pairs(playersInRace) do
		local character = game.Workspace:WaitForChild(player.Name)
		character.HumanoidRootPart.Anchored = true

		local countdown = coroutine.wrap(function()
			--Create the checkpoint folder:
			local checkpointFolder = Instance.new("Folder")
			checkpointFolder.Parent = player
			checkpointFolder.Name = raceName.."Checkpoints"

			--Create the checkpoint values:
			for index = 1, numberOfCheckpoints, 1 do
				local checkpointValue = Instance.new("BoolValue")
				checkpointValue.Name = index
				checkpointValue.Parent = checkpointFolder
			end


			--Do the countdown:
			local raceGui = player.PlayerGui.RaceGuiV2
			local info = TweenInfo.new(0.2)
			local inTween = TweenService:Create(raceGui.Countdown, info, {Size = UDim2.new(0.15, 0, 0.3, 0)})
			local outTween = TweenService:Create(raceGui.Countdown, info, {Size = UDim2.new(0, 0, 0, 0)})

			raceGui.Countdown.Visible = true
			raceGui.Countdown.TextContent.Text = "3"
			inTween:Play()
			wait(0.8)
			outTween:Play()
			wait(0.2)

			raceGui.Countdown.TextContent.Text = "2"
			inTween:Play()
			wait(0.8)
			outTween:Play()
			wait(0.2)

			raceGui.Countdown.TextContent.Text = "1"
			inTween:Play()
			wait(0.8)
			outTween:Play()
			wait(0.2)

			character.HumanoidRootPart.Anchored = false
			player:SetAttribute("StartTime", os.clock())

			raceGui.Countdown.TextContent.Text = "Go!"
			inTween:Play()
			wait(0.8)
			outTween:Play()

			raceGui.Countdown.Visible = false
		end)

		countdown()
	end

	wait(3)

	for index, AIDrone in pairs(raceFolder.AI:GetChildren()) do
		AIDrone.PrimaryPart.Anchored = false
	end

	RaceManager.CheckpointCollection(raceName)
end

return RaceManager
1 Like

this block of code has alot of repetition. Whenever you see patterns like this you can often shrink it down

2 Likes

Line that are all the same in each part of this if - loop can be taken out of it if your going to read it in all three scenarios anyway

1 Like

You also have alot of nested if statments. if you have something like:

if something then
if anotherthing then

 end

end

You can turn it into
If something and anotherthing then

end

2 Likes

Thank you!!! I appreciate the help, and I’ll work on those solutions

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.