Site Tools


topic:havok_behavior_editing

Differences

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

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
topic:havok_behavior_editing [2025/02/25 14:03] admintopic:havok_behavior_editing [2025/02/27 00:44] (current) admin
Line 47: Line 47:
 === Generators === === Generators ===
  
-Generators are the most common type of behavior object. As mentioned previously, [[[*reference:havok_behavior_editing#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_editing#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_editing#hkbScriptGenerator | hkbScriptGenerators]]], which are used to attach hks functions such as update, activate or deactivate functions to all child states and [[[reference:havok_behavior_editing#hkbBlenderGenerator | hkbBlenderGenerators]]] and [[[reference:havok_behavior_editing#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_editing#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_editing#CustomLookAtTwistModifier | CustomLookAtTwistModifiers]]], and IK. Also present are [[[reference:havok_behavior_editing#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_editing#hkbModifierGenerator | hkbModifierGenerator]]] which specifies a child generator and a modifier. Often this modifier is an [[[reference:havok_behavior_editing#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.
Line 63: Line 63:
 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_editing#hkbBlendingTransitionEffect | hkbBlendingTransitionEffects]]] and [[[reference:havok_behavior_editing#CustomTransitionEffect | CustomTransitionEffects]]], with the default transition effect used for the player being CustomTransitionEffect #292. Transition effects are applied in [[[reference:havok_behavior_editing#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_editing | 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_editing#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 ====
Line 89: Line 89:
 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>+<code Lua>
 env(ID, args) env(ID, args)
 </code> </code>
Line 103: Line 103:
 The function has the following syntax: The function has the following syntax:
  
-<code>+<code Lua>
 act(ID, args) act(ID, args)
 </code> </code>
Line 111: Line 111:
 ==== onUpdate ==== ==== onUpdate ====
  
-Each CMSG, given a unique [[[reference:havok_behavior_editing#CMSG | userData]]] value and provided the "enableScripthkparam is enabled, automatically creates its own onUpdate function named [name of [[[reference:havok_behavior_editing#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>+<code Lua>
 function SMSIName_onUpdate() function SMSIName_onUpdate()
     if SomeCommonFunction(args) == TRUE then     if SomeCommonFunction(args) == TRUE then
Line 126: Line 126:
 As an example the first one-handed r1 attack: As an example the first one-handed r1 attack:
  
-<code>+<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 139: 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_editing#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_editing | 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 an Existing Animation ID for a TAE Section ==== ==== Registering an Existing Animation ID for a TAE Section ====
Line 153: Line 153:
 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_editing#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>+<code Lua>
 <hkparam name="animId">30000</hkparam> <hkparam name="animId">30000</hkparam>
 </code> </code>
Line 165: 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_editing#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:
Line 181: 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_editing#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>+<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 215: 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_editing#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.
Line 231: Line 231:
 === 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_editing#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_editing#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_editing#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_editing#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_editing#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:
Line 263: 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_editing#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_editing#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.
Line 285: Line 285:
 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 297: 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 307: 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 313: 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 329: 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 338: 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 352: Line 352:
 ==== Creating Weapon Art Behaviors ==== ==== Creating Weapon Art Behaviors ====
  
-Weapon art behaviors are a simple example of how [[[reference:havok_behavior_editing#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 364: Line 364:
 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_editing#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 ^
Line 379: Line 379:
 === hkbVariableBindingSets === === hkbVariableBindingSets ===
  
-Variables are bound to parameters using [[[reference:havok_behavior_editing#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. 
Line 391: 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 399: 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 409: 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 432: 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 441: 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 465: Line 465:
 === Creating hkbVariables and Setting Them in Hks === === Creating hkbVariables and Setting Them in Hks ===
  
-As mentioned in the [#Structure 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:+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   - Add entries to the "variableInfos", "variableBounds", "wordVariableValues" and "variableNames" hkparams
   - Reference the variable from an hkbVariableBindingSet   - Reference the variable from an hkbVariableBindingSet
   - Set the variable in hks.   - 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_editing#Enums | reference page]]] for possible values. In this case we will use "VARIABLE_TYPE_INT16" like the other weapon art selector variables.+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.
  
 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 482: 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 519: Line 519:
 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_editing#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 ===
Line 531: Line 531:
 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 539: 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 547: 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
topic/havok_behavior_editing.1740492202.txt.gz · Last modified: by admin