I have figured out how to pass functions around in Lua, and I am using the technique of inserting functions into tables in order to dynamically assign functions to the cards in a trading card game. The functions are assigned when the Card is first created, but more could easily be added later. I am still new to Lua so I would love to hear about any syntax errors I am making, or if the way I am doing anything might be dangerous in the future or violates Lua best practices.
The basic idea is that each Card has a unique ID, and each ID has a table of strings associated with it. The Card first gets this table of strings from the CardFunctionsList script, then passes them to the FunctionsList script which returns a table of the functions themselves. Then the card can run any function that has been loaded in this way, and could pass the function along to another card if need be. When called, the card passes itself into the function, and the function modifies it.
There are some problems with this approach. I do not like having to use tables full of strings in CardFunctionsList.lua
, but I do not see a better way to do it. I originally just used numbers, but the readability was worse compared to this version. But if there are any typos the functions will not be loaded. Also, I think it might be better to have just one script instead of two to return the functions table, but it seems cleaner to have it separated this way. I am not sure about this. Finally, I am struggling with the names CardFunctionsList
and FunctionsList
, but I can't think of anything better that makes sense.
Card.lua
local Card = {}
function Card:new(cardSuit, cardType, cardId)
local instance = setmetatable({}, { __index = self })
instance.suit = cardSuit
instance.id = cardId
instance.type = cardType
--these will be assigned by a script that is not yet complete
instance.health = 0
instance.strength = 0
--these are the functions of the card
instance.abilityList = {}
instance:loadAbilityList(cardId)
--this will contain things like "Taunt" or "Charge"
instance.statusList = {}
--diagnostic method for the prototype
instance:doTestAbilitiesAndPrint()
return instance
end
function Card:loadAbilityList(cardId)
local cardFunctionsList = require "CardFunctionsList"
local functionsList = require "FunctionsList"
local functionNames = cardFunctionsList:functionNamesForId(cardId)
self.abilityList = functionsList:new(functionNames)
end
--if the card does not have the ability, nothing will happen
function Card:doAbility(abilityName)
for key,functionTable in ipairs(self.abilityList) do
for name,func in pairs(functionTable) do
if name == abilityName then
amountToIncrease = 1
func(self,amountToIncrease) --self because the card passes itself into the function
end
end
end
end
function Card:doTestAbilitiesAndPrint()
self:doAbility("addTaunt")
self:doAbility("addCharge")
for key,value in ipairs(self.statusList) do
print(value)
end
end
return Card
CardFunctionsList.lua
local CardFunctionsList = {}
--For each card ID, the names must be manually entered as a table
--they must match values in the FunctionsList script
function CardFunctionsList:functionNamesForId(id)
if id == 1 then return {"increaseHealth"}
elseif id == 2 then return {"increaseStrength"}
elseif id == 3 then return {"addTaunt"}
elseif id == 4 then return {"increaseHealth", "addTaunt"}
elseif id == 5 then return {"increaseStrength", "addTaunt", "addCharge"}
elseif id == 6 then return {"increaseHealth", "increaseStrength", "addTaunt", "addCharge"}
elseif id == 7 then return {"increaseHealth", "increaseStrength"}
else return {} --if the id does not match anything, nothing bad will happen
end
end
return CardFunctionsList
FunctionsList.lua
local FunctionsList = {}
--The inputted functionNames are a table created by the CardFunctionsList
function FunctionsList:new(functionNames)
local instance = setmetatable({}, { __index = self })
instance.functions = {}
instance:loadFunctionsToReturn(functionNames)
return instance.functions
end
function FunctionsList:loadFunctionsToReturn(functionNames)
for key,name in ipairs(functionNames) do
table.insert(self.functions, self:functionForName(name))
end
end
function FunctionsList:functionForName(name)
--even though the table key looks like plain text, it will accept a string to access it
if name == "increaseHealth" then return {increaseHealth = increaseHealth}
elseif name == "increaseStrength" then return {increaseStrength = increaseStrength}
elseif name == "addTaunt" then return {addTaunt = addTaunt}
elseif name == "addCharge" then return {addCharge = addCharge}
end
end
--Here is the list of all functions
function increaseHealth(card, amount)
card.health = card.health + amount
end
function increaseStrength(card, amount)
card.strength = card.strength + amount
end
function addTaunt(card)
table.insert(card.statusList, "Taunt")
end
function addCharge(card)
table.insert(card.statusList, "Charge")
end
return FunctionsList
From here in order to add new gameplay to the game, a card ID entry must be created in CardFunctionsList.lua
that returns a table of strings. Then entries must be made for those strings in the else-if in FunctionsList.lua
, and the actual functions added to that script.
One goal is for a person with possibly very little coding knowledge to be able to create new cards for the game by editing the scripts directly, so that is something that should be considered as far as the structure and readability of the code.
FunctionsList.lua
. \$\endgroup\$FunctionsList.lua
, do you mean it should belocal card.strength = card.strength + amount
? \$\endgroup\$function increaseHealth(card, amount) ...
and the other ones. You could change them (or a mod could, since you want to support that), and then you would start adding the modified (or replaced) function instead of your original one. I wouldn't make them local, though -- you could put them into a table, and keep that table in FunctionsList. Here's an example of a table switch (which will replace an elseif chain that checks the same value) lua-users.org/wiki/SwitchStatement \$\endgroup\$