I’m trying to choose a random player out of all the players that are on the “Playing” team:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local IntermissionUIRemote = ReplicatedStorage:WaitForChild("IntermissionUIRemote")
local ToMapRemote = ReplicatedStorage:WaitForChild("ToMapRemote")
local Intermission = game.Workspace:WaitForChild("Intermission")
local OnGoingRound = game.Workspace:WaitForChild("OnGoingRound")
local CanClickStartButton = game.Workspace:WaitForChild("CanClickStartButton")
local CanStartIntermission = game.Workspace:WaitForChild("CanStartIntermission")
local CompleteSound = game.Workspace:WaitForChild("CompleteSound")
-- Teleport all players to a random spawn and select a random tagger
local function teleportPlayers(plr)
plr.PlayerGui.PickingIT.Frame.Visible = true
plr.PlayerGui.PickingIT.Frame.Text.Visible = true
CanClickStartButton.Value = false
CanStartIntermission.Value = false
Intermission.Value = false
OnGoingRound.Value = true
plr.Team = game.Teams.Playing
local MapSpawns = game.Workspace:WaitForChild("TagMap"):GetChildren()
local validSpawns = {}
-- Collect all valid spawn locations
for _, v in pairs(MapSpawns) do
if v.Name == "MapSpawn" then
table.insert(validSpawns, v)
end
end
if #validSpawns > 0 then
-- Teleport all players to a random spawn point
local randomSpawn = validSpawns[math.random(1, #validSpawns)]
if plr.Character then
plr.Character:SetPrimaryPartCFrame(randomSpawn.CFrame)
end
end
-- Set a random walk speed
plr.Character:WaitForChild("Humanoid").WalkSpeed = 145
wait(0.5)
local allPlayers = game:GetService("Players")
local playersTable = {}
for _, v in pairs(allPlayers:GetPlayers()) do
if v.Team == game.Teams.Playing then
table.insert(playersTable, v)
end
end
wait(5)
-- Select a random player from the playersTable
local ChosenTagger = playersTable[math.random(1,#playersTable)] -- Choose ONE random player
if ChosenTagger and ChosenTagger.Character then
local TaggerBool = ChosenTagger.Character:FindFirstChild("Tagger")
if TaggerBool then
TaggerBool.Value = true
ChosenTagger.Character:WaitForChild("Humanoid").WalkSpeed = 150
if ChosenTagger.PlayerGui:FindFirstChild("TagCooldownUI") then
ChosenTagger.PlayerGui.TagCooldownUI:WaitForChild("Frame").Visible = true
end
plr.PlayerGui.PickingIT.Frame.Text.Visible = false
plr.PlayerGui.PickingIT.Frame.Text2.Visible = true
CompleteSound:Play()
wait(1)
for i = 0, 1, 0.05 do
plr.PlayerGui.PickingIT.Frame.Text2.TextTransparency = i
plr.PlayerGui.PickingIT.Frame.BackgroundTransparency = i
task.wait(0.05) -- Controls the speed of the fade (smaller value means slower fade)
end
plr.PlayerGui.PickingIT.Frame.Visible = false
plr.PlayerGui.PickingIT.Frame.Text.Visible = false
plr.PlayerGui.PickingIT.Frame.Text2.Visible = false
end
end
wait()
game.Workspace:WaitForChild("OnGoingRound").Value = true
game.Workspace:WaitForChild("Intermission").Value = false
game.Workspace:WaitForChild("CanStartIntermission").Value = false
end
ToMapRemote.OnServerEvent:Connect(function(plr)
-- Teleport players to the map and select the Tagger
teleportPlayers(plr)
end)
My issue: Sometimes it’ll choose just one player, which is good, but then it would sometimes choose all or more than one.
local IntermissionUIRemote = game:GetService("ReplicatedStorage"):WaitForChild("IntermissionUIRemote")
local STOPIntermissionUIRemote = game:GetService("ReplicatedStorage"):WaitForChild("STOPIntermissionUIRemote")
local ResetIntermissionUIRemote = game:GetService("ReplicatedStorage"):WaitForChild("ResetIntermissionUIRemote")
local ToMapRemote = game:GetService("ReplicatedStorage"):WaitForChild("ToMapRemote")
local Frame = script.Parent
local Countdown = script.Parent:WaitForChild("Countdown")
local CountdownTick = game.Workspace:WaitForChild("CountdownTick")
local isCountingDown = false
local countdownCoroutine = nil -- To store the countdown coroutine so we can stop it
local function startCountdown()
-- Show the countdown UI
Frame.Visible = true
Frame.Parent.Parent:WaitForChild("WaitingForPlr"):WaitForChild("Label").Visible = false
-- Countdown from 30 to 0
for i = 30, 0, -1 do
if not isCountingDown then
return -- Stop the countdown if isCountingDown is false (e.g., reset or stop happened)
end
Countdown.Text = tostring(i)
CountdownTick:Play()
wait(1)
end
-- After the countdown, hide the UI and trigger teleport
Frame.Visible = false
ToMapRemote:FireServer()
end
IntermissionUIRemote.OnClientEvent:Connect(function()
-- Ensure we are not already counting down
if isCountingDown then return end
-- Start the countdown
isCountingDown = true
countdownCoroutine = coroutine.create(startCountdown)
coroutine.resume(countdownCoroutine)
end)
ResetIntermissionUIRemote.OnClientEvent:Connect(function()
-- Reset the countdown if it's running
isCountingDown = false
-- Stop the countdown coroutine if it's running
if countdownCoroutine then
-- We can't stop the coroutine directly, but we can have it exit early.
-- Just set isCountingDown to false to break the loop in the countdown.
countdownCoroutine = nil
end
-- Reset UI (countdown value and visibility)
Countdown.Text = "30" -- Reset the countdown to 30
Frame.Visible = false -- Hide the countdown UI
-- Start the countdown again from 30
isCountingDown = true
countdownCoroutine = coroutine.create(startCountdown)
coroutine.resume(countdownCoroutine)
end)
STOPIntermissionUIRemote.OnClientEvent:Connect(function()
-- Stop the countdown when STOP event is triggered
isCountingDown = false
-- Hide the countdown UI
Frame.Visible = false
-- Optionally reset the countdown text to 30
Countdown.Text = "30"
end)
This is the LocalScript that triggers it^^^. That LocalScript is triggered from A serverScript which is:
local Button = script.Parent
local players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
-- BoolValues
local Intermission = Workspace:WaitForChild("Intermission")
local CanStartIntermission = Workspace:WaitForChild("CanStartIntermission")
local OnGoingRound = Workspace:WaitForChild("OnGoingRound")
local CanClickStartButton = Workspace:WaitForChild("CanClickStartButton")
-- Remotes
local IntermissionUIRemote = ReplicatedStorage:WaitForChild("IntermissionUIRemote")
local GameResetRemote = ReplicatedStorage:WaitForChild("GameResetRemote")
local STOPIntermissionUIRemote = ReplicatedStorage:WaitForChild("STOPIntermissionUIRemote")
local ResetIntermissionUIRemote = ReplicatedStorage:WaitForChild("ResetIntermissionUIRemote")
local StartButton = Workspace:WaitForChild("StartButton"):WaitForChild("Glow")
local StartButtonSound = Button.Parent:WaitForChild("StartButtonSound")
local BlockedStartButtonSound = Button.Parent:WaitForChild("BlockedStartButtonSound")
local ButtonClicked = false
local ClickCooldown = false
local EnoughPlr = false
local NotEnoughPlr = false
-- Table to track players that are fully loaded
local playersFullyLoaded = {}
local function startCountdown()
IntermissionUIRemote:FireAllClients()
end
local function resetCountdown()
ResetIntermissionUIRemote:FireAllClients()
end
local function stopCountdown()
STOPIntermissionUIRemote:FireAllClients()
end
local function allPlayersReady()
for _, player in pairs(players:GetPlayers()) do
if not playersFullyLoaded[player.UserId] then
return false
end
end
return true
end
local function updatePlayerStatus(plr)
if ClickCooldown then
-- If cooldown is active, return early
print("Click cooldown active! Player cannot click yet.")
return
end
ClickCooldown = true -- Set cooldown to true
local playerCount = #players:GetPlayers()
if playerCount > 1 then
EnoughPlr = true
NotEnoughPlr = false
CanStartIntermission.Value = true
print("There is more than one player in the server.")
elseif playerCount == 1 then
EnoughPlr = false
NotEnoughPlr = true
CanStartIntermission.Value = false
Intermission.Value = false
OnGoingRound.Value = false
print("There is one or no players in the server.")
end
if EnoughPlr then
-- Ensure the UI is visible for the player
local waitingForPlrLabel = plr.PlayerGui:WaitForChild("WaitingForPlr"):WaitForChild("Label")
waitingForPlrLabel.Visible = false
StartButton.Color = Color3.fromRGB(0, 255, 0)
if CanClickStartButton.Value == true then
CanStartIntermission.Value = true
OnGoingRound.Value = false
CanClickStartButton.Value = false
if ButtonClicked == true then
resetCountdown()
Intermission.Value = true
ButtonClicked = false
-- Ensure the sound is playing
if StartButtonSound then
StartButtonSound:Play()
else
print("StartButtonSound not found!")
end
StartButton.Color = Color3.fromRGB(255, 0, 0)
wait(1) -- You can consider using a different timing mechanism if needed
ClickCooldown = false
end
end
elseif NotEnoughPlr then
local allPlayers = players:GetPlayers()
if OnGoingRound.Value == true then
for _, v in pairs(allPlayers) do
v.Team = game.Teams.Lobby
if v.Character then
v.Character:BreakJoints() -- Break joints if character exists
end
end
stopCountdown()
end
stopCountdown()
-- Ensure the UI is visible for the player
local waitingForPlrLabel = plr.PlayerGui:WaitForChild("WaitingForPlr"):WaitForChild("Label")
waitingForPlrLabel.Visible = true
CanStartIntermission.Value = false
OnGoingRound.Value = false
Intermission.Value = false
CanClickStartButton.Value = true
-- Play the blocked sound if button was clicked
if ButtonClicked == true then
ButtonClicked = false
if BlockedStartButtonSound then
BlockedStartButtonSound:Play()
else
print("BlockedStartButtonSound not found!")
end
end
StartButton.Color = Color3.fromRGB(255, 0, 0)
end
wait(1) -- This is important so the cooldown isn't set to false too soon
ClickCooldown = false -- Reset cooldown after 1 second
end
-- Player added event
players.PlayerAdded:Connect(function(plr)
playersFullyLoaded[plr.UserId] = false
plr.CharacterAdded:Connect(function(char)
local humanoid = char:WaitForChild("Humanoid")
humanoid.Health = humanoid.MaxHealth
playersFullyLoaded[plr.UserId] = true
print(plr.Name .. " has fully loaded.")
updatePlayerStatus(plr)
end)
plr.CharacterRemoving:Connect(function()
playersFullyLoaded[plr.UserId] = false
updatePlayerStatus(plr)
end)
updatePlayerStatus(plr)
end)
-- Player removing event
players.PlayerRemoving:Connect(function(plr)
playersFullyLoaded[plr.UserId] = false
updatePlayerStatus(plr)
end)
-- Button click handling
Button.MouseClick:Connect(function(plr)
ButtonClicked = true
if CanClickStartButton.Value == true then
updatePlayerStatus(plr)
end
end)
It’s a mess and I’m so sorry for that. I’ve been at this for days now and it’s all just falling apart.
The serverScript that triggers the localScript, which triggers the ToMap stuff is a script that constantly checks for more than one player in the game when players join, die, leave… and if there’s more than one player, then it does what it needs to (which doesn’t work properly).***
The LocalScript controls the 30 second countdown intermission UI – Stop, Reset, Start countdown.
local function startCountdown()
-- Show the countdown UI
Frame.Visible = true
Frame.Parent.Parent:WaitForChild("WaitingForPlr"):WaitForChild("Label").Visible = false
-- Countdown from 30 to 0
for i = 30, 0, -1 do
if not isCountingDown then
return -- Stop the countdown if isCountingDown is false (e.g., reset or stop happened)
end
Countdown.Text = tostring(i)
CountdownTick:Play()
wait(1)
end
-- After the countdown, hide the UI and trigger teleport
Frame.Visible = false
ToMapRemote:FireServer()
end
I know what’s wrong. I’m testing w/ my alt and i turn my alt’s game volume all the way down, and I can hear the sounds (for the UI, etc…) being played twice. it’s being triggered the amount of times that there is #of players in the game: (2 players in the game = triggered 2 times). But I don’t know how to go about fixing this. Let me know when you think of something please. Thanks for helping too!
I suggest you rewrite most of the code to make it work on a server script while using local scripts for the bare minimum, use module scripts if you know how to. It’ll make debugging easier while making the game less susceptible to exploits. You already have an idea of what you want to do so rewriting the code to run on the server would be easy.
This is actually not that hard to code. Here’s a script that selects a random player on the server-side to prevent exploits:
local players = game.Players:GetChildren()
local plrCount = #players
-- make sure there's actually players in the game
if plrCount > 0 then
local target = players[math.random(plrCount)]
if target:IsA("Player") then
print(target.Name)
end
else
print("no players found")
end
This uses the math.random() function and the Players class to select a random player by returning a list of children in the parent. Hope this helped! If you needed something else, feel free to tell me.
I don’t know how to work with Module scripts, but I’m lost when you say localScripts should be used for the bare minimum when I need local scripts to control the Intermission Countdown UI. unless there’s a way to do that on the server (which would be so much easier). Thanks for helping btw!
I Tried this and now it always picks all players. I’m very positive it’s doing this because the FireAllClients() that are called from the ServerScript – handling the playerjoin/leaves and button press. Thank you so much for helping!!
I tried to do it on the server side as much as possible and it works, but it almost feels like it’s doing the functions for one player at a time; button is clicked, started AND shows countdown ui for one player (not the other one YET) and teleports them, THEN starts AND shows countdown ui for the other player and then teleports them. Here’s the server script that it’s all in (this script is located in ServerScriptService):
local Button = game.Workspace:WaitForChild("StartButton"):WaitForChild("Button"):WaitForChild("ClickDetector")
local players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- BoolValues
local Intermission = game.Workspace:WaitForChild("Intermission")
local CanStartIntermission = game.Workspace:WaitForChild("CanStartIntermission")
local OnGoingRound = game.Workspace:WaitForChild("OnGoingRound")
local CanClickStartButton = game.Workspace:WaitForChild("CanClickStartButton")
-- Remotes
local IntermissionUIRemote = ReplicatedStorage:WaitForChild("IntermissionUIRemote")
local GameResetRemote = ReplicatedStorage:WaitForChild("GameResetRemote")
local STOPIntermissionUIRemote = ReplicatedStorage:WaitForChild("STOPIntermissionUIRemote")
local ResetIntermissionUIRemote = ReplicatedStorage:WaitForChild("ResetIntermissionUIRemote")
local StartButton = game.Workspace:WaitForChild("StartButton"):WaitForChild("Glow")
local StartButtonSound = Button.Parent:WaitForChild("StartButtonSound")
local BlockedStartButtonSound = Button.Parent:WaitForChild("BlockedStartButtonSound")
local ButtonClicked = false
local ClickCooldown = false
local EnoughPlr = false
local NotEnoughPlr = false
local isCountingDown = false
local countdownCoroutine = nil
local CountdownTick = game.Workspace:WaitForChild("CountdownTick")
-- Table to track players that are fully loaded
local playersFullyLoaded = {}
local function allPlayersReady()
for _, player in pairs(players:GetPlayers()) do
if not playersFullyLoaded[player.UserId] then
return false
end
end
return true
end
-- Teleport all players to a random spawn and select a random tagger
local function teleportPlayers()
for _, v in pairs(players:GetPlayers()) do
local player = v
player.PlayerGui.PickingIT.Frame.Visible = true
player.PlayerGui.PickingIT.Frame.Text.Visible = true
CanClickStartButton.Value = false
CanStartIntermission.Value = false
Intermission.Value = false
OnGoingRound.Value = true
player.Team = game.Teams.Playing
local MapSpawns = game.Workspace:WaitForChild("TagMap"):GetChildren()
local validSpawns = {}
-- Collect all valid spawn locations
for _, v in pairs(MapSpawns) do
if v.Name == "MapSpawn" then
table.insert(validSpawns, v)
end
end
if #validSpawns > 0 then
-- Teleport all players to a random spawn point
local randomSpawn = validSpawns[math.random(1, #validSpawns)]
if player.Character then
player.Character:SetPrimaryPartCFrame(randomSpawn.CFrame)
end
end
-- Set a random walk speed
player.Character:WaitForChild("Humanoid").WalkSpeed = 145
local allPlayers = game:GetService("Players")
local playersTable = {}
-- Get players who are in the "Playing" team
for _, v in pairs(allPlayers:GetPlayers()) do
if v.Team == game.Teams.Playing then
table.insert(playersTable, v)
end
end
wait(5)
-- Ensure only one player is chosen as the tagger
local ChosenTagger = playersTable[math.random(1, #playersTable)] -- Choose ONE random player
if ChosenTagger and ChosenTagger.Character then
local TaggerBool = ChosenTagger.Character:FindFirstChild("Tagger")
local CompleteSound = game.Workspace:WaitForChild("CompleteSound")
if TaggerBool then
TaggerBool.Value = true
ChosenTagger.Character:WaitForChild("Humanoid").WalkSpeed = 150
if ChosenTagger.PlayerGui:FindFirstChild("TagCooldownUI") then
ChosenTagger.PlayerGui.TagCooldownUI:WaitForChild("Frame").Visible = true
end
-- Hide the prompt for the original player
player.PlayerGui.PickingIT.Frame.Text.Visible = false
player.PlayerGui.PickingIT.Frame.Text2.Visible = true
CompleteSound:Play()
wait(1)
-- Fade out the "Picking IT" UI
for i = 0, 1, 0.05 do
player.PlayerGui.PickingIT.Frame.Text2.TextTransparency = i
player.PlayerGui.PickingIT.Frame.BackgroundTransparency = i
task.wait(0.05)
end
-- Hide the "Picking IT" UI after the fade
player.PlayerGui.PickingIT.Frame.Visible = false
player.PlayerGui.PickingIT.Frame.Text.Visible = false
player.PlayerGui.PickingIT.Frame.Text2.Visible = false
end
end
end
end
local function startCountdown()
for _, v in pairs(players:GetPlayers()) do
local player = v
local Frame = player.PlayerGui:WaitForChild("IntermissionUI"):WaitForChild("Frame")
local Countdown = Frame:WaitForChild("Countdown")
-- Show the countdown UI
Frame.Visible = true
Frame.Parent.Parent:WaitForChild("WaitingForPlr"):WaitForChild("Label").Visible = false
-- Countdown from 30 to 0
for i = 10, 0, -1 do
if not isCountingDown then
return -- Stop the countdown if isCountingDown is false (e.g., reset or stop happened)
end
Countdown.Text = tostring(i)
CountdownTick:Play()
wait(1)
end
-- After the countdown, hide the UI and trigger teleport
Frame.Visible = false
isCountingDown = false
teleportPlayers()
end
end
local function updatePlayerStatus2()
ClickCooldown = true -- Set cooldown to true
if EnoughPlr then
StartButton.Color = Color3.fromRGB(0, 255, 0)
if CanClickStartButton.Value == true then
CanStartIntermission.Value = true
OnGoingRound.Value = false
if ButtonClicked == true then
isCountingDown = true
startCountdown()
Intermission.Value = true
CanClickStartButton.Value = false
ButtonClicked = false
-- Ensure the sound is playing
if StartButtonSound then
StartButtonSound:Play()
else
print("StartButtonSound not found!")
end
StartButton.Color = Color3.fromRGB(255, 0, 0)
wait(1) -- You can consider using a different timing mechanism if needed
ClickCooldown = false
end
end
elseif NotEnoughPlr then
local allPlayers = players:GetPlayers()
if OnGoingRound.Value == true then
for _, v in pairs(allPlayers) do
v.Team = game.Teams.Lobby
if v.Character then
v.Character:BreakJoints() -- Break joints if character exists
end
end
--stopCountdown()
end
--stopCountdown()
CanStartIntermission.Value = false
OnGoingRound.Value = false
Intermission.Value = false
CanClickStartButton.Value = true
-- Play the blocked sound if button was clicked
if ButtonClicked == true then
ButtonClicked = false
if BlockedStartButtonSound then
BlockedStartButtonSound:Play()
else
print("BlockedStartButtonSound not found!")
end
end
StartButton.Color = Color3.fromRGB(255, 0, 0)
end
wait(1) -- This is important so the cooldown isn't set to false too soon
ClickCooldown = false -- Reset cooldown after 1 second
end
local function updatePlayerStatus1()
local playerCount = #players:GetPlayers()
-- Check if there are enough players (at least 2)
if playerCount > 1 then
EnoughPlr = true
NotEnoughPlr = false
CanStartIntermission.Value = true
StartButton.Color = Color3.fromRGB(0, 255, 0)
print("There is more than one player in the server.")
elseif playerCount == 1 then
EnoughPlr = false
NotEnoughPlr = true
CanStartIntermission.Value = false
Intermission.Value = false
OnGoingRound.Value = false
StartButton.Color = Color3.fromRGB(255, 0, 0)
print("There is one or no players in the server.")
end
end
-- Player joining event
players.PlayerAdded:Connect(function(plr)
playersFullyLoaded[plr.UserId] = false
plr.CharacterAdded:Connect(function(char)
local humanoid = char:WaitForChild("Humanoid")
humanoid.Health = humanoid.MaxHealth
playersFullyLoaded[plr.UserId] = true
print(plr.Name .. " has fully loaded.")
end)
plr.CharacterRemoving:Connect(function()
playersFullyLoaded[plr.UserId] = false
end)
updatePlayerStatus1()
end)
-- Player leaving event
players.PlayerRemoving:Connect(function(plr)
playersFullyLoaded[plr.UserId] = false
updatePlayerStatus1()
end)
-- Button click handling
Button.MouseClick:Connect(function()
ButtonClicked = true
if CanClickStartButton.Value == true then
updatePlayerStatus2()
end
end)