I am trying to rescript a spellcasting type system that was made by another scripter that I am working with that has three different elements. Each element has its own set of moves each with different stamina and cooldown values. The current code uses a LocalScript and a ServerScript with all the moves in a giant elseif statement, but I would like to streamline it and make it more readable.
I heard using OOP is an efficient way, and I have read up on how to use OOP, see here:
But I am not sure how I could make this apply to my use case, if anyone has any experience using OOP or has a more efficient way of creating a system like this, please provide me information on how I would go about setting this up.
Here is what I have so far:
local Move = {}
Move.__index = Move
-- Creates framework for all future moves
function Move.new(Player, Element, Cooldown, Stamina)
local newmove = {}
setmetatable(newmove, Move)
newmove.Player = Player
newmove.Element = Element
newmove.Cooldown = Cooldown
newmove.Stamina = Stamina
return newmove
end
For a spellcasting system, efficiency is not much of a concern. Generally speaking you only need to do anything when they actually activate a spell, so the most intensive part will typically be the actual animating of the spell.
In your specific case it might be beneficial to also keep a field in Move specifying when the spell was last used in order to ensure that they can’t re-use it until now - then > Cooldown.
To continue, you might add a new method to the Move class called DoMove or something that ensures you can use the spell, update the “last used” field, and does whatever the actual spell needs to do (perhaps in a new thread if you don’t want it to be blocking). For example, you might have the Move constructor accept a new parameter that is the actual function to call that deals with the animating of the spell and everything else and DoMove would call that:
function Move:DoMove()
local now = tick()
if now - self.LastUsed > self.Cooldown then
self.LastUsed = now
coroutine.wrap(self.HandleMove)(self) -- HandleMove initialized via the constructor
-- if you want it to be blocking, just do self:HandleMove() instead
end
end
So all you have to do when they want to “cast a spell” would just be to call DoMove.
First tip would be that you primarily design a game to be well organised and easily expanded upon, anything beyond some basic common-sense efficiency measures are generally applied as needed.
If I were doing this I’d probably have a superclass for all spells which just sets up the absolute basics, and then you’d have multiple classes of spells that inherit from from that super class. You’d then also have a module somewhere that defined what each spell is, so I have something like this:
function CastSpell(Player, Definition)
local NewSpell = require(Definition.Class).new(Player, Definition)
--etc.
end
CastSpell(Player, SpellDefinitions.FireBall)
So the spell class can then refer to all the information about itself by looking at that definition, instead of passing tons of parameters for damage, cooldown, cost, range, etc.
This isn’t the only way of doing things, but it’s worked well for me.
Thank you for your input, I had an idea where I wanted to go, but I didn’t exactly know how I wanted to get there, I will definitely keep this in mind.