Page 1 of 1

stack on_act and similar function fields, code inside

Posted: Sat Apr 06, 2013 11:40 pm
by nate
actor.on_act() and other fields are great ideas. Syntax is simple, easy to use. Unlike hooks, can unattach. Specific to the actors you want to affect. Has the potential to keep actor.act from turning into an endless wasteland of a function, at the same time that it allows authors to put the effect of an object or talent in the same file as the object or talent. So why is it used only a handful of times in the code? Probably because it is a dangerous trap: very difficult to stack assignments to on_act, maybe impossible to do it safely. If you write to on_act, then another part of the code writes to it, you've lost your on_act-- and you're probably going to write a nil to on_act sometime in the future, thinking you need to clean up after yourself, but just erasing whatever other on_act overwrote your own.

Thankfully, there is an easy answer, and if it was standardized, this problem would disappear (and as this spread to other on_Xs, you might even reclaim some existing endless wasteland functions). Implement a stack of on_act functions.

Code: Select all

_M.on_act = function(self)
     if not self.stack or not self.stack.on_act then return end
     for key, fct in pairs(self.stack.on_act) do fct(self) end         --not ipairs; we don't want to skip holes
end

_M.pushOnStack = function(self, stackName, func)    --call with example self:pushStack("on_act", myFct)
     self.stack = self.stack or {}; self.stack[stackName] = self.stack[stackName] or {}
     local pos = 0
     for key, value in ipairs(self.stack[stackName]) do pos = key end    --we want the first nil space
     pos = pos+1
     self.stack[stackName][pos] = func
     return pos
end

_M.popOnStack = function(self, stackName, pos)
     if not self.stack or not self.stack[stackName] or not self.stack[stackName][pos] then return err, "No stack or no entry at that position" end
     self.stack[stackName][pos] = nil          --we've got a table full of holes, it's the price of consistent IDs for removal
     return true
end
That's untested code, but it's small and, I believe, easily understood.

These three simple functions should solve all of the problems with on_act and with any similar fields. There are a lot of possible permutations depending on how much work you wanted to put into it-- you could make call, push, pop all a single function, with the entire stack maintained inside of the function. You could allow functions to be assigned to on_act with keys, in case the author was willing to accept the risk of error in the case of duplicate keys in exchange for code clarity.

Currently, on_act is used by very few talents (probably for the reasons already described). The earlier this change is made, the less painful it is to change. Fields like on_hit would benefit from the same change, but their prevalence makes it much more painful to make a stack for on_hit (you could monitor for changes to on_hit and update stack based on that, but that's a temporary solution, not very elegant). You'll notice that I specifically wrote these prototype pushOnStack and popOnStack functions to work with any number of different on_X fields.