-- NEW Baseplate project. Script: ServerScriptService > RoundServer -- Remove the template SpawnLocation first. Keep the template Baseplate. local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local MIN_PLAYERS = 1 -- Use 2 after verifying solo; minimum to start a round. local TARGET = 8 local ROUND_SECONDS = 45 local INTERMISSION_SECONDS = 5 local RESULTS_SECONDS = 6 assert(not workspace:FindFirstChild("CoopArena"), "Run only one RoundServer") assert(not ReplicatedStorage:FindFirstChild("RoundState"), "Remove duplicate RoundServer") local arena = Instance.new("Folder") arena.Name = "CoopArena" arena.Parent = workspace local floor = Instance.new("Part") floor.Name = "ArenaFloor" floor.Size = Vector3.new(100, 1, 100) floor.Position = Vector3.new(0, 0.5, 0) floor.Anchored = true floor.Color = Color3.fromRGB(65, 85, 100) floor.Parent = arena local spawn = Instance.new("SpawnLocation") spawn.Name = "CoopSpawn" spawn.Size = Vector3.new(8, 1, 8) spawn.Position = Vector3.new(0, 1.5, 0) spawn.Anchored = true spawn.Neutral = true spawn.Duration = 0 spawn.Parent = arena local coinsFolder = Instance.new("Folder") coinsFolder.Name = "RoundCoins" coinsFolder.Parent = arena local state = Instance.new("Folder") state.Name = "RoundState" state:SetAttribute("Status", "Waiting for players") state:SetAttribute("Collected", 0) state:SetAttribute("Target", TARGET) state:SetAttribute("TimeLeft", 0) state.Parent = ReplicatedStorage local phase = "Waiting" local roundId = 0 local collected = 0 local activeDeadline = 0 local function playerCount() return #Players:GetPlayers() end local function setStatus(text) state:SetAttribute("Status", text) end local function makeCoins(thisRound) for index = 1, TARGET do local angle = (index - 1) * math.pi * 2 / TARGET local coin = Instance.new("Part") coin.Name = string.format("TeamCoin%02d", index) coin.Shape = Enum.PartType.Ball coin.Size = Vector3.new(3, 3, 3) coin.Position = Vector3.new(math.cos(angle) * 24, 3, math.sin(angle) * 24) coin.Anchored = true coin.CanCollide = false coin.CanTouch = true coin.Color = Color3.fromRGB(255, 215, 60) local claimed = false coin.Touched:Connect(function(hit) if phase ~= "Playing" or roundId ~= thisRound or claimed then return end if coin.Parent ~= coinsFolder then return end local character = hit:FindFirstAncestorOfClass("Model") local player = character and Players:GetPlayerFromCharacter(character) if not player then return end local humanoid = character:FindFirstChildOfClass("Humanoid") local root = character:FindFirstChild("HumanoidRootPart") if not humanoid or humanoid.Health <= 0 or not root or not root:IsA("BasePart") then return end local distance = (root.Position - coin.Position).Magnitude if not (distance <= 8) then return end -- Rejects NaN as well as excess distance. if os.clock() >= activeDeadline then return end -- Claim before changing shared score; this callback never yields. claimed = true collected += 1 state:SetAttribute("Collected", collected) coin:Destroy() end) coin.Parent = coinsFolder end end while true do phase = "Waiting" collected = 0 state:SetAttribute("Collected", 0) state:SetAttribute("TimeLeft", 0) setStatus("Waiting for " .. MIN_PLAYERS .. " player(s)") while playerCount() < MIN_PLAYERS do task.wait(1) end phase = "Intermission" setStatus("Next round starts soon") for seconds = INTERMISSION_SECONDS, 1, -1 do if playerCount() < MIN_PLAYERS then break end state:SetAttribute("TimeLeft", seconds) task.wait(1) end if playerCount() < MIN_PLAYERS then continue end roundId += 1 local thisRound = roundId activeDeadline = os.clock() + ROUND_SECONDS phase = "Playing" setStatus("Collect the gold spheres together") makeCoins(thisRound) while collected < TARGET and playerCount() > 0 do local seconds = math.max(0, math.ceil(activeDeadline - os.clock())) state:SetAttribute("TimeLeft", seconds) if seconds == 0 then break end task.wait(0.2) end phase = "Results" state:SetAttribute("TimeLeft", 0) if collected >= TARGET then setStatus("Team success!") elseif playerCount() == 0 then setStatus("Round ended: no players remain") else setStatus("Time expired; try splitting up next round") end coinsFolder:ClearAllChildren() task.wait(RESULTS_SECONDS) end