Introduction to Havok Behaviors

Havok behaviors are used in Dark Souls III, Bloodborne and Sekiro to control all character animations. The behavior system consists of two parts: hkx behavior files which store the character's behavior info and hks Havok script files which trigger the behaviors in the character's behavior files. I will be focusing on Dark Souls III behaviors, however Bloodborne uses the same version of Havok, and thus will likely be nearly, if not completely, identical in this respect. Unfortunately, there is currently no support for Sekiro behaviors.


Getting Started

For simple edits such as registering animations or creating new CustomManualSelectorGenerators for regular attacks or weapon art attacks you can use DS3BehaviorTool. Its ability to register CustomManualSelectorGenerators can also be used as a starting point for more advanced edits.

To directly edit behavior hkx files as outlined here, get hkxpack-souls from the souls modding discord server (the link will not work unless you have joined it but you should be there anyway) and unpack the behavior hkx found in your character's behbnd in the Characters folder. This will produce an XML file which you can edit in your editor of choice. I highly suggest using an editor with syntax highlighting and syntax based code folding such as Notepad++. You will also need this decompiled c0000.hks for the player and DSLuaDecompiler to decompile any other hks files you might need. Additionally this decompilation of common_define.hks is a useful reference, however using it directly supposedly breaks blocking with greatshields. Anything from common_define can be implemented in c0000.hks, the same goes for enemies which have common hks and behavior files called c9997.


Behavior Hkx Files

Behavior hkx files store the behaviors of a character and determine how they are blended, how they transition from one to the next, whether they are looped or played a single time. Animations can be cropped, mirrored, even slowed down and sped up. All parameters in a character's behavior file can be adjusted dynamically using hkbVariables and Havok script which we will look at in the section Creating hkbVariables and Setting Them in Hks.


Structure

All hkx packfiles consist of an hksection called "__data__", within which there are hkobjects consisting of hkparams. When unpacked to XML form using hkxpack-souls all named hkobject are listed within the "__data__" hksection XML element. It is important to note that the actual behavior hierarchy does not correspond to the XML hierarchy but is instead reference based.

Hkobject names always begin with "#" and are always sequential, meaning there can be no gaps between IDs. Fortunately hkxpack handles this automatically and will rename hkobjects, including all references to said hkobjects, if it finds a gap in name IDs. Nevertheless it is advisable to follow this naming scheme and keep the IDs sequential. Duplicate IDs must likewise be avoided.

All behavior packfiles start with an hkRootLevelContainer with ID #90 which references an hkbBehaviorGraph with ID #91. From there the root level generator, which in Dark Souls III is always an hkbStateMachine called "Root", and the hkbBehaviorGraphData is referenced.

