I’ve been working on a little project on mine, and ive gotten to the point where I need to make a gamemode system, i’ve looked on the dev forums and not alot has been explained here… atleast from what i’ve seen. I’m trying to make something similar to Epic Minigames I can take care of all the scripting, I just need to figure out how to implement it.
I’d want something like dodgeball and Hot Potato! with my team system being implemented but im not really sure where to start.
For my system I am currently using the code from the post below which is pretty good.
Promises are really good for this scenario as it can handle race conditions (times up Promise.delay(), team eliminated, admin force change round both use Promise.fromEvent). There are probably other solutions but it should work now.
I have modularized it into OOP format and added some utility functions to change and respawn teams, to add gamemodes you will have to create more functions similar to :startCapturePointGame below:
You will also have to remove some stuff such as triangle map, as that is confidential and messy code at the moment.
There is also this tutorial by @BRicey763 which should provide further information:
RoundSystem script:
--In server script, just start the round system once
local round = RoundSystem.new()
round:Start()
--Module script:
--[[
Singleton RoundSystem class
]]
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerScriptService = game:GetService("ServerScriptService")
local Teams = game:GetService("Teams")
local BlueTeam : Team = Teams.Blue
local RedTeam : Team = Teams.Red
local ChoosingTeam = Teams.Choosing
local DEFAULT_MAP_SETTINGS = {
["XSize"] = 6000,
["ZSize"] = 6000,
["ChunkSize"] = 500,
["Multiplier"] = 1,
-- ["Height"] = 0; --640 by default
}
local LOBBY_MAP_SETTINGS = {
["XSize"] = 3000,
["ZSize"] = 3000,
["ChunkSize"] = 250,
["BaseChunkAmounts"] = 4,
["Multiplier"] = 1,
["Height"] = 300; --640 default
["Frequency"] = 0.185; --0.135 default
}
local Promise = require(ReplicatedStorage.Shared.Util.Promise)
local Maid = require(ReplicatedStorage.Shared.Util.Maid)
local Signal = require(ReplicatedStorage.Shared.Signal)
local TriangleMap = require(ServerScriptService.Server.Modules.TriangleMap)
local Network = require(ReplicatedStorage.Shared.Network)
local RoundSystem = {}
RoundSystem.__index = RoundSystem
RoundSystem.RoundWonSignal = Signal.new()
function RoundSystem.new()
local self = setmetatable({}, RoundSystem)
self._WaitStartTime = nil --:GetServerTimeNow
self._WaitLength = nil
self._WaitMessage = nil
self._GlobalRoundMaid = Maid.new()
return self
end
function RoundSystem:SendRoundStatus(status, length)
self._WaitStartTime = workspace:GetServerTimeNow() --:GetServerTimeNow
self._WaitLength = length
self._WaitMessage = status
Network:FireAllClients("RoundTimer", workspace:GetServerTimeNow(), length, status)
end
function RoundSystem:Start()
--Be able to get current time from client even when joining in between
--Lobby initially
--Load lobby map
self._CurrentRound = self.waitForPlayers
while true do
local newRound = self:_CurrentRound()
self._CurrentRound = newRound:expect()
task.wait() --avoid crashing studio on accident
end
end
--Promise, round system
local function enoughPlayers()
return #Players:GetPlayers() >= 2
end
-- promise that resolves when enough players have joined the game
local function notEnoughPlayersPromise()
return Promise.fromEvent(Players.PlayerRemoving, function()
print("Player removing?")
return #Players:GetPlayers() - 1 < 2
end)
end
-- promise that resolves when there are no longer enough players
local function enoughPlayersPromise()
return Promise.fromEvent(Players.PlayerAdded, function()
print("Enough players?", enoughPlayers())
return enoughPlayers()
end)
end
-- promise that resolves when there are no longer enough players
local function roundWonPromise()
return Promise.fromEvent(RoundSystem.RoundWonSignal, function(team)
return team
end)
end
function RoundSystem:waitForPlayers()
print("Waiting for players, creating lobby")
self.LobbyMap = TriangleMap.new(LOBBY_MAP_SETTINGS)
self.LobbyMap:GenerateLobbyMap()
self._GlobalRoundMaid:GiveTask(self.LobbyMap)
RoundSystem.changeAllPlayersTeam(RedTeam)
RoundSystem.resetPlayers()
self:AutoAssignTeam(RedTeam)
return enoughPlayersPromise()
:andThenReturn(RoundSystem.intermission)
-- enough players, so we can start the intermission! :D
end
function RoundSystem:intermission()
print("Waiting to start the game")
--Intermission to start the game
local startCondition = Promise.race({
notEnoughPlayersPromise():andThenReturn(RoundSystem.waitForPlayers),
-- not enough players, so we go back to waiting for players :'(
Promise.delay(5) --Confirm the player stays and doesn go away
}):andThenReturn("Success!"):andThen(function()
-- load the map
print("Players found, have a bit of remaining lobby time")
end)
:andThenCall(Promise.delay, 5)
:andThenReturn(RoundSystem.startCapturePointGame)
return startCondition
end
function RoundSystem:startCapturePointGame()
self._GlobalRoundMaid:DoCleaning()
local capturePointMap = TriangleMap.new(DEFAULT_MAP_SETTINGS)
capturePointMap:GenerateDefaultMapWithBase()
self._GlobalRoundMaid:GiveTask(capturePointMap)
self:AutoAssignTeam(ChoosingTeam)
self.changeAllPlayersTeam(ChoosingTeam)
self.resetPlayers()
print("Game started, waiting for condition!")
local roundTime = 20
local displayWinningTeamTime = 5
self:SendRoundStatus("Capture Point", roundTime)
return Promise.race({
Promise.delay(roundTime):andThenReturn("Time is up");
roundWonPromise();
})
:andThen(function(winners : Team)
-- handle winners
print("These are the winners or not: ",winners)
self:SendRoundStatus("Capture Point", displayWinningTeamTime)
end)
:andThenCall(Promise.delay, 5)
:andThen(function()
print("Cleaning up game")
self._GlobalRoundMaid:DoCleaning()
end)
:andThen(function() -- can't use andThenReturn here because of conditional
print("Restart game? Or not enough players")
return enoughPlayers() and RoundSystem.startCapturePointGame or RoundSystem.waitForPlayers
-- return the next block of code we should do!
end)
end
function RoundSystem.changeAllPlayersTeam(team)
for _, player : Player in pairs(Players:GetPlayers()) do
player.Team = team
end
end
function RoundSystem.resetPlayers()
for _, player : Player in pairs(Players:GetPlayers()) do
task.spawn(function()
player:LoadCharacter()
end)
end
end
function RoundSystem:AutoAssignTeam(team)
if self._AutoAssignConnection then
self._AutoAssignConnection:Disconnect()
self._AutoAssignConnection = nil
end
if team ~= nil then
self._AutoAssignConnection = Players.PlayerAdded:Connect(function(player : Player)
player.Team = team
player:LoadCharacter()
end)
end
end
return RoundSystem