Making a gamemode system

Universe here!

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.

Thank you in advance!

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
8 Likes

Thanks a ton! this is exactly what i’ve been looking for, as always you have a solution :slight_smile:

2 Likes

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