The behavior hierarchy proper starts with this root state machine. At the top level, various modifiers are applied which affect all behaviors, it is only at the state machine called "Master_SM" (#256 for the player character) where the hierarchy diverges. All the main state machines are registered under the master state machine. They serve to group animations so that blends and other modifiers can be applied categorically. For instance AttackLeft contains all left hand attacks, to which a blend layer is applied to blend the right arm depending on the right hand weapon hold position (see Layer Blends). State machines also map event names to states.

Eventually every branch of the behavior hierarchy reaches a CustomManualSelectorGenerator which I will refer to as CMSG for the sake of brevity. CMSGs are Fromsoftware's custom havok generator class which define the animation IDs used in TAE. It is here where animations are registered for each TAE section. If you want to add a new TAE section or add new animation IDs to an existing TAE section, you must register each animation at the corresponding CMSG in the form of an hkbClipGenerator hkobject (see Registering an Existing Animation ID for a TAE Section). CMSGs function similarly to hkbManualSelectorGenerators, the base Havok equivalent, however instead of selecting generators using an hkbVariable they are hardcoded to select hkbClipGenerators according to the player's TAE section. In addition to this, they include hkbScriptGenerator functionality and automatically generate scripts named "[state name]_on[script type]". They generate all script types supported by hkbScriptGenerators.

The hkbBehaviorGraphData and hkbBehaviorGraphStringData hkobjects, referenced at the beginning and located at the end of the XML file, store the event names, which are used to trigger animations from hks, and the info for all hkbVariables. For enemies it also contains character property info, which is taken directly from various params and can be used, just like hkbVariables, in hkbVariableBindingSets to set various properties of behavior objects (see hkbVariableBindingSets). Unlike hkbVariables however, these are constant. Their main use is in the common enemy behavior file c9997.


Naming Conventions

The name of each state is defined without any prefix or suffix in its hkbStateMachineStateInfo. The descendant objects of a state info object use various suffixes to denote their function.

See the reference page for the suffixes used for each class.

General Types

The following are the most common types of classes of hkobjects found in behavior files. Generally hkobjects of the same type can all be referenced from the same hkparams. A detailed list of which hkobject classes are referenced from which params can be found on the behavior reference page.


Generators

Generators are the most common type of behavior object. As mentioned previously, CustomManualSelectorGenerators define all animation IDs and hkbClipGenerators register individual TAE sections to use these animation IDs. Other common generator types include hkbManualSelectorGenerators (hkbMSGs), which are most commonly used to select between generators using bound hkbVariables (see Creating Weapon Art Behaviors for a detailed example and use case), hkbScriptGenerators, which are used to attach hks functions such as update, activate or deactivate functions to all child states and hkbBlenderGenerators and hkbLayerGenerators for animation blending (see Advanced Behavior Systems).

hkbStateMachines are also generators which are most commonly used to group states into categories, such as attack animations, movement animations, half-blend animations, etc. and to map event names to child states, thus allowing them to be triggered from hks.


Modifiers

Modifiers are mostly used at root level for character head rotation, in the form of CustomLookAtTwistModifiers, and IK. Also present are hkbTwistModifiers which twist bones based on hkbVariables.

Modifiers are applied to generators using an hkbModifierGenerator which specifies a child generator and a modifier. Often this modifier is an hkbModifierList which allows one to apply multiple modifiers at once, thus avoiding having to chain hkbModifierGenerators.

Modifiers will not be covered here, however they are present on the behavior reference page.


Transitions

Transition effects specify how animations transition from one to the next. Among other things it also determines whether animations are blended when transitioning into themselves.

The most commonly used transition effect objects are hkbBlendingTransitionEffects and CustomTransitionEffects, with the default transition effect used for the player being CustomTransitionEffect #292. Transition effects are applied in hkbTransitionInfoArrays which map event names to states in a state machine and can also be overwritten at the CMSG level by the generatorChangedTransitionEffect hkparam, which can be useful in cases where multiple CMSGs are triggered using the same event name as outlined in the section Using hkbMSGs to Select from Multiple CMSGs.


Classes and HkParams

See this page for an annotated list of all Havok behavior classes and their hkparams. Also included is a list of used enums and a list of valid reference classes per hkparam.


Havok Script Files

Havok script files are lua scripts which controls which behaviors are triggered when. It is also where hkbVariables are set to modify behaviors.

For the player c0000.hks handles which inputs trigger which animations. Enemies mainly use c9997.hks with their own hks typically containing very little. As c9997.hks has not been accurately decompiled, its contents are still mostly unclear, however it is definitely used to interface between AI animation calls and the behavior system. Here is an experimental decomp of c9997.hks which might be useful as a reference, please note that it does not work in game.

I will mainly be focusing on behavior edits here since they are less well documented, this will involve hks work, however hks-only edits will not be covered or will only covered in passing. Dedicated hks tutorials which focus on such edits can be found here and here.


CommonFunctions

CommonFunctions are the functions used in onUpdate functions to interpret player inputs. Generally all substates of the "Master" state machine will have their own CommonFunction, including Idle, which is CMSG. They will call various Exec functions, either using constant arguments or passing on the arguments they receive from onUpdate, such as state specific attack eventNames.


Exec

Exec functions check for player request inputs and execute animations by calling the lower level ExecEvent functions "ExecEventAllBody" or "ExecEventHalfBlend", which in turn all call the ExecEvent function which uses the base Havok function hkbFireEvent to execute events based on eventName.

Env

The env function gets info about the character from the game, it has the following syntax:

env(ID, args)

and returns varying types depending on the env. For instance env(1116, speffectID) checks whether the player has the specified speffect and returns a bool.

Most envs called in the player hks are referred to by numbers, however some are named in japanese.


Act

The act function is the opposite of the env function. It sets character info in the game, for instance act(2002, speffectID) applies the specified speffect to the player.

The function has the following syntax:

act(ID, args)

As with the env function, most acts are referred to by numbers but some are named in japanese.


onUpdate

Each CMSG, given a unique userData value and provided the "enableScript" hkparam is enabled, automatically creates its own onUpdate function named [name of SMSI]_onUpdate(). This function is responsible for handling player inputs during the animation. Should an onUpdate function not be present in hks for a given CMSG, the player will be locked in the corresponding animation until it has ended and animation cancel jumptables in TAE will not work.

Most basic onUpdate functions have the following form:

function SMSIName_onUpdate()
    if SomeCommonFunction(args) == TRUE then
        return
    else
        return
    end
end

As an example the first one-handed r1 attack:

function AttackRightLight1_onUpdate()
    if AttackCommonFunction("W_AttackRightLight2", "W_AttackRightHeavy1SubStart", "W_AttackLeftLight1",
        "W_AttackLeftHeavy1", "W_AttackBothLight2", "W_AttackBothHeavy1Start", FALSE, TRUE) == TRUE then
        return
    else
        return
    end
end

AttackCommonFunction/SwordArtsCommonFunction are used to execute the specified attack events given the appropriate button input. For instance, the AttackRightLight1_onUpdate() function will trigger a transition to event "W_AttackRightLight2", which is the second one-handed r1 attack, if the player presses r1 while it is active. OnUpdate functions are active for the duration of the corresponding animation.

As mentioned previously, update functions can also be applied at any point in the behavior hierarchy to all child states using an hkbScriptGenerator.


Basic Editing

The following section will cover the basics of behavior editing which should serve to establish a rudimentary understanding of the process, which due to the modular nature of the behavior system, should convey the necessary information to allow you to create any behavior structure you desire. See the reference page for a full list of classes and what they do, and always refer to vanilla examples to see how they are used.

Each example will build on what is discussed in the previous ones so I recommend looking at them in order if you do not yet possess a solid understanding of the behavior system. I will also cover a lot of general info, including tips and good practices, in the first few examples.

All examples here except for adding magic casting animations are supported by my tool DS3BehaviorTool so for anything other than educational purposes I suggest using that.


Registering an Existing Animation ID for a TAE Section

To start, let's go over the most basic operation in behavior editing which you will need to do for every new instance of an animation ID you add to TAE. There have already been many quick tutorials on how to do this, so this tutorial will go more in depth to explain fundamental practices of editing behaviors to build a solid foundation for more advanced editing.

First, unpack your hkx behavior file using hkxpack-souls, which is located in your character's behbnd in the Characters folder, and open the resulting XML file.

In this example we will register the first one-handed r1 animation for TAE section 299. Replace animation ID 30000 and TAE 299 with your animation ID and TAE section of choice.

Once we have opened the behavior file we will want to find the CustomManualSelectorGenerator, or CMSG for short, of the animation we want to register. The animation ID of a CMSG is stored in its hkparam called "animId", in the XML it looks like this:

<hkparam name="animId">30000</hkparam>

Thus an effective method for finding the CMSG you want is searching for:

"animId">[Id of your animation]

Once we have located our CMSG of choice, in this case #528, we need to add an hkbClipGenerator to its "generators" hkparam, which, as can be seen by the fact that all numbers are prefixed with a "#", is a list of references.

To add any hkobject to your behavior file you need to do the following:

  1. copy/paste an existing instance of your desired class
  2. edit the parameters of your new hkobject
  3. find the next available name ID and set your hkobject's name attribute
  4. reference your new hkobject somewhere

For step 1 we will copy an existing hkbClipGenerator from those registered under the CMSG to which we wish to register our new animation. This is a safe way to avoid incorrectly set hkparams in our new object, as generally all instances of the same animation ID will have hkbClipGenerators which are set up similarly.

Once we have that, we will scroll to the bottom of the behavior file and paste our new hkobject after the ending tag of the previous hkobject and before the ending tag of the hksection.

Now we will edit the parameters of our newly created hkobject. Since we copied it from an instance which was registered under the same CMSG, we will most likely only need to edit the hkparams "name" and "animationName". You can refer to the reference page for documentation on what the other parameters do as well as their default/typical values.

It is unclear what the "name" hkparam does for hkbClipGenerators, however it is generally of the form a[TAE section]_[animation ID]_hkx_AutoSet_[anibnd number?]. The hkx_AutoSet_XX part does affect the behavior in any way so I tend to just call mine a[TAE section]_[animation ID].hkx since I do not know what the AutoSet number represents. In this case we will use a299_030000.hkx.
The hkparam which does matter, and which is responsible for registering your animation is "animationName". It is of the form a[TAE section]_[animation ID], so in this case a299_030000.

For step 3 we will need to look for the next available name ID. Hkobjects in the data hksection will always have a name attribute which is an ID prefixed with "#". They will always be numbered in the order they appear in the XML file, so to find the next available name ID you will want to look at the last hkobject in the file and add 1 to its ID. In vanilla the behavior file ends with the hkbBehaviorGraphStringData hkobject with name "#12801", thus the first object you add to your behavior file will have the ID "#12802".

As stated previously, hkxpack will make sure your IDs are sequential and rename hkobjects and references to them accordingly, however I recommended following the naming scheme regardless and not leaving any gaps. Duplicate name IDs will break your behaviors so make sure the name ID you pick is unused.

In this example the next free name ID is #12802, so we will change the name attribute of our new hkbClipGenerator to equal that.

Here is the final version of our new hkbClipGenerator:

<hkobject class="hkbClipGenerator" name="#12802" signature="0xd4cc9f6">
    <hkparam name="variableBindingSet">null</hkparam>
    <hkparam name="userData">0</hkparam>
    <hkparam name="name">a299_030000.hkx</hkparam>
    <hkparam name="animationBundleName"/>
    <hkparam name="animationName">a299_030000</hkparam>
    <hkparam name="triggers">null</hkparam>
    <hkparam name="userPartitionMask">0</hkparam>
    <hkparam name="cropStartAmountLocalTime">0.0</hkparam>
    <hkparam name="cropEndAmountLocalTime">0.0</hkparam>
    <hkparam name="startTime">0.0</hkparam>
    <hkparam name="playbackSpeed">1.0</hkparam>
    <hkparam name="enforcedDuration">0.0</hkparam>
    <hkparam name="userControlledTimeFraction">0.0</hkparam>
    <hkparam name="animationBindingIndex">65535</hkparam>
    <hkparam name="mode">MODE_SINGLE_PLAY</hkparam>
    <hkparam name="flags">0</hkparam>
</hkobject>

Finally we need to reference our new hkbClipGenerator at the corresponding CMSG. To do this, we add its name ID to the end of the list of generators. Make sure to include the "#" prefix and to put your reference on a new line.

It is important to always add your new reference to the end of reference lists, because some reference lists are indexed by hkbVariables. For instance hkbManualSelectorGenerators use hkbVariables to select which generator to activate. An example of this in Dark Souls III are the selector generators for magic casting animations, where the index of the selected CMSG is set based on a value in the gameparam. If you were to insert a new generator higher in the list, it would offset all casting animations used in the game (see #7921 in c0000.hkx).

You will notice that the "generators" hkparam of the CMSG has an attribute called "numelements", this is present on all hkparams which contain a list of references or objects and keeps track of how many elements are within that list. Hkxpack will automatically adjust this when repacking, so you generally don't need to worry about doing so manually.

Once you have done all that, repack your behavior file using hkxpack-souls. Congratulations, you have successfully registered a new animation to be used in the game.


Adding an Animation ID

In the previous example we covered how each instance of an animation ID must be registered at the corresponding CMSG so that it can be triggered in game, by doing this we can have new TAE sections use existing animation IDs. For this example we will go one step higher in the hierarchy and take a look at the CMSGs themselves.

As we saw when adding hkbClipGenerators, CMSGs define the animation IDs which we can use in the game. So what if we wanted to add a new animation ID? In this example we will be adding a 4th right-handed light attack.


Creating a CMSG

In order to define a new animation ID we need to create a new CMSG. Like in our previous example, we will begin by copying an existing CMSG. We will want to copy a CMSG which is as similar in function to the one we wish to add as possible, in this case we will copy the CMSG for the third right-handed r1 as it immediately precedes our new animation. If you are unfamiliar with the naming scheme of attack animation generators, use the method outlined in the previous example to find this CMSG. The CMSG in question is named "AttackRightLight3_CMSG" (this will be important later) and has the ID #639. Copy paste this CMSG as described earlier and give it a new ID. We will also rename it to "AttackRightLight4_CMSG" following the existing naming scheme.

Now we will want to set up the parameters for our new CMSG. Provided you have copied a similar CMSG to the one which you want to create, you will need to change "userData", "generators" and "animId". For a full list of parameters and what they do see the reference page.

The generators hkparam is, as seen in the previous example, a reference list of all hkbClipGenerators with the corresponding animation ID. CMSGs must always have at least one child generator referenced here, so let's create a clipGenerator for our animation as described in Registering an Existing Animation ID for a TAE Section and reference it here.

The animId hkparam specifies our new animation ID, I will choose the anim ID 30040 for my animation, following the existing ID system.

The userData hkparam must be unique for each state, otherwise our onUpdate function will not work. As mentioned in Structure CMSGs are typically grouped by hkbStateMachines, this is reflected in their userData values. Every hkbStateMachine has a userData offset to all others, providing a large range of available userData values. Technically the userData you specify does not need to be in this range but I suggest taking the userData of the last generator in the parent hkbStateMachine and adding 1.

To find the parent hkbStateMachine where we wish to reference our CMSG, we search for the name ID of the original CMSG we copied (AttackRightLight3_CMSG) which is #639 and notice that it is referenced not in an hkbStateMachine, but in an hkbStateMachineStateInfo.

Child objects of hkbStateMachines are always of the class hkbStateMachineStateInfo and primarily serve to specify the state ID which is mapped to the event name of the state in the hkbStateMachineTransitionInfoArray. These state IDs are unique for all states of a state machine but are reused between state machines. Each hkbStateMachineStateInfo also references a child generator, in this case the CMSG AttackRightLight3.

When creating a CMSG the state info object is also important for determining the userData to use. Since the userData is only unique for each separate state, every CMSG which shares an hkbStateMachineStateInfo object must also share userData. This occurs most frequently with the state info object referencing an hkbManualSelectorGenerator which selects between multiple CMSGs. All of those CMSGs belong to the same state and share eventName, userData and onUpdate function. This is explained further in the examples Adding a Magic Casting Animation and Creating Weapon Art Behaviors.

To find the parent state machine, we once again search for the name ID of our object, this time hkbStateMachineStateInfo #638 and see that it is referenced in hkbStateMachine #285, called AttackRight_SM. This state machine, as we can see by the name, references all right hand attack states, so this is where we will reference our CMSG from. Before we do that, we will look for the last generator in the state list (#1180) and check its userData value, which is 17104921. Therefore we pick 17104922 as the userData value for our new CMSG. Performing a search for this value shows no other instances thereof, so we know it's safe to use.

I will not cover the hkparams of hkbStateMachines in depth in this example, please refer to the reference page for documentation on that. For the purposes of this example we will look at two hkparams, "states" and "wildcardTransitions".

The "states" parameter is a reference list like "generators" for CMSGs, the key difference being that it does not reference generator type objects, but hkbStateMachineStateInfo objects. The active state is selected based on the state ID of the state info objects.

The hkparam responsible for selecting the state is the "wildcardTransitions" hkparam, more specifically the object it references, an hkbStateMachineTransitionInfoArray, in our case #1184. As mentioned earlier, the state machine is where hks event calls are mapped to behavior states, this is done in the transition info array, which also specifies which transition effect to use when transitioning to the selected state.

When referencing our CMSG we must therefore do two things:

  1. Add it to the state list
  2. Create an event name to use in hks and map it to our stateId

To add a generator to a state machine state list, we must first create an hkbStateMachineStateInfo object as explained previously. Once again we reference the 3rd right-handed r1 state and copy its state info object.

To keep the behavior file somewhat tidy I like to have objects appear in hierarchical order whenever possible, so I will paste the hkbStateMachineStateInfo before my CMSG and make sure the name IDs reflect this order. Should you want to, you can also add hkobjects near their vanilla counterparts, as long as you give them a unique ID hkxpack will handle the name IDs, renaming objects and references to suit the new order of hkobjects. The reason I don't do this is twofold, for one this causes vanilla behaviors to constantly change IDs every time I add a new object and for another I like having modded behaviors separated from vanilla behaviors. In the end this is up to personal preference.

Once we have created our new state info hkobject, we add a reference to it to the end of the state machine state list and change the "generator" hkparam to reference our new CMSG. We will also change the "name" hkparam to AttackRightLight4. This is important because this is the name we will use in hks for the state's onUpdate function. The last thing we need to change is the stateId. For this I have found no apparent ID system, so we will now go to the transition info array referenced in the "wildcardTransitions" hkparam of our parent state machine (#1184) and look for an available stateId.

HkbStateMachineTransitionInfoArrays consist of a single hkparam called "transitions" which is an array of unnamed transition info objects. To map an event name to our new state we will want to duplicate one of these objects. The main hkparams which interest us here are "eventId", "toStateId", once again see the reference page for full documentation.

After creating our new transition info object within the "transitions" hkparam we will want to look through the existing objects for an unused stateId. In this case I will pick 47 and set the "toStateId" in our transition info object as well as the "stateId" hkparam of our hkbStateMachineStateInfo accordingly.

Now all we need to do is create an eventName which we will use to trigger our new behavior. Event names are stored at the end of the vanilla behavior file in the hkbBehaviorGraphStringData hkobject under the "eventNames" hkparam. To save yourself from having to scroll about 1300 lines every time you want to add a new event name, search for "attributeNames" which is the next hkparam. We will add a new hkcstring and call it W_AttackRightLight4, as all event names have the prefix "W_". To set this event name in our transition info object we refer to it by its index in the "eventId" hkparam. There are 1267 event names in vanilla with the index starting at 0, thus our new event name will have the index 1267. Additionally there is an entry under the "eventInfos" hkparam for every event. This is always 0 and omitting it does not break anything apparent so it is unclear what exactly it is used for. If you want to be thorough I suggest duplicating the last entry here for every event name you add. Once again searching for "variableBounds" will get you to the end of the list to save you from scrolling.

We are now done with editing the behavior file. Don't forget to repack it to hkx and repack the behbnd.


Triggering a Behavior from Hks

Once we have implemented a behavior we want to be able to trigger it in game, that's where hks comes into play.

The base level function to trigger a behavior from hks is hkbFireEvent, however you will never use this function directly. Generally when triggering attack behaviors you want to use AttackCommonFunction or SwordArtsCommonFunction if it's a weapon art, these functions call various higher level Exec functions such as ExecAttack and ExecEvasion based on the player input. These functions in turn call ExecEventAllBody or ExecEventHalfBlend which both call ExecEvent which finally calls hkbFireEvent.

Regular attack behaviors such as the one we just added are usually triggered from update functions. The first r1, r2, etc are triggered on input from Idle_onUpdate through IdleCommonFunction calling ExecAttack with their eventNames as arguments if it detects an attack button input. Followup attacks are all triggered from the update function of the previous attack using AttackCommonFunction. Each attack behavior has its own onUpdate function which is not only responsible for triggering followup attacks but handling button inputs during the animation in general. Without an onUpdate function a behavior will not react to button inputs, thus leaving the character locked in the animation until it finishes.


Creating an onUpdate() Function

To finish setting up our new attack behavior we will set up its onUpdate function and trigger it from the onUpdate function of the preceding attack, which looks like this:

function AttackRightLight3_onUpdate()
    if AttackCommonFunction("W_AttackRightLight2", "W_AttackRightHeavy1SubStart", "W_AttackLeftLight1", "W_AttackLeftHeavy1", "W_AttackBothLight2", "W_AttackBothHeavy1Start", FALSE, TRUE) == TRUE then
        return
    else
        return
    end
end

As we can see it consists of a single function call to AttackCommonFunction which has the following arguments:

AttackCommonFunction(r1, r2, l1, l2, b1, b2, guardcondition, use_atk_queue)

As mentioned earlier this function calls various Exec functions, including ExecAttack which it passes its attack event args to. It may only accept arguments for attack inputs but like all other CommonFunctions it handles all inputs. Generally most state machines directly under Master_SM will have a corresponding CommonFunction, so make sure to use the correct CommonFunction for the behavior you are adding. In fact, unless you are making exceptions for specific TAE sections your onUpdate functions will generally only consist of a call to this CommonFunction.

If we wanted to implement our 4th r1 for all weapons, we would need to change the first argument of the function ("W_AttackRightLight2") to "W_AttackRightLight4" in AttackRightLight3's onUpdate function. In this example we'll do something a bit more advanced and implement it only for a specific TAE section, 299.

The env to check for the override tae section of the player's weapon is:

env("装備武器特殊カテゴリ番号取得", hand)

Since this is a right hand attack we want to get the right hand weapon's tae section, so for the argument "hand" we choose the variable "HAND_RIGHT" which results in the following code:

function AttackRightLight3_onUpdate()
    local r1 = "W_AttackRightLight2"
    if env("装備武器特殊カテゴリ番号取得", HAND_RIGHT) == 299 then
        r1 = "W_AttackRightLight4"
    end
    if AttackCommonFunction(r1, "W_AttackRightHeavy1SubStart", "W_AttackLeftLight1", "W_AttackLeftHeavy1", "W_AttackBothLight2", "W_AttackBothHeavy1Start", FALSE, TRUE) == TRUE then
        return
    else
        return
    end
end

For two handed attacks, you need to account for the possibility of the player two-handing their left hand weapon, which can be accomplished using the following code:

local hand = HAND_RIGHT
if c_Style == HAND_LEFT_BOTH then
    hand = HAND_LEFT
end

Now we want to set up the onUpdate function for our new behavior. Make sure to use the name of its hkbStateMachineStateInfo along with the "_onUpdate" prefix. In this case I chose to have it combo to the first r1 attack but you can choose whichever behavior you want.

function AttackRightLight4_onUpdate()
    if AttackCommonFunction("W_AttackRightLight1", "W_AttackRightHeavy1SubStart", "W_AttackLeftLight1", "W_AttackLeftHeavy1", "W_AttackBothLight1", "W_AttackBothHeavy1Start", FALSE, TRUE) == TRUE then
        return
    else
        return
    end
end

And that's all there is to setting up a new attack animation. This basic process applies for any new animation you might want to add, with the difference generally being extra generators between it and the state info object, such as in the next example.


Creating Weapon Art Behaviors

Weapon art behaviors are a simple example of how hkbManualSelectorGenerators (hkbMSGs) are used and illustrate how we can insert generators between CMSG and StateMachineStateInfo to change the way our character behaves.

Basic weapon arts consist of two animations, one which is played when the player has enough FP to perform the move and the other if the player does not. This could be done using only the previous method and making separate CMSG states for both animations, however this has multiple downsides. First of all, you would need to check the player's FP for each weapon art individually and trigger separate behaviors based on that. Not only does this require a lot more hks work but also bloats the behavior file unnecessarily. Secondly, this would require twice the number of update functions for states which are essentially identical and thus would have identical update functions. Should separate update functions be needed for some reason, they can still be made using hkbScriptGenerators while preserving the other advantages of the system used for weapon arts.

Most behavior structures can be imitated using only hks and existing behaviors, however this is rarely advisable. As a general rule you want to maximize the work you do in the behavior file to keep a clean, minimal hks.

Instead of the structure of regular attack behaviors which have a separate state for each animation ID, weapon art behaviors consist of 2 CMSGs under the same state. This is achieved through the use of hkbManualSelectorGenerators.


Using hkbMSGs to Select from Multiple CMSGs

In this example we will create a simple single-move, right-handed weapon art under SwordArtsRight_SM. We will start off by creating a new state info object and referencing it at the end of the state list. We will call it "NewSwordArt" and create an event name called "W_NewSwordArt" as well as a transition info object as discussed in the previous example. When determining the stateId for our new behavior it is important to note that since many child states of SwordArtsRight_SM are also state machines, the transition info array does not contain all used stateIds for this SM. Thus we cannot rely solely on checking the transition info objects for used stateIds but must instead double check by looking at the other state info objects under this SM.

Between our SMSI and our 2 CMSGs, one for the animation which is played when the player has sufficient FP to perform the weapon art and one when they don't, we will create an hkbManualSelectorGenerator which will be used to select between our CMSGs. We will call it "NewSwordArt_Selector" and set its hkparams as follows:

Hkparam Value
userData 0
selectedGeneratorIndex 0
indexSelector null
selectedIndexCanChangeAfterActivate true
generatorChangedTransitionEffect null

We will then create 2 CMSGs with matching userData and reference them from the "generators" hkparam of our selector hkbMSG. We will call the first CMSG in the reference list "NewSwordArt_CMSG" and give it an available animId. The second CMSG will be called "NewSwordArt_NoPoints_CMSG" and will have the animId of the previous CMSG plus 1.

To select which CMSG is triggered when hks calls the event name, we use an hkbVariable. Generally when adding a weapon art behavior you will be able to use existing variables. For the first move of a weapon art, activated by pressing L2, you will want to use the variable called "IsEnoughArtPointsL2" which has index 128. Similarly for an r1 or r2 weapon art followup you will use "IsEnoughArtPointsR1" (129) or "IsEnoughArtPointsR2" (130) respectively. These variables are set in the SetSwordArtsPointInfo function in hks, which is also where you will want to add any further weapon art selector variables. For our new weapon art we will use "IsEnoughArtPointsL2" and bind it to the "selectedGeneratorIndex" hkparam of our selector hkbMSG.


hkbVariableBindingSets

Variables are bound to parameters using hkbVariableBindingSets which contain bindings. Almost all hkobjects in behavior files can reference a single binding set, which can however contain multiple bindings. Each binding specifies the index of an hkbVariable in their "variableIndex" hkparam and the name of the hkparam to bind it to in their "memberPath" hkparam. The "bitIndex" hkparam always has the value 255 and for the player "bindingType" is always set to "BINDING_TYPE_VARIABLE". As mentioned in the Structure section the enemy behavior file in Dark Souls III also uses so-called character properties which are also bound using hkbVariableBindingSets by setting "bindingType" to "BINDING_TYPE_CHARACTER_PROPERTY".

To bind the "IsEnoughArtPointsL2" variable to the "selectedGeneratorIndex" parameter of our selector hkbMSG we will create an hkbVariableBindingSet and reference it from the "variableBindingSet" hkparam. We will set the "memberPath" of our new binding set to "selectedGeneratorIndex" and the "variableIndex" to the index of our variable which in this case is 128.

The best way to obtain the index of a variable from its name is to find the name definition under the "variableNames" hkparam. Assuming you are using a text editor which displays the number of selected lines, i.e. Notepad++, select all the lines up to the variable we want to find the index of and subtract 1 from the number of lines to obtain its index.


Weapon Arts in Hks

All we have left to do now is to trigger our new weapon art from hks. The game determines which weapon art animation to play depending on the actionId specified in SwordArtsParam. In vanilla actionIds up to 36 are used so our new weapon art will have the actionId 37. In order to make our hks edits more clear and legible we will first define a few global variables which we will use when triggering our new event.

Looking at common_define we can see that each actionId has three corresponding global variables. One called "SWORDARTS_[name of action]" which is equivalent to the actionId, and two called "SWORDARTS_REQUEST_RIGHT_[name of action]" and "SWORDARTS_REQUEST_LEFT_[name of action]" respectively which have the values 200 + actionId and 100 + actionId. For example the parry weapon art with actionId 1 has the following variables defined in common_define.hks:

SWORDARTS_PARRY = 1
SWORDARTS_REQUEST_LEFT_PARRY = 101
SWORDARTS_REQUEST_RIGHT_PARRY = 201

Thus for our new actionId 37 with name NewSwordArt we put the following:

SWORDARTS_NEW = 37
SWORDARTS_REQUEST_LEFT_NEW = 137
SWORDARTS_REQUEST_RIGHT_NEW = 237

As of the time of writing this, there is no completely accurate decompilation of common_define.hks, so we will put this into c0000.hks at the top of the file. As mentioned earlier anything present in common_define can also be implemented in c0000.hks therefore, common_define should only be used as a reference until an accurate decompilation is made.

Unlike standard attacks, which weapon art to execute is not directly determined in onUpdate functions. Every CommonFunction calls ExecAttack, passing on its attack arguments which are set differently in each onUpdate function. ExecAttack in turn calls the GetAttackRequest function to determine which of the attacks to execute. GetAttackRequest checks which button is currently being pressed and returns the appropriate ATTACK_REQUEST, if the button being pressed is L2 however it calls the GetSwordArtsRequest() function which does the following:

function GetSwordArtsRequest()
    local style = c_Style
    local is_both = FALSE
    local arts_id = c_SwordArtsID
    if HAND_LEFT_BOTH <= style then
        is_both = TRUE
    end
    if is_both == TRUE then
        local request = arts_id + 200
        return request
    elseif c_SwordArtsHand == HAND_RIGHT then
        local request = arts_id + 200
        return request
    else
        local request = arts_id + 100
        return request
    end
end

The variable arts_id corresponds to the actionId from the SwordArtsParam and thus our variable "SWORDARTS_NEW" when wielding a weapon with a weapon art with actionId 37. This function checks which hand we are wielding our weapon with and returns the appropriate request to ExecAttack where the local variable "request" is used to store it. Now all we need to do is tell the ExecAttack function what to do when it receives our new request. Since our weapon art does not depend on which hand we are wielding our weapon in we will check for either request == SWORDARTS_REQUEST_LEFT_NEW or request == SWORDARTS_REQUEST_RIGHT_NEW and trigger our new behavior if true using the following code:

elseif request == SWORDARTS_REQUEST_RIGHT_NEW or request == SWORDARTS_REQUEST_LEFT_NEW then
        ExecEventAllBody("W_NewSwordArt")

We will add this to ExecAttack where all the other weapon art requests are handled.

Finally we need to create an onUpdate function for our new weapon art, which unlike the update functions of regular attacks will call SwordArtsCommonFunction instead of AttackCommonFunction.

function ChargeContinue3_onUpdate()
    if SwordArtsCommonFunction("W_AttackRightLight1", "W_AttackRightHeavy1Start", "W_AttackLeftLight1", "W_AttackLeftHeavy1", "W_AttackBothLight1", "W_AttackBothHeavy1Start", FALSE, FALSE, FALSE, GEN_TRANS_LEFT) ==
        TRUE then
        return
    elseif env("アニメ終了か", 1) == TRUE then
        SetArtsGeneratorTransitionIndex(GEN_TRANS_LEFT, TRUE)
        ExecEventAllBody("W_Idle")
        return
    else
        return
    end
end

Creating a Weapon Art followup

To finish off this example, let's suppose we want to create a third followup for a weapon art, Specifically the weapon art used by the Ringed Knight Spear, which is activated by pressing R2 after the second followup. To this end we will create a state consisting of two CMSGs as outlined previously under ChargeRight_SM called ChargeContinue3.

In hks the default variable to determine whether the character has enough FP required for the weapon art for each input is of the form "IsEnoughArtPoints[input]". For weapon arts which feature multiple followup attacks there is usually a condition which checks for the active selector object using the IsNodeActive function and sets "IsEnoughArtPoints[input]_2" for the first followup or "IsEnoughArtPoints[input]_3" for the second followup. Thus if we were to add a third followup we'd use the variable "IsEnoughArtPoints[input]_4" according to the naming scheme, so in this case "IsEnoughArtPointsR2_4". Realistically we could use existing variables but sticking to the existing system is preferable in order to keep things clean and avoid confusion. Thus we will create a new hkbVariable of this name.


Creating hkbVariables and Setting Them in Hks

As mentioned in the Structure section, hkbVariables are defined in the hkbBehaviorGraphData (#12799) and hkbBehaviorGraphStringData (#12801) hkobjects and their initial values are set in the hkbVariableValueSet (#12800) hkobject. When adding a new hkbVariable we need to do the following:

  1. Add entries to the "variableInfos", "variableBounds", "wordVariableValues" and "variableNames" hkparams
  2. Reference the variable from an hkbVariableBindingSet
  3. Set the variable in hks.

First, we will look for the "variableInfos" hkparam of the hkbBehaviorGraphData hkobject and add a new entry to the end of the list by duplicating one of the existing hkobjects under said hkparam. Once we've got that we will set the parameters of our new entry. The hkparam "role" with its single sub-object is always identical so we do not need to touch it. The hkparam "type" determines the variable type, check the reference page for possible values. In this case we will use "VARIABLE_TYPE_INT16" like the other weapon art selector variables.

Next we will set the minimum and maximum values of our variable in the "variableBounds" hkparam of the hkbBehaviorGraphData hkobject. Once again we will duplicate an entry and adjust the values as desired. Common values for each variable type can be found on the reference page. For our variable we will set a minimum value of 0 and a maximum value of 1, as our selector hkbMSG only has 2 child generators.

To set the initial value of our variable we will add an entry to the "wordVariableValues" hkparam of the hkbVariableValueSet hkobject. This won't really matter as our variable will be set every time we activate the state. We will use the value 0.

Finally, we will add a new entry to the "variableNames" hkparam of the hkbBehaviorGraphStringData hkobject. This will be the name we use in hks to refer to our new variable. As stated previously we will call it "IsEnoughArtPointsR2_4".

Our variable is now fully defined and all we have left to do is to bind it to the "selectedGeneratorIndex" hkparam of our selector object. To this end, we use an hkbVariableBindingSet as discussed in the previous section, referring to our new variable by its index, 223.

The function which sets the IsEnoughArtPoints variables in hks is called SetSwordArtsPointInfo. For our new followup attack, which is activated by pressing R2, we want to add the following condition under the "elseif button == ACTION_ARM_R2" condition:

elseif IsNodeActive("ChargeContinue2_Selector") == TRUE then
    val = "IsEnoughArtPointsR2_4"

This will make sure the correct hkbVariable is being set to the appropriate value.


Adding a Magic Casting Animation


Advanced Behavior Systems

Here are some examples of more advanced behavior edits which have been successfully implemented in the following fashion. This list is an ongoing effort, so if you are interested in contributing your own edits please add them here or contact me on discord (The12thAvenger#6149).

This section will assume a solid understanding of behavior systems and will function less as a tutorial and more as an explanation of how various hkobjects are used in these contexts and how the hks functions which interact with them work.

I would like to thank El Fonz0 and NamelessHoodie for implementing and testing the layer blend method outlined here.


HalfBlend

The Dark Souls III HalfBlend behavior system allows for blending of different animations on the lower and upper body of the player character. It is used most commonly for animations which allow you to move freely while they play, for example gesture and item use animations.

HalfBlend Structure

HalfBlend is a state machine under Master_SM with 2 child SMs, "Lower_SM" and "Upper_SM", which are blended using hkbBlenderGenerator "HalfBlend_Blend". The hkbModifierList "MoveAnimeTwist00", which is identical to the one called "MoveAnimeTwist" found in the Move behavior hierarchy, applies the following modifiers to the blender generator:

Name Variable Name Variable Index Start Bone Index End Bone Index
TwistMaster TwistMasterAngle 49 0 0
TwistRootRotYCancel TwistMasterAngle 49 41 43
TwistPelvisCancel TwistMasterAngle60 81 8 8
TwistUpper TwistUpperRootAngle 48 39 43
MoveTwistModifier MoveTwistAngle 66 39 78
TwistR_Thigh TwistMasterAngle40 82 25 25
TwistL_Thigh TwistMasterAngle40 82 10 10

All of them are hkbTwistModifiers which, set the rotation of a range of bones, determined by start and end bone index, in a single axis which is determined by their axisOfRotation hkparams. This rotation is controlled by the variables bound to their "twistAngle" hkparams, listed above.

The hkbBlenderGenerator "HalfBlend_Blend" is responsible for the half-blend itself. Its children both have a "weight" hkparam of 1 and complementary bone weight arrays. The bone weight array of the upper hkbBlenderGeneratorChild (#10192) which has the name ID #11707 reveals an interesting property of hkbBoneWeightArrays. It only specifies 39 bone weights, setting them all to 0, whereas the player has 110 bones. This corresponds to the the lower bone weight array setting the first 39 bones to 1 and the rest to 0 and shows that hkbBoneWeightArrays automatically set the weight of all bones for which a weight is not defined to 1.


Adding HalfBlend Behaviors

When adding half blend animations to existing state machines you want to make two versions of the same CMSG, named as follows: "[state name]_CMSG" and "[state name]_Upper_CMSG". The former will be responsible for the lower half and the latter for the upper. At CMSG level they should both be identical except for "enableScript" and "enableTae" which will be set to false on the lower CMSG. They will also have different userData according to their state machines. Make sure the event names you use adhere to the naming scheme here, lower event with no suffix and identically named upper event with suffix "_Upper", it will be important when triggering half blended behaviors from hks.

To add state machines to HalfBlend_SM a specific system of variable binding must be observed. Each state machine after HalfBlend_SM, including lower and upper SMs have an hkbVariable bound to their "startStateId" hkparam called either "LowerDefaultState0X" or "UpperDefaultState0X" depending on the parent SM. The last digit in the variable name depends on how many SMs away from Lower_SM or Upper_SM you are. So "LowerDefaultState00" is bound to Lower_SM, "LowerDefaultState01" is bound to child SMs of Lower_SM and "LowerDefaultState02" is bound to the child SMs of those SMs. Should your behavior structure be more than 2 SMs deep you will need to add new hkbVariables accordingly, which have to adhere to this naming scheme. So if you added a hierarchy of behavior objects 3 SMs deep to Lower_SM you would need to create an hkbVariable called "LowerDefaultState03" and bind it to the "startStateId" hkparam of your new SM.


HalfBlend in Hks

HalfBlend animations are triggered using the ExecEventHalfBlend function which has the following arguments:

ExecEventHalfBlend(event_table, blend_type)

As we can see, half blend does not use single event names to trigger behaviors but rather event tables. This makes sense since upper and lower blend are separate state machines, thus the upper and lower behaviors are also separate states and cannot be triggered by a single event name.

Event tables consist of the base event name and a state Id for every state machine between HalfBlend_SM and the behavior's ClipGenerators. For example the upper blend of GestureStart animations are all referenced by the hkbMSG "Gesture_Upper_Selector", which is state 0 of "Gesture_Upper_SM". "Gesture_Upper_SM" in turn is state 22 of "Upper_SM" which is a state of "HalfBlend_SM". So between "HalfBlend_SM" and the hkbClipGenerators we have 2 state machines, "Gesture_Upper_SM" and "Upper_SM", thus our the event table needs to have 3 elements: our base event name and our 2 state ids. Taking a look at the event name for GestureStart we can see that this is indeed the case.

GESTURE_DEF0 = 22
GESTURESTART_DEF1 = 0
Event_GestureStart = {"W_GestureStart", GESTURE_DEF0, GESTURESTART_DEF1}

It is important that the stateIds are in hierarchical order because they are systematically assigned to the default state variables mentioned in the previous section by the function ExecEventHalfBlend. Looking at the ExecEventHalfBlend function we can also see the reason for the eventName naming scheme.

function ExecEventHalfBlend(event_table, blend_type)
    if blend_type == ALLBODY then
        SetVariable("MoveSpeedLevelReal", 0)
        local lower_event = event_table[1]
        local upper_event = lower_event .. "_Upper"
        ExecEvents(lower_event, upper_event)
        for local0 = 2, #event_table, 1 do -- iterates through state Id arguments and sets them to the default state variables
            SetVariable("LowerDefaultState0" .. local0 - 2, event_table[local0])
            SetVariable("UpperDefaultState0" .. local0 - 2, event_table[local0])
        end
    elseif blend_type == LOWER then
        ExecEvent(event_table[1])
        for local1 = 2, #event_table, 1 do
            SetVariable("LowerDefaultState0" .. local1 - 2, event_table[local1])
        end
    elseif blend_type == UPPER then
        ExecEvent(event_table[1] .. "_Upper")
        for local1 = 2, #event_table, 1 do
            SetVariable("UpperDefaultState0" .. local1 - 2, event_table[local1])
        end
    end
    return
end

As we can see the name of the upper event is obtained from the event table by appending "_Upper" to the name of the lower event. Thus it is important to stick to the naming system when creating our event names.


Various uses of Blender Generators

Directional Blend

Directional blend is used in various movement animations to allow for smooth movement in any direction. The basic structure is the same as any other blend, with multiple BlenderGeneratorChildren which are blended with each other. As with all these examples, the directionality is achieved through an interesting use of the "blendParameter" hkparam.

The "blendParameter" hkparam determines the target weight based on which the children of an hkbBlenderGenerator are blended. By default it has a value of 1, with the children having a "weight" parameter between 0 and 1, with 0 being no weight and 1 being full weight. Commonly the weight of the children is bound to multiple hkparams, such as in the Layer Blend example below, however binding the blendParameter of the parent blender generator allows for some very useful functionality, such as with directional blends.

A fairly simple example of directional blending occurs with rolling animations. Directional blend for rolling is achieved through the use of hkbBlenderGenerators for each weight class under Rolling_SM and uses a variable called "RollingAngleReal", which is a value between -180 and 180 that is set to the movement direction relative to the direction the player is looking if the player initiates a roll while locked on to a target. This variable is bound to the blendParameter of the blender generators.

Each child object of these generators has a weight set in intervals of 90 based on their direction, with the forward roll having a weight of 0, the roll to the right a weight of 90, the roll to the left a weight of -90, and two children with weights of 180 and -180 respectively which both reference the backward roll. Thus by setting the variable bound to the blendParameter to the current rolling angle, a blend corresponding to the rolling direction is achieved. All blend children which affect the same bones are blended relative to each other, so for example when rolling forward "RollingAngleReal" has the value 0, so the forward roll will be blended at 100% weight, since its "weight" parameter perfectly matches blendParameter of its parent blender generator, and the other layers will all have 0% weight. Similarly, when rolling at a 45 degree angle to the left "RollingAngleReal" has the value -45, so the left roll animation with a "weight" parameter of -90 will have 50% weight and the front rolling animation with a "weight" value of 0 will also have 50% weight, creating a blend which lies perfectly between the two animations.

When blending CMSGs which have tae enabled it is important to specify which tae should be used, as we do not want to use both, otherwise sound effects, visual effects etc. would be doubled. This is handled by binding different variables to the "enableTae" hkparam of the CMSGs. In the case of the rolling behavior these are named "EnableTAE_Rolling[Direction]" so for instance for the front rolling CMSGs it is named "EnableTAE_RollingFront". These are set in hks in the ExecEvasion function by simply checking the value of "RollingAngleReal", enabling the tae of whichever direction is blended the most and disabling the rest.


ID-Based Selection

Layer Blends