-
Notifications
You must be signed in to change notification settings - Fork 4
How to update the code
In this page, you will find useful information about how a contributor can find where to update the code for his specific class, depending on what he wants to do.
Classes files are in the hrgenerator/classes
folder. Let's say I want to update the spells in the paladin.py
file, to add Wake of Ashes to the Retribution Paladin.
In each file, there is a Python dict
called SPELL_INFO
containing the data for all the spells registered for the class. This dict has the following structure:
SPELL_INFO {
<class_name>: {
COMMON: {<spells common to all the specs>},
<spec_1>: {<spells for spec_1>},
<spec_2>: {<spells for spec_2>},
},
}
Therefore, I just have to find the RETRIBUTION
key and add the Wake of Ashes spell to it. A spell entry is formatted as follows:
RETRIBUTION: {
'<spell_name>': {SPELL: <spell_id>,
<additional facultative properties>},
}
Here, spell_name
should be exactly the name as it appears in simc APLs, and spell_id
should simply be the ID of the spell in-game. A spell can have multiple attributes:
-
SPELL
[int]: the ID of the castable spell. -
BUFF
[int]: the ID of the buff the spell applies on the player. Some passives may have aBUFF
line without aSPELL
line. -
DEBUFF
[int]: the ID of the debuff the spell applies on the target. Some passives may have aDEBUFF
line without aSPELL
line. -
RANGE
[int]: the range, in yards, of the spell. Default is melee range, if it is not specified. -
USABLE
[bool]: callsIsUsableP
instead ofIsCastableP
in lua, in case this specific spell has to run additional checks on top of the default. -
INTERRUPT
[bool]: if the spell is an interrupt; this adds additional conditions, to check whether the player wants interrupts to be displayed or not. -
CD
[bool]: if the spell should be considered as a cooldown; this checks if the player wants cooldowns to be predicted by the addon. -
OGCDAOGCD
[bool]: flags the spell as an OffGCD as OffGCD, which will be displayed as a suggested spell instead of the main spell. -
GCDAOGCD
[bool]: flags the spell as a GCD as OffGCD, which will be displayed as a suggested spell instead of the main spell. -
MELEE
[bool]: a very specific tag, used in Demon Hunter APL to check whether the unit is in melee range by taking his dash into consideration.
It is of course possible to add custom tags, if they are used by the class otherwise.
Custom lua functions are defined in the hrgenerator/luafunctions
folder. Each file contains a single function, which is written in lua.
To add a new function, for example the function IsInMeleeRange
to the Demon Hunter file, I first create the file IsInMeleeRange.lua
in hrgenerator/luafunctions
, containing the code for the function. Then, in the demonhunter.py
file in the hrgenerator/classes
file, there is a Python dict
called CLASS_FUNCTIONS
in which I can define the custom lua functions to import when using a specific spec. CLASS_FUNCTIONS
has the following structure:
CLASS_FUNCTIONS = {
<class_name>: {
COMMON: [<common function names>],
<spec_1>: [<function names for spec_1>],
<spec_2>: [<function names for spec_2>],
},
}
The lists of function names should contain the string names of the files in hrgenerator/luafunctions
containing the functions to add, without the .lua
extension. Therefore, in my case, I will add 'IsInMeleeRange'
to the HAVOC
key.
Although possible, this part requires more work. To be able to properly customize the behavior of the generator, I first have to identify where in the parser the behavior should be updated. In most cases, what we want is call a custom function when the parser reaches a specific expression, to run additional checks instead of the default behavior. For example, let's say we want, as a Warlock, to call the custom function ActiveUAs
when reading the expression buff.active_uas.stack
.
The first thing I have to identify is where in the code this expression is handled. In this case, it's pretty easy. In the hrgenerator/objects/expressions.py
file, the class Buff
handles everything that starts with buff.
. This class has a method stack
, which parses expressions of the form buff.{spell}.stack
. Therefore, this is this method that we have to updated.
To update it, we need to write a Python decorator, i.e. a second-order function that takes the stack
method and returns a modified version of this stack
method, with the additional behavior. In our case, our decorator will look as follows:
def affliction_active_uas_stack(fun):
from ..objects.lua import Method
def stack(self):
"""
Return the arguments for the expression buff.active_uas.stack.
"""
if (self.condition.parent_action.player.spec.simc == AFFLICTION
and self.condition.condition_list[1] == 'active_uas'):
self.object_ = None
self.method = Method('ActiveUAs')
self.args = []
else:
fun(self)
return stack
Our decorator, affliction_active_uas_stack
, takes the original stack
method under the fun
parameter, and defines a new stack
method. In this method, it first tests whether the current spec is AFFLICTION:
-
self
is the currentBuff
expression object, -
self.condition
is the current condition (thebuff.{something}.stack
part), of classConditionExpression
, -
self.condition.parent_action
is the APL line itself of classAction
, -
self.condition.parent_action.player
is a reference to the current player, of classPlayer
, -
self.condition.parent_action.player.spec
is the spec of the current player, of classPlayerSpec
, -
self.condition.parent_action.player.spec.simc
is the simc string of the spec, from the linespec=affliction
.
It also checks if the second element of the condition is the string active_uas
: self.condition.condition_list
is the list of strings of the current condition, after splitting on dots. Therefore, if parsing buff.active_uas.stack
, self.condition.condition_list
will contain ["buff", "active_uas", "stack"]
.
An expression contains three important attributes: object_
, method
and args
, each of a lua parsable class as defined in the hrgenerator/objects/lua.py
or hrgenerator/objects/executions.py
files (depending on the context). In any case, the expression will be parsed as
-
<object_>:<method>(<args>)
ifobject_
is notNone
, -
<method>(<args>)
ifobject_
isNone
.
Therefore, as in our case we want to parse buff.active_uas.stack
as ActiveUAs()
, we want to define self.object_
as None
, self.method
as Method("ActiveUAs")
and self.args
as []
(i.e. no argument).
Finally, once the decorator is properly defined, all we have to do is tell the parser to apply it to the stack
method of the Buff
class: for that, we simply have to add
{
'class_name': 'Buff',
'method': 'stack',
'decorator': affliction_active_uas_stack,
},
in the DECORATORS
dict
under the WARLOCK
key.