Now that all the classes have been created, let's look at a few important key points in the classes and subclasses.
You should notice that for every property there is at least a get<Prop> method and some properties also have a set<Prop> method.
This is a good practice as it allows one to add additional needs in the future when manipulating a property. You should never directly set a property, but always use its "getter" or "setter" for better control.
For example, in our Mage class:
... function Mage:constructor(name, isType, health, maxHealth, weapon, mana, maxMana) Mage.super.constructor(self, name, isType, health, maxHealth, weapon) self.mana = mana self.maxMana = maxMana end function Mage:setMana(amount) self.mana = amount end function Mage:getMana() return self.mana end ...
We have a
mana property, but we would always use the
setMana methods to manipulate that property. You would never want to to do
self.mana = val or
mana = self.mana in any of your methods.
To refill our mana we use a seperate method called
... function Mage:refillMana(amount) amount = amount or self:getMaxMana() amount = self:getMana() + amount self:setMana(math.min(amount, self:getMaxMana())) end ...
You should notice that we never call the property directly, but use the "getter" and "setter" methods instead.
In the example code above we "clamp" the
mana value to the
math.min). It's better to do this in a seperate method so we don't pollute the setting of the raw
setMana method itself will take any value:
... function Mage:setMana(amount) self.mana = amount end ...
This allows us to override the
maxMana if we want to with a "buff" or potion. For example, let's assume we have some "buff" functionality built into our Mage class that adds additional bonus mana when active, we can then easily adjust our
... function Mage:refillMana(amount) amount = amount or self:getMaxMana() amount = self:getMana() + amount --do we have a buff? if self:hasActiveBuff() then amount = amount + self:getBuffValue() self:setMana(amount) --no clamping here else self:setMana(math.min(amount, self:getMaxMana())) end end ...
You could put the
refillMana functionality directly in the
setMana method, but it is better to abstract the functionality out, keeping the property "getter" and "setter" clean.
All subclass are created using the
extends method on the class or subclass we want to extend. We do not use
Classy.create, but we need to pull in the base class using
require so that we can subclass it.
... --The base class (or subclass) local Character = require("classes.Character") --Create a subclass by "extending" local Warrior = Character:extends("Warrior") ...
When creating a subclass, we need to make sure to pass up the required base class arguments by using the
constructor function. This done using the super property:
... function Warrior:constructor(name, isType, health, maxHealth, weapon) --Pass the arguments up the hierarchy using "super" Warrior.super.constructor(self, name, isType, health, maxHealth, weapon) end ...
Pay attention to the signature of the
super.constructor function. You need to make sure you pass a reference of
self to it, along with any additional arguments, and use only dot (.) syntax:
This is very important to remember, or your subclasses will not work as expected..
In the case of subclasses that have their own specific properties, we only need to pass the ones the base class needs, and we then store the others on the subclass itself:
... function Mage:constructor(name, isType, health, maxHealth, weapon, mana, maxMana) --Pass the base class arguments up the hierarchy using "super" Mage.super.constructor(self, name, isType, health, maxHealth, weapon) --Store properties specific to this subclass self.mana = mana self.maxMana = maxMana end ...