Skip to content

Discussion

Now that all the classes have been created, let's look at a few important key points in the classes and subclasses.

Getters/Setters

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 getMana and 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 refillMana:

...

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 maxMana (using math.min). It's better to do this in a seperate method so we don't pollute the setting of the raw mana property.

The 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 refillMana method:

...

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.

Subclass Notes

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:

<Class>.super.constructor(self[, args])

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

...