Troll Warlord's Fervor
a stack neverflow
EDIT: Some links have been updated due to github repository change.
There's not much to discuss for Fervor's base .txt and main lua. As a passive, Fervor does not require targetting filter and other important values, so the txt file is pretty much similar to Spell Shield. There is one difference, however, that it states "SpellDispellable" as no. This is optional, since it only affects the tooltip.
The ability file is also as simple as Spell Shield. A copy-paste, in fact, since the ability is not required to do anything other than spawning the modifier.
The actual actions happened at modifier instead, so let's focus on that one.
This modifier should appear at buff bar, hence we need to show it. Since it is not on the background anymore, its characteristics should be specified, such as:
- Is it hidden?
- Is it a debuff? (Otherwise is a buff)
- Is it can be purged?
- Is it removed on death?
So far, Spell Shield uses default values, but now they will be specified clearly. On the top, let's write:
Those functions are pretty self-explanatory, I think. You can always omit these functions, and the answers should fall back to its default value.
As always, let's start with gathering information. The obvious would be the attack speed bonus per stack, and stack limit.
Another function to call at the start of modifier's creation is to set its stack count.
A stack in a modifier does not mean anything; not even the number of that modifier exist on the parent. It simply an integer storage. If not specified, it has default value of 0, and is not displayed.
Since it is a storage, the value may be set, retrieved and modified as needed. Once set, the modifier icon will display its value (if it is shown at all).
Let's set its stack.
When leveling up, Troll should not lose his stack, so just leave it as is on refresh.
Modifier: Dynamic Property
Like Spell Shield, Fervor will modify a property, which is attack speed. The twist is that the attack speed depends on the number of stacks.
A simple algebra would be "stack_count" times "stack multiplier".
Another twist for the ability is that when Troll's passive is disabled (break), the modifier should not give bonus. To check whether the parent has break applied, call PassivesDisabled().
No bonus means 0, what's easier than setting return to zero than multiplying it with zero?
Now, we declare its function, then implements the function as described:
Modifier: Event Listener
This is a new concept, but I think you know what it means. The modifier listens to an event, and should respond accordingly.
In the same manner as modifying property, we declare which event should it listen.
Fervor needs to track the attack's target, and should change its stack when Troll has just finished his attack point. Therefore, we'll use this one:
See that "params" over there? It is there for a reason. The "params" table gives information about what happened during the event.
You can use Lua's print() to know all possible key-values it holds (see P.S.), but mostly you'll want:
- params.attacker: returns unit table of the attacker.
- params.target: returns unit table of the target.
- params.damage: attack damage value that the attacker threw.
Valve made a twist that each modifier is run separately between client and server. Generally, game logic should be done on server, and visual effects should be done on client. What we're gonna do is game logic, so we'll use IsServer() to keep any game logic done in server.
When a listener is declared, the modifier will listen to anything. This means that for any unit in-game which has finished their attack, this function will be called. We don't want that. We only want the modifier to listen only when its parent is attacking.
In order to specify it, a filter is needed.
A standard way is to check whether the attacker is this modifier's parent:
Now the events have been filtered, it's now about the action.
Logic: Custom Variables and Functions
Storing Custom Variables
Fervor's logic wants to compare if the currently attacked target is the same as previous target.
How do we know? We don't know. But if you store this current target into a variable, next time this function is called, we know the previous target.
Storing as "self.var_name" means that the variable will be available as long as the modifier is alive.
Our code now should be:
An important thing though, the very first time this function is called, "self.currentTarget" is a nil value, and Lua may yell at you if you try to compare with a nil. A good way is to initialize it when created:
Creating Custom Functions
The piece of code above lacks something: increasing the stack. But it may get crowded if we just fill all logic there. A separate custom function may help, and here's how to define it:
function <modifier_name>:functionName( some, parameters, here, if, any ) end
In my case, I want a separate (helper) function for increasing the stack, and another function to reset.
The code changed into:
Try to understand by yourself why the helper function is written that way, ok?
Finally, here's the complete code.
I hope that this part will give you insights about variables, event listener, and defining a response when an event occured.
Next time, we'll get into more action, using Ogre Magi's Fireblast; a unit-targeted ability. Stay tuned!
Questions, Comments, and Critics are always welcomed.
P.S. forp print()
If you're not sure what a table (such as "params" above) contains, you can use this piece of code as the logic:
See what's printed on console.