Site Tools


topic:havok_behavior_editing

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
topic:havok_behavior_editing [2025/02/25 13:14] – created admintopic:havok_behavior_editing [2025/02/27 00:44] (current) admin
Line 7: Line 7:
 ===== Getting Started ===== ===== Getting Started =====
  
-For simple edits such as registering animations or creating new [[[*reference:havok-behavior-reference#CMSG CustomManualSelectorGenerators]]] for regular attacks or weapon art attacks you can use [*https://github.com/The12thAvenger/DS3BehaviorTool/releases DS3BehaviorTool]. Its ability to register CustomManualSelectorGenerators can also be used as a starting point for more advanced edits.+For simple edits such as registering animations or creating new [[common-refmat:havok_behavior_reference#Generators|CustomManualSelectorGenerator]] for regular attacks or weapon art attacks you can use [[https://github.com/The12thAvenger/DS3BehaviorTool/releases 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 [*https://discord.com/channels/529802828278005773/529900741998149643/1187076379116839033 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 [[file c0000.hks | this]] decompiled c0000.hks for the player and [[file DSLuaDecompiler-alpha-0.2.zip | DSLuaDecompiler]] to decompile any other hks files you might need. Additionally [[file common_define.hks | 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.+To directly edit behavior hkx files as outlined here, get [[https://discord.com/channels/529802828278005773/529900741998149643/1187076379116839033 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 {{c0000.hks|this}} decompiled for the player and {{DSLuaDecompiler-alpha-0.2.zip |DSLuaDecompiler}} to decompile any other hks files you might need. Additionally {{common_define.hks|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 =====
  
-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 [#hkbVariables Creating hkbVariables and Setting Them in Hks].+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 [[havok_behavior_editing#Creating hkbVariables and Setting Them in Hks|]]
  
-[[# Structure]] +==== Structure ====
-=== 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.+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.+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 [[[reference:havok-behavior-reference#hkbStateMachine hkbStateMachine]]] called "Root", and the hkbBehaviorGraphData is referenced. +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 [[havok_behavior_editing#Generators|state machine]] 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 Layer Blends]). State machines also map event names to states.+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. 
  
-Eventually every branch of the behavior hierarchy reaches a [[[*reference:havok-behavior-reference#CMSG | 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 TAEIt is here where animations are registered for each TAE sectionIf you want to add 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 [[[*reference:havok-behavior-reference#hkbClipGenerator | hkbClipGenerator]]] hkobject (see [#registering-animations 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.+All the main state machines are registered under the master state machineThey serve to group animations so that blends and other modifiers can be applied categoricallyFor instance AttackLeft contains all left hand attacks, to which blend layer is applied to blend the right arm depending on the right hand weapon hold position (see [[havok_behavior_editing#Layer Blends]]). State machines also map event names to states.
  
-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 [[[*reference:havok-behavior-reference#hkbVariableBindingSet hkbVariableBindingSets]]] to set various properties of behavior objects (see [#hkbVariableBindingSets hkbVariableBindingSets])Unlike hkbVariables however, these are constant. Their main use is in the common enemy behavior file c9997.+Eventually every branch of the behavior hierarchy reaches a [[havok_behavior_editing#Generators|CustomManualSelectorGenerator]] which I will refer to as CMSG for the sake of brevityCMSGs are Fromsoftware's custom havok generator class which define the animation IDs used in TAE
  
-=== Naming Conventions ===+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 [[havok_behavior_editing#Generators|hkbClipGenerator]] hkobject (see [[havok_behavior_editing#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 [[havok_behavior_editing#hkbVariableBindingSets]] to set various properties of behavior objects (see [[havok_behavior_editing#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. 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:havok-behavior-reference | reference page]]] for the suffixes used for each class.+See the [[common-refmat:havok_behavior_reference | reference page]] for the suffixes used for each class.
  
-=== General Types ===+==== 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:havok-behavior-reference | reference page]]].+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 [[common-refmat:havok_behavior_reference | reference page]].
  
-[[# Generators]] +=== Generators ===
-== Generators ==+
  
-Generators are the most common type of behavior object. As mentioned previously, [[[*reference:havok-behavior-reference#CMSG | CustomManualSelectorGenerators]]] define all animation IDs and hkbClipGenerators register individual TAE sections to use these animation IDs. Other common generator types include [[[reference:havok-behavior-reference#hkbManualSelectorGenerator | hkbManualSelectorGenerators]]] (hkbMSGs), which are most commonly used to select between generators using bound hkbVariables (see [#creating-WA-behaviors Creating Weapon Art Behaviors] for a detailed example and use case), [[[reference:havok-behavior-reference#hkbScriptGenerator | hkbScriptGenerators]]], which are used to attach hks functions such as update, activate or deactivate functions to all child states and [[[reference:havok-behavior-reference#hkbBlenderGenerator | hkbBlenderGenerators]]] and [[[reference:havok-behavior-reference#hkbLayerGenerator | hkbLayerGenerators]]] for animation blending (see [#Advanced-Behavior-Systems Advanced Behavior Systems]).+Generators are the most common type of behavior object. As mentioned previously, [[common-refmat:havok_behavior_reference#Generators|CustomManualSelectorGenerators]] define all animation IDs and hkbClipGenerators register individual TAE sections to use these animation IDs. Other common generator types include [[common-refmat:havok_behavior_reference#CustomManualSelectorGenerator|hkbManualSelectorGenerators]] (hkbMSGs), which are most commonly used to select between generators using bound hkbVariables (see [[havok_behavior_editing#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 [[common-refmat:havok_behavior_reference#hkbBlenderGenerator|hkbBlenderGenerators]] and hkbLayerGenerators for animation blending (see [[havok_behavior_editing#Advanced Behavior Systems]]).
  
-[[[reference:havok-behavior-reference#hkbStateMachines | 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.+''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 ===
  
-Modifiers are mostly used at root level for character head rotation, in the form of [[[reference:havok-behavior-reference#CustomLookAtTwistModifier | CustomLookAtTwistModifiers]]], and IK. Also present are [[[reference:havok-behavior-reference#hkbTwistModifier | hkbTwistModifiers]]] which twist bones based on hkbVariables.+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 [[[reference:havok-behavior-reference#hkbModifierGenerator | hkbModifierGenerator]]] which specifies a child generator and a modifier. Often this modifier is an [[[reference:havok-behavior-reference#hkbModifierList | hkbModifierList]]] which allows one to apply multiple modifiers at once, thus avoiding having to chain hkbModifierGenerators.+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. Modifiers will not be covered here, however they are present on the behavior reference page.
  
-== Transitions ==+=== 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. 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 [[[reference:havok-behavior-reference#hkbBlendingTransitionEffect | hkbBlendingTransitionEffects]]] and [[[reference:havok-behavior-reference#CustomTransitionEffect | CustomTransitionEffects]]], with the default transition effect used for the player being CustomTransitionEffect #292. Transition effects are applied in [[[reference:havok-behavior-reference#hkbTransitionInfoArray | 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 [#hkbMSGs Using hkbMSGs to Select from Multiple CMSGs].+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 [[havok_behavior_editing#Using hkbMSGs to Select from Multiple CMSGs]].
  
-=== Classes and HkParams ===+==== Classes and HkParams ====
  
-See [[[*reference:havok-behavior-reference | 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.+See [[common-refmat:havok_behavior_reference | 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 =====
  
-Havok script files are lua scripts which controls which behaviors are triggered when. It is also where [[[*reference:havok-behavior-reference#hkbVariable | hkbVariables]]] are set to modify behaviors.+Havok script files are lua scripts which controls which behaviors are triggered when. It is also where [[common-refmat:havok_behavior_reference#hkbVariables | 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. [[file c9997.hks | Here]] is an experimental decomp of c9997.hks which might be useful as a reference, please note that it does not work in game.+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. {{c9997.hks | 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 [*https://docs.google.com/document/d/1vvMslEOA9-og2SpoBNsf1uWFs1RW8lS7UNw7GOy8vRw/edit?usp=sharing here] and [[[modding-movesets-the-muffin-knowledge-compenium-ds3 | here]]]. +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 [[https://docs.google.com/document/d/1vvMslEOA9-og2SpoBNsf1uWFs1RW8lS7UNw7GOy8vRw/edit?usp=sharing |here]] and [[tutorial:modding-movesets-the-muffin-knowledge-compenium-ds3 | here]].
-------+
  
-+++ CommonFunctions+==== CommonFunctions ====
  
-CommonFunctions are the functions used in [#onUpdate onUpdate] functions to interpret player inputs. Generally all substates of the "Masterstate machine will have their own CommonFunction, including Idle, which is CMSG. They will call various [#Exec Exec] functions, either using constant arguments or passing on the arguments they receive from onUpdate, such as state specific attack eventNames. +CommonFunctions are the functions used in [[havok_behavior_editing#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 [[havok_behavior_editing#Exec]] functions, either using constant arguments or passing on the arguments they receive from onUpdate, such as state specific attack eventNames.
-------+
  
-+++ Exec+==== Exec ====
  
-Exec functions check for player request inputs and execute animations by calling the lower level ExecEvent functions "ExecEventAllBodyor "ExecEventHalfBlend", which in turn all call the ExecEvent function which uses the base Havok function hkbFireEvent to execute events based on eventName.+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]] +==== Env ====
-+++ Env+
  
 The env function gets info about the character from the game, it has the following syntax: The env function gets info about the character from the game, it has the following syntax:
  
-[[code type="lua"]]+<code Lua>
 env(ID, args) env(ID, args)
 </code> </code>
Line 95: Line 96:
  
 Most envs called in the player hks are referred to by numbers, however some are named in japanese. Most envs called in the player hks are referred to by numbers, however some are named in japanese.
------- 
  
-+++ Act+==== 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 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.
Line 103: Line 103:
 The function has the following syntax: The function has the following syntax:
  
-[[code type="lua"]]+<code Lua>
 act(ID, args) act(ID, args)
 </code> </code>
  
 As with the env function, most acts are referred to by numbers but some are named in japanese. As with the env function, most acts are referred to by numbers but some are named in japanese.
------- 
  
-[[# onUpdate]] +==== onUpdate ====
-+++ onUpdate+
  
-Each CMSG, given a unique [[[reference:havok-behavior-reference#CMSG | userData]]] value and provided the "enableScripthkparam is enabled, automatically creates its own onUpdate function named [name of [[[reference:havok-behavior-reference#hkbStateMachineStateInfo | 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.+Each CMSG, given a unique userData value and provided the ''enableScript'' hkparam is enabled, automatically creates its own onUpdate function named <name>_onUpdate() based on the hkbStateMachineStateInfo name. 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: Most basic onUpdate functions have the following form:
-[[code type="lua"]]+<code Lua>
 function SMSIName_onUpdate() function SMSIName_onUpdate()
     if SomeCommonFunction(args) == TRUE then     if SomeCommonFunction(args) == TRUE then
Line 128: Line 126:
 As an example the first one-handed r1 attack: As an example the first one-handed r1 attack:
  
-[[code type="lua"]]+<code Lua>
 function AttackRightLight1_onUpdate() function AttackRightLight1_onUpdate()
     if AttackCommonFunction("W_AttackRightLight2", "W_AttackRightHeavy1SubStart", "W_AttackLeftLight1",     if AttackCommonFunction("W_AttackRightLight2", "W_AttackRightHeavy1SubStart", "W_AttackLeftLight1",
Line 141: Line 139:
 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.  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 [[[reference:havok-behavior-reference#hkbScriptGenerator | hkbScriptGenerator]]]. +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+===== 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:havok-behavior-reference | reference page]]] for a full list of classes and what they do, and always refer to vanilla examples to see how they are used.+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 [[common-refmat:havok_behavior_reference | 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. 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 [*https://github.com/The12thAvenger/DS3BehaviorTool/releases DS3BehaviorTool] so for anything other than educational purposes I suggest using that. +All examples here except for adding magic casting animations are supported by my tool [[https://github.com/The12thAvenger/DS3BehaviorTool/releases |DS3BehaviorTool]] so for anything other than educational purposes I suggest using that.
-------+
  
-[[# registering-animations]] +==== Registering an Existing Animation ID for a TAE Section ====
-+++ 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. 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 [*https://discord.com/channels/529802828278005773/529900741998149643/699509305929629849 hkxpack-souls], which is located in your character's behbnd in the Characters folder, and open the resulting XML file.+First, unpack your hkx behavior file using [[https://discord.com/channels/529802828278005773/529900741998149643/699509305929629849 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.  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 [[[*reference:havok-behavior-reference#CMSG | 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:+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:
  
-[[code type="xml"]]+<code Lua>
 <hkparam name="animId">30000</hkparam> <hkparam name="animId">30000</hkparam>
 </code> </code>
Line 170: Line 165:
 Thus an effective method for finding the CMSG you want is searching for:  Thus an effective method for finding the CMSG you want is searching for: 
  
-<code>+<code Lua>
 "animId">[Id of your animation] "animId">[Id of your animation]
 </code> </code>
  
-Once we have located our CMSG of choice, in this case #528, we need to add an [[[*reference:havok-behavior-reference#hkbClipGenerator | 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.+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: To add any hkobject to your behavior file you need to do the following:
- +  - copy/paste an existing instance of your desired class 
-copy/paste an existing instance of your desired class +  edit the parameters of your new hkobject 
-edit the parameters of your new hkobject +  find the next available name ID and set your hkobject's name attribute 
-find the next available name ID and set your hkobject's name attribute +  reference your new hkobject somewhere
-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. 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.
Line 187: Line 181:
 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. 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:havok-behavior-reference#hkbClipGenerator | reference page]]] for documentation on what the other parameters do as well as their default/typical values+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"
  
-It is unclear what the "namehkparam 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. +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.+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"+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.  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. +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: Here is the final version of our new hkbClipGenerator:
  
-[[code type="xml"]]+<code Lua>
 <hkobject class="hkbClipGenerator" name="#12802" signature="0xd4cc9f6"> <hkobject class="hkbClipGenerator" name="#12802" signature="0xd4cc9f6">
     <hkparam name="variableBindingSet">null</hkparam>     <hkparam name="variableBindingSet">null</hkparam>
Line 221: Line 215:
 </code> </code>
  
-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. +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 [[[*reference:havok-behavior-reference#hkbManualSelectorGenerator | 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).+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. 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. 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+==== 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. 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. 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+=== 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.+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""generatorsand "animId". For a full list of parameters and what they do see the [[[reference:havok-behavior-reference#CMSG | reference page]]].+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''
  
-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-animations Registering an Existing Animation ID for a TAE Section] and reference it here.+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 [[havok_behavior_editing#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 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 onUpdate] function will not work. As mentioned in {anchor:Structure:Structure}} CMSGs are typically grouped by [[[reference:havok-behavior-reference#hkbStateMachine | 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.+The userData hkparam must be unique for each state, otherwise our [[havok_behavior_editing#onUpdate]] function will not work. As mentioned in 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 [[[reference:havok-behavior-reference#hkbStateMachineStateInfo | hkbStateMachineStateInfo]]].+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.  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 [#magic-casting-animation Adding a Magic Casting Animation] and [#creating-WA-behaviors Creating Weapon Art Behaviors].+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 [[havok_behavior_editing#Adding a Magic Casting Animation]] and [[havok_behavior_editing#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.+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:havok-behavior-reference#hkbStateMachine | reference page]]] for documentation on that. For the purposes of this example we will look at two hkparams, "states" and "wildcardTransitions"+I will not cover the hkparams of hkbStateMachines in depth in this example, please refer to the [[common-refmat:havok_behavior_reference | reference page]] for documentation on that. For the purposes of this example we will look at two hkparams, "states" and "wildcardTransitions"
  
-The "statesparameter is a reference list like "generatorsfor CMSGs, the key difference being that it does not reference [#Generators generator] type objects, but hkbStateMachineStateInfo objects. The active state is selected based on the state ID of the state info objects. +The ''states'' parameter is a reference list like ''generators'' for CMSGs, the key difference being that it does not reference [common-refmat:havok_behavior_reference#Generators|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 [[[reference:havok-behavior-reference#hkbStateMachineTransitionInfoArray | 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.+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: When referencing our CMSG we must therefore do two things:
- +  - Add it to the state list 
-Add it to the state list +  Create an event name to use in hks and map it to our stateId
-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 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. 
Line 272: Line 263:
 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. 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.+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:havok-behavior-reference#hkbStateMachineTransitionInfoArray | reference page]]] for full documentation.+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 [[common-refmat:havok_behavior_reference | 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. 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 [[[reference:havok-behavior-reference#hkbBehaviorGraphStringData | hkbBehaviorGraphStringData]]] hkobject under the "eventNameshkparam. To save yourself from having to scroll about 1300 lines every time you want to add a new event name, search for "attributeNameswhich 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 "variableBoundswill get you to the end of the list to save you from scrolling.+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. 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+=== 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.  Once we have implemented a behavior we want to be able to trigger it in game, that's where hks comes into play. 
Line 290: Line 280:
  
 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. 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 
  
 +== 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: 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:
  
-<code>+<code Lua>
 function AttackRightLight3_onUpdate() function AttackRightLight3_onUpdate()
     if AttackCommonFunction("W_AttackRightLight2", "W_AttackRightHeavy1SubStart", "W_AttackLeftLight1", "W_AttackLeftHeavy1", "W_AttackBothLight2", "W_AttackBothHeavy1Start", FALSE, TRUE) == TRUE then     if AttackCommonFunction("W_AttackRightLight2", "W_AttackRightHeavy1SubStart", "W_AttackLeftLight1", "W_AttackLeftHeavy1", "W_AttackBothLight2", "W_AttackBothHeavy1Start", FALSE, TRUE) == TRUE then
Line 308: Line 297:
 As we can see it consists of a single function call to AttackCommonFunction which has the following arguments: As we can see it consists of a single function call to AttackCommonFunction which has the following arguments:
  
-<code>+<code Lua>
 AttackCommonFunction(r1, r2, l1, l2, b1, b2, guardcondition, use_atk_queue) AttackCommonFunction(r1, r2, l1, l2, b1, b2, guardcondition, use_atk_queue)
 </code> </code>
  
-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 [#CommonFunction CommonFunctionsit 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.+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. 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.
Line 318: Line 307:
 The [#env env] to check for the override tae section of the player's weapon is: The [#env env] to check for the override tae section of the player's weapon is:
  
-<code>+<code Lua>
 env("装備武器特殊カテゴリ番号取得", hand) env("装備武器特殊カテゴリ番号取得", hand)
 </code> </code>
Line 324: Line 313:
 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: 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:
  
-<code>+<code Lua>
 function AttackRightLight3_onUpdate() function AttackRightLight3_onUpdate()
     local r1 = "W_AttackRightLight2"     local r1 = "W_AttackRightLight2"
Line 340: Line 329:
 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: 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:
  
-<code>+<code Lua>
 local hand = HAND_RIGHT local hand = HAND_RIGHT
 if c_Style == HAND_LEFT_BOTH then if c_Style == HAND_LEFT_BOTH then
Line 349: Line 338:
 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. 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.
  
-<code>+<code Lua>
 function AttackRightLight4_onUpdate() function AttackRightLight4_onUpdate()
     if AttackCommonFunction("W_AttackRightLight1", "W_AttackRightHeavy1SubStart", "W_AttackLeftLight1", "W_AttackLeftHeavy1", "W_AttackBothLight1", "W_AttackBothHeavy1Start", FALSE, TRUE) == TRUE then     if AttackCommonFunction("W_AttackRightLight1", "W_AttackRightHeavy1SubStart", "W_AttackLeftLight1", "W_AttackLeftHeavy1", "W_AttackBothLight1", "W_AttackBothHeavy1Start", FALSE, TRUE) == TRUE then
Line 360: Line 349:
  
 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. 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-WA-behaviors]] +==== Creating Weapon Art Behaviors ====
-+++ Creating Weapon Art Behaviors+
  
-Weapon art behaviors are a simple example of how [[[reference:havok-behavior-reference#hkbManualSelectorGenerator | hkbManualSelectorGenerators]]] (hkbMSGs) are used and illustrate how we can insert generators between CMSG and StateMachineStateInfo to change the way our character behaves.+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.  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. 
Line 372: Line 359:
  
 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. 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.
------- 
  
-[[# hkbMSGs]] +=== Using hkbMSGs to Select from Multiple CMSGs ===
-+++++ 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. 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 [[[reference:havok-behavior-reference#hkbManualSelectorGenerator | hkbManualSelectorGenerator]]] which will be used to select between our CMSGs. We will call it "NewSwordArt_Selector" and set its hkparams as follows:+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 || +Hkparam Value ^ 
-|| userData || 0 |+| userData | 0 | 
-|| selectedGeneratorIndex || 0 |+| selectedGeneratorIndex | 0 | 
-|| indexSelector || null |+| indexSelector | null | 
-|| selectedIndexCanChangeAfterActivate || true |+| selectedIndexCanChangeAfterActivate | true | 
-|| generatorChangedTransitionEffect || null ||+| 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. 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. 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+=== hkbVariableBindingSets ===
  
-Variables are bound to parameters using [[[reference:havok-behavior-reference#hkbVariableBindingSet | 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 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".+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 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.  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. 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+=== 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. 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.
Line 408: Line 391:
 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: 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:
  
-<code>+<code Lua>
 SWORDARTS_PARRY = 1 SWORDARTS_PARRY = 1
 SWORDARTS_REQUEST_LEFT_PARRY = 101 SWORDARTS_REQUEST_LEFT_PARRY = 101
Line 416: Line 399:
 Thus for our new actionId 37 with name NewSwordArt we put the following: Thus for our new actionId 37 with name NewSwordArt we put the following:
  
-<code>+<code Lua>
 SWORDARTS_NEW = 37 SWORDARTS_NEW = 37
 SWORDARTS_REQUEST_LEFT_NEW = 137 SWORDARTS_REQUEST_LEFT_NEW = 137
Line 426: Line 409:
 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: 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:
  
-<code>+<code Lua>
 function GetSwordArtsRequest() function GetSwordArtsRequest()
     local style = c_Style     local style = c_Style
Line 449: Line 432:
 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: 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:
  
-<code>+<code Lua>
 elseif request == SWORDARTS_REQUEST_RIGHT_NEW or request == SWORDARTS_REQUEST_LEFT_NEW then elseif request == SWORDARTS_REQUEST_RIGHT_NEW or request == SWORDARTS_REQUEST_LEFT_NEW then
         ExecEventAllBody("W_NewSwordArt")         ExecEventAllBody("W_NewSwordArt")
Line 458: Line 441:
 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. 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.
  
-<code>+<code Lua>
 function ChargeContinue3_onUpdate() function ChargeContinue3_onUpdate()
     if SwordArtsCommonFunction("W_AttackRightLight1", "W_AttackRightHeavy1Start", "W_AttackLeftLight1", "W_AttackLeftHeavy1", "W_AttackBothLight1", "W_AttackBothHeavy1Start", FALSE, FALSE, FALSE, GEN_TRANS_LEFT) ==     if SwordArtsCommonFunction("W_AttackRightLight1", "W_AttackRightHeavy1Start", "W_AttackLeftLight1", "W_AttackLeftHeavy1", "W_AttackBothLight1", "W_AttackBothHeavy1Start", FALSE, FALSE, FALSE, GEN_TRANS_LEFT) ==
Line 473: Line 456:
  
 </code>  </code> 
------- 
  
-+++++ Creating a Weapon Art followup+=== 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. 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.  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+=== Creating hkbVariables and Setting Them in Hks ===
  
-As mentioned in the [#Structure Structuresection, 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:+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: 
 +  - Add entries to the "variableInfos", "variableBounds", "wordVariableValues" and "variableNames" hkparams 
 +  - Reference the variable from an hkbVariableBindingSet 
 +  - Set the variable in hks.
  
-# Add entries to the "variableInfos", "variableBounds", "wordVariableValues" and "variableNames" hkparams +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 [[common-refmat:havok_behavior_reference#Enums | reference page]] for possible values. In this case we will use "VARIABLE_TYPE_INT16" like the other weapon art selector variables.
-# Reference the variable from an hkbVariableBindingSet +
-# 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:havok-behavior-reference#Enums | 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. 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.
Line 502: Line 482:
 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: 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:
  
-<code>+<code Lua>
 elseif IsNodeActive("ChargeContinue2_Selector") == TRUE then elseif IsNodeActive("ChargeContinue2_Selector") == TRUE then
     val = "IsEnoughArtPointsR2_4"     val = "IsEnoughArtPointsR2_4"
Line 508: Line 488:
  
 This will make sure the correct hkbVariable is being set to the appropriate value. This will make sure the correct hkbVariable is being set to the appropriate value.
------- 
  
-[[# magic-casting-anims]] +==== Adding a Magic Casting Animation ==== 
-+++ Adding a Magic Casting Animation +TODO
-------+
  
-[[# Advanced-Behavior-Systems]] +===== Advanced Behavior Systems =====
-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). 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).
Line 522: Line 499:
  
 I would like to thank El Fonz0 and NamelessHoodie for implementing and testing the layer blend method outlined here. I would like to thank El Fonz0 and NamelessHoodie for implementing and testing the layer blend method outlined here.
------- 
  
-+++ HalfBlend+==== 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. 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 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: 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 || +Name Variable Name Variable Index Start Bone Index End Bone Index ^ 
-|| TwistMaster || TwistMasterAngle || 49 || 0 || 0 |+| TwistMaster | TwistMasterAngle | 49 | 0 | 0 | 
-|| TwistRootRotYCancel || TwistMasterAngle || 49 || 41 || 43 |+| TwistRootRotYCancel | TwistMasterAngle | 49 | 41 | 43 | 
-|| TwistPelvisCancel || TwistMasterAngle60 || 81 || 8 || 8 |+| TwistPelvisCancel | TwistMasterAngle60 | 81 | 8 | 8 | 
-|| TwistUpper || TwistUpperRootAngle || 48 || 39 || 43 |+| TwistUpper | TwistUpperRootAngle | 48 | 39 | 43 | 
-|| MoveTwistModifier || MoveTwistAngle || 66 || 39 || 78 |+| MoveTwistModifier | MoveTwistAngle | 66 | 39 | 78 | 
-|| TwistR_Thigh || TwistMasterAngle40 || 82 || 25 || 25 |+| TwistR_Thigh | TwistMasterAngle40 | 82 | 25 | 25 | 
-|| TwistL_Thigh || TwistMasterAngle40 || 82 || 10 || 10 ||+| 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.  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 [[[reference:havok-behavior-reference#hkbBoneWeightArray | 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. +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+=== 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. 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. 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 in Hks ===
  
 HalfBlend animations are triggered using the ExecEventHalfBlend function which has the following arguments: HalfBlend animations are triggered using the ExecEventHalfBlend function which has the following arguments:
  
-<code>+<code Lua>
 ExecEventHalfBlend(event_table, blend_type) ExecEventHalfBlend(event_table, blend_type)
 </code> </code>
Line 565: Line 539:
 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. 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.
  
-<code>+<code Lua>
 GESTURE_DEF0 = 22 GESTURE_DEF0 = 22
 GESTURESTART_DEF1 = 0 GESTURESTART_DEF1 = 0
Line 573: Line 547:
 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. 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.
  
-<code>+<code Lua>
 function ExecEventHalfBlend(event_table, blend_type) function ExecEventHalfBlend(event_table, blend_type)
     if blend_type == ALLBODY then     if blend_type == ALLBODY then
Line 600: Line 574:
  
 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. 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.
------- 
  
-[[# BlenderGenerators]] +==== Various uses of Blender Generators ====
-+++ Various uses of Blender Generators+
  
-+++++ Directional Blend+=== 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. 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.
Line 616: Line 588:
  
 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. 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 
- 
  
 +=== ID-Based Selection ===
 +TODO
  
-[[# Layer-Blends]] +==== Layer Blends ==== 
-+++ Layer Blends +TODO
-------+
topic/havok_behavior_editing.1740489249.txt.gz · Last modified: by admin