I am trying to create Round/“Stage” objects by combining a map with a game mode, but I am having trouble daisy-chaining it together or imagining anything else.
I think the Stage should be a superclass that gives methods/signals like :RoundStart() :RoundEnd(), Game mode modules should set up objectives, win conditions, and map modules should set up parts and connect to signals from the game mode.
To really simplify my approach, something like
local map = require(crossroads)
local gamemode = require(ctf)
local stage = map.new(gamemode)
--map modules
local map = require(StageSuperClass).new() --stage class as the base
map.__index = map
map.Name= "Crossroads"
function map.new(gamemode)
local newmap = gamemode.new()
setmetatable(newmap, map)
return newmap
end
--mode modules
local mode = {}
mode.__index = mode
mode.GameMode = "Capture the Flag"
function mode.new()
local gamemode= {}
setmetatable(gamemode, mode)
return gamemode
end
I know this currently doesn’t retain the game mode. Not sure where to go. previously the map just directly got the game mode without .new() before I realized it was overwriting the entire module everytime.
Its easy to imagine map/mode as subclasses of stage, but not subclasses of eachother. Not sure how to combine. What do you think of this attempt, would you have done it differently?
Personally, I wouldn’t use OOP for a round system unless it’s very complicated, but that’s just my preference. If it works, and is easy to debug, then your fine.
I don’t think you should have the module script handle and change the game, keep the module script as something to compose from thru other normal scripts and not have it change when you use it:
local map = require(StageSuperClass).new() --stage class as the base
map.__index = map
map.Name= "Crossroads"
function map.new(gamemode)
return setmetatable(gamemode.new(), map)
end
That is the diamond problem, I talked about it here like @JamminRedPandaMan said and why I moved on to use ECS instead (Though it has other problems like too many components and my bad organization because I’m new to ECS and didn’t plan my system).
If you are still going with an OOP approach consider componentizing it instead. Discussion here.
Instead of a IsA relationship you use HasA relationship maybe something like this?.
map.__index = map
function map.new(gamemode, mapSettings)
local newmap= map.new()
setmetatable(newmap, map)
local mode = mode.new(mapSettings)
if mode == "CaptureTheFlag" then
self:AddFlagToMap()
end
return newmap
end
Each map has a mode which can modify the map object.
Honestly either way, just functionalize everything make it easy to copy and paste when you need to rework the system eventually.
Stage - It is basically an interface for the round itself. It also contains the map and the gamemode.
Gamemode - Contains the rules of the game as well as checks, rewards, etc.
Map - Sets up the map, clones the parts from a backup, fixes up all the RBXScriptSignals.
Now, daisy-chaining them would be kind of difficult, which is why I suggest you don’t do that. As dthecoolest (forgive me if I’m misinterpreting this) is suggesting, instead of having the gamemode → map → stage, you could have the stage contain an instance of both.
Here, CaptureTheFlagGamemode extends the Gamemode class, and it has a createMap function, which returns an instance of CaptureTheFlagMap. Once the round starts, the stage would probably call an init function on the map, and the gamemode which can the do the setting up of the timer, rules, and the map itself.
But this is just an example, I am very bad at explaining, so sorry if I didn’t exactly get what you mean, or if I didn’t explain well, but this is just one way it can be done.
An alternative is you can handle all of it in one Gamemode object, but you can carry the same principle of storing an instance of it rather than extending it.