quite a lengthy read so be warned
What does the code do and what are you not satisfied with?
- The code I have written below is the scripts for my round loop. I made it so that it loops continously when the conditions for running the round are still met and when they are no longer met, the server gets notified by this and immediately stops the function. This is why I am using a coroutine for this. There is a part of the round loop function that can ignore the coroutine conditions as the player might win the round properly, causing them to be the only player in the round.
Script Problems:
- I’m not satisfied with using global variables between server and module scripts
- I’m not satisfied with having to use a coroutine which triggers a function that runs as a while loop, which are three different features used for just one ‘function’
- The round ending system is quite confusing and might not be foolproof (although it is necessary to reward players properly if they have killed everyone in the game
What potential improvements have you considered?
- I’m trying to transfer the variables between scripts using parameters, the main server script cannot detect the change in the module script unless if I return the function (obviously I don’t want to do that)
- I’m willing to sacrifice not being able to end the round immediately when there is not enough players for a simpler way to end the round, already coded in the game which is meant to reward players. That means only using a while loop instead of having to add a coroutine.
How (specifically) do you want to improve the code?
- Removing all the global variables and putting them as parameters of the while loop
- Make the conditions for ending the round simpler and more manageable to edit
- Making the code more efficient would be good
Main server script for review:
-- Variables for Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local Teams = game:GetService("Teams")
-- Variables
local round = require(script.Round)
local Weapon = require(script.Weapon)
local Status = workspace.Info.Status
local Events = ReplicatedStorage.Events
local PlayingTeam = Teams.Playing
local IdleTeam = Teams.Idle
-- Variables for RemoteEvents
local ChangeTeam = Events:FindFirstChild("ChangeTeam")
local ChangeVisibility = Events:FindFirstChild("ChangeVisibility")
-- Variables that will take on a different value later in the Round Loop module script (necessary to put them here)
local GameLoop = nil
_G.ClonedMap = nil
_G.RoundActivated = false
-- Default Value for the game Status
Status.Value = "Waiting for 2 players to start the round"
--[[ Function below would be activated to change the state of the round with a coroutine, if neither condition met
then it ignores the if statements. This is the main focus of this script which handles the round
]]
local function activateRound()
if #PlayingTeam:GetPlayers() >= 2 and _G.RoundActivated == false then
-- If there's more than two players and the round hasn't started yet (activate the round)
GameLoop = coroutine.create(round.GameLoop)
coroutine.resume(GameLoop)
elseif #PlayingTeam:GetPlayers() < 2 and _G.RoundActivated == true then
-- If there's less than two players remaining and the round is still running (stop the round)
coroutine.close(GameLoop)
_G.RoundActivated = false
ChangeVisibility:FireAllClients("MapGui", false)
ChangeVisibility:FireAllClients("PlayingStatus", true)
if _G.ClonedMap then
_G.ClonedMap:Destroy()
for i, instance in _G.SkyboxAspects do
instance:Destroy()
end
end
Status.Value = "Round paused, server has inadequate players to start round"
task.wait(3)
Status.Value = "Waiting for 2 players to start the round"
end
end
--[[ All the code below are mainly used to check every time a player is added or removed from PlayingTeam.
This is important as the coroutine needs to only run when there are two or more players in the PlayingTeam
so when someone leaves, the coroutine immediately stops (coroutine.close), whereas if PlayingTeam
gets 2+ players, it immediately starts (coroutine.resume)
]]
-- Changes a player's team and counter for PlayingTeam and checks if round should be started/stopped
local function changeTeam(player, newTeam)
if newTeam == "Playing" then
player.Team = PlayingTeam
else
player.Team = IdleTeam
end
activateRound()
end
-- Connecting from client (as team change occurs with the click of a button in the client)
ChangeTeam.OnServerEvent:Connect(changeTeam)
-- As player joins affect the amount of players in PlayingTeam, the script also checks if round should be started/stopped
Players.PlayerAdded:Connect(function()
activateRound()
end)
Players.PlayerRemoving:Connect(function()
activateRound()
end)
Round script (handles the loop that the main server script triggers and its contents)
-- Variables
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local Players = game:GetService("Players")
local Lighting = game:GetService("Lighting")
local Teams = game:GetService("Teams")
local round = {}
local Weapon = require(script.Parent.Weapon)
local Events = ReplicatedStorage.Events
local Maps = ServerStorage:WaitForChild("Maps")
local ChangeVisibility = Events:FindFirstChild("ChangeVisibility")
local Status = workspace.Info.Status
local Playing = Teams.Playing
local gameReward = 100
local GameLength = 60
local intermission = 10
local continueRound = false
-- The function for the game loop, it keeps looping it gets activated
function round.GameLoop()
while true do
-- Set RoundActivated to true (cannot create another round) and do countdown to begin round
_G.RoundActivated = true
-- deleted code that handles round intermission (not important for this post)
local AvailableMaps = Maps:GetChildren()
local SelectedMap = AvailableMaps[math.random(1, #AvailableMaps)]
_G.ClonedMap = SelectedMap:Clone()
_G.ClonedMap.Parent = workspace
-- deleted code that handles PlayingTeam players' teleportation to the cloned map (not important)
-- Preparing for the fight with a countdown
for i = 3, 1, -1 do
Status.Value = "Fight starts in " .. i
task.wait(1)
end
-- coroutine cannot end without giving the last standing player a bonus anymore
_G.RoundActivated = "nil"
-- deleted code that handles the battle (not important)
-- Counting down the round length whilst checking the amount of players in the battle
for seconds = GameLength, 0, -1 do
-- Checking to see if the player is still in the round and removing them for the PlayersList table if they aren't
for i, player in pairs(playersList) do
if player then
local character = player.Character
if character then
if character:FindFirstChild("GameTag") then
-- The player is alive
print(player.Name .. " is still alive")
else
-- The player is dead
table.remove(playersList, i)
print(player.Name .. " has been removed")
end
else
-- The player left the game
table.remove(playersList, i)
print(player.Name .. " has been removed")
end
else
-- If the player left the game
table.remove(playersList, i)
print(player.Name .. " has been removed")
end
end
-- Counting down towards 0
Status.Value = seconds .. " seconds remaining. " .. #playersList .. " players remain standing."
if #playersList == 1 then
-- Last person standing
if #Players:GetPlayers() <= 1 then
-- If the second player left the game (to avoid giving player 1 an unnecessary advantage)
Status.Value = "Not enough players to continue to round"
task.wait(2)
Status.Value = playersList[1].Name .. " will be rewarded a fraction of the winning reward"
playersList[1].leaderstats.Cash.Value += gameReward / 20
break
end
-- If it was a fair victory
Status.Value = playersList[1].Name .. " has won the round!"
playersList[1].leaderstats.Cash.Value += gameReward
break
elseif #playersList == 0 then
-- No one survived the round
Status.Value = "No one survived"
break
elseif seconds == 0 then
-- Time runs out
Status.Value = "Time's up!"
task.wait(3)
Status.Value = "Nobody won the round"
for integer, survivor in pairs(playersList) do
survivor.leaderstats.Cash.Value += gameReward / 5
end
break
end
task.wait(1)
end
Status.Value = "Clearing map..."
-- the script removed any items the players use inside the round and teleporting them back to the lobby (deleted)
-- Returning to normal lobby conditions and removing the map lighting
_G.ClonedMap:Destroy()
for i, instance in _G.SkyboxAspects do
instance:Destroy()
end
-- If there are less than 2 players then the round will not repeat (without closing the coroutine)
if #Players:GetPlayers() < 1 then
Status.Value = "Waiting for 2 players to start the round"
_G.RoundActivated = false
break
end
end
end
return round