This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| topic:havok_behavior_editing [2025/02/25 13:59] – admin | topic: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 [[havok_behavior_reference# | + | For simple edits such as registering animations or creating new [[common-refmat: |
| To directly edit behavior hkx files as outlined here, get [[https:// | To directly edit behavior hkx files as outlined here, get [[https:// | ||
| Line 15: | Line 15: | ||
| 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' | 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' | ||
| - | === Structure === | + | ==== Structure |
| All hkx packfiles consist of an hksection called '' | All hkx packfiles consist of an hksection called '' | ||
| Line 35: | Line 35: | ||
| 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, | 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, | ||
| - | === Naming Conventions === | + | ==== 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 [[[havok_behavior_reference | reference page]]] for the suffixes used for each class. | + | See the [[common-refmat: |
| - | === 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 [[[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: |
| - | == 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 | + | 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, |
| - | [[[reference: | + | '' |
| - | == Modifiers == | + | === Modifiers |
| - | Modifiers are mostly used at root level for character head rotation, in the form of [[[reference: | + | Modifiers are mostly used at root level for character head rotation, in the form of '' |
| - | Modifiers are applied to generators using an [[[reference: | + | Modifiers are applied to generators using an '' |
| 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: | + | The most commonly used transition effect objects are '' |
| - | === Classes and HkParams === | + | ==== Classes and HkParams |
| - | See [[[*reference:havok_behavior_editing | + | See [[common-refmat:havok_behavior_reference |
| ===== 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 | + | Havok script files are lua scripts which controls which behaviors are triggered when. It is also where [[common-refmat:havok_behavior_reference#hkbVariables |
| - | 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. | + | 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. |
| - | 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:// | + | 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:// |
| - | === CommonFunctions === | + | ==== CommonFunctions |
| - | CommonFunctions are the functions used in [# | + | CommonFunctions are the functions used in [[havok_behavior_editing#onUpdate]] functions to interpret player inputs. Generally all substates of the '' |
| - | === Exec === | + | ==== Exec ==== |
| - | Exec functions check for player request inputs and execute animations by calling the lower level ExecEvent functions | + | Exec functions check for player request inputs and execute animations by calling the lower level ExecEvent functions |
| - | [[# 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: | ||
| - | < | + | < |
| env(ID, args) | env(ID, args) | ||
| </ | </ | ||
| Line 98: | Line 97: | ||
| 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 104: | Line 103: | ||
| The function has the following syntax: | The function has the following syntax: | ||
| - | < | + | < |
| act(ID, args) | act(ID, args) | ||
| </ | </ | ||
| Line 110: | Line 109: | ||
| 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 | + | Each CMSG, given a unique userData value and provided the '' |
| Most basic onUpdate functions have the following form: | Most basic onUpdate functions have the following form: | ||
| - | < | + | < |
| 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: | ||
| - | < | + | < |
| function AttackRightLight1_onUpdate() | function AttackRightLight1_onUpdate() | ||
| if AttackCommonFunction(" | if AttackCommonFunction(" | ||
| Line 141: | Line 139: | ||
| AttackCommonFunction/ | AttackCommonFunction/ | ||
| - | As mentioned previously, update functions can also be applied at any point in the behavior hierarchy to all child states using an [[[reference: | + | 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 | + | 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 |
| 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:// | + | All examples here except for adding magic casting animations are supported by my tool [[https:// |
| - | [[# 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:// | + | First, unpack your hkx behavior file using [[https:// |
| 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: | + | Once we have opened the behavior file we will want to find the CustomManualSelectorGenerator, |
| - | < | + | < |
| <hkparam name=" | <hkparam name=" | ||
| </ | </ | ||
| Line 168: | 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: | ||
| - | < | + | < |
| " | " | ||
| </ | </ | ||
| - | Once we have located our CMSG of choice, in this case #528, we need to add an [[[*reference: | + | Once we have located our CMSG of choice, in this case '' |
| 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 | + | |
| - | # find the next available name ID and set your hkobject' | + | |
| - | # 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 185: | 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 " | + | 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 " |
| - | It is unclear what the "name" | + | It is unclear what the '' |
| - | 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 '' |
| - | 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 '' |
| As stated previously, hkxpack will make sure your IDs are sequential and rename hkobjects and references to them accordingly, | As stated previously, hkxpack will make sure your IDs are sequential and rename hkobjects and references to them accordingly, | ||
| - | 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 '' |
| Here is the final version of our new hkbClipGenerator: | Here is the final version of our new hkbClipGenerator: | ||
| - | < | + | < |
| < | < | ||
| <hkparam name=" | <hkparam name=" | ||
| Line 219: | Line 215: | ||
| </ | </ | ||
| - | 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 "#" | + | 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 '' |
| - | 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 | + | 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 '' |
| You will notice that the " | You will notice that the " | ||
| Line 227: | Line 223: | ||
| Once you have done all that, repack your behavior file using hkxpack-souls. Congratulations, | Once you have done all that, repack your behavior file using hkxpack-souls. Congratulations, | ||
| - | === 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. | ||
| Line 233: | Line 229: | ||
| As we saw when adding hkbClipGenerators, | As we saw when adding hkbClipGenerators, | ||
| - | == 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" | + | 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 '' |
| - | 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 | + | 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 |
| - | 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 | + | 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# |
| - | 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 '' |
| - | The userData hkparam must be unique for each state, otherwise our [#onUpdate | + | 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, |
| - | 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, | + | 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 '' |
| 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 | + | 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 |
| - | I will not cover the hkparams of hkbStateMachines in depth in this example, please refer to the [[[reference:havok_behavior_editing# | + | I will not cover the hkparams of hkbStateMachines in depth in this example, please refer to the [[common-refmat:havok_behavior_reference |
| - | The "states" | + | The '' |
| - | The hkparam responsible for selecting the state is the " | + | The hkparam responsible for selecting the state is the " |
| 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 | + | |
| 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 268: | 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. | 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. | ||
| - | 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 " | + | 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 " |
| - | HkbStateMachineTransitionInfoArrays consist of a single hkparam called " | + | HkbStateMachineTransitionInfoArrays consist of a single hkparam called " |
| After creating our new transition info object within the " | After creating our new transition info object within the " | ||
| - | 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: | + | 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 '' |
| 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 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: | ||
| - | < | + | < |
| function AttackRightLight3_onUpdate() | function AttackRightLight3_onUpdate() | ||
| if AttackCommonFunction(" | if AttackCommonFunction(" | ||
| Line 302: | 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: | ||
| - | < | + | < |
| AttackCommonFunction(r1, | AttackCommonFunction(r1, | ||
| </ | </ | ||
| - | 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 [# | + | 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, |
| If we wanted to implement our 4th r1 for all weapons, we would need to change the first argument of the function (" | If we wanted to implement our 4th r1 for all weapons, we would need to change the first argument of the function (" | ||
| Line 312: | Line 307: | ||
| The [#env env] to check for the override tae section of the player' | The [#env env] to check for the override tae section of the player' | ||
| - | < | + | < |
| env(" | env(" | ||
| </ | </ | ||
| Line 318: | Line 313: | ||
| Since this is a right hand attack we want to get the right hand weapon' | Since this is a right hand attack we want to get the right hand weapon' | ||
| - | < | + | < |
| function AttackRightLight3_onUpdate() | function AttackRightLight3_onUpdate() | ||
| local r1 = " | local r1 = " | ||
| Line 334: | 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: | ||
| - | < | + | < |
| local hand = HAND_RIGHT | local hand = HAND_RIGHT | ||
| if c_Style == HAND_LEFT_BOTH then | if c_Style == HAND_LEFT_BOTH then | ||
| Line 343: | 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 " | 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 " | ||
| - | < | + | < |
| function AttackRightLight4_onUpdate() | function AttackRightLight4_onUpdate() | ||
| if AttackCommonFunction(" | if AttackCommonFunction(" | ||
| Line 355: | Line 350: | ||
| 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: | + | 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' | 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' | ||
| Line 366: | Line 360: | ||
| 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, | In this example we will create a simple single-move, | ||
| - | 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: | + | 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 " |
| ^ Hkparam ^ Value ^ | ^ Hkparam ^ Value ^ | ||
| Line 384: | Line 377: | ||
| 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 " | 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 " | ||
| - | == hkbVariableBindingSets == | + | === hkbVariableBindingSets |
| - | Variables are bound to parameters using [[[reference: | + | 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 " |
| To bind the " | To bind the " | ||
| Line 392: | Line 385: | ||
| The best way to obtain the index of a variable from its name is to find the name definition under the " | The best way to obtain the index of a variable from its name is to find the name definition under the " | ||
| - | == 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 398: | Line 391: | ||
| Looking at common_define we can see that each actionId has three corresponding global variables. One called " | Looking at common_define we can see that each actionId has three corresponding global variables. One called " | ||
| - | < | + | < |
| SWORDARTS_PARRY = 1 | SWORDARTS_PARRY = 1 | ||
| SWORDARTS_REQUEST_LEFT_PARRY = 101 | SWORDARTS_REQUEST_LEFT_PARRY = 101 | ||
| Line 406: | 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: | ||
| - | < | + | < |
| SWORDARTS_NEW = 37 | SWORDARTS_NEW = 37 | ||
| SWORDARTS_REQUEST_LEFT_NEW = 137 | SWORDARTS_REQUEST_LEFT_NEW = 137 | ||
| Line 416: | 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, | 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, | ||
| - | < | + | < |
| function GetSwordArtsRequest() | function GetSwordArtsRequest() | ||
| local style = c_Style | local style = c_Style | ||
| Line 439: | Line 432: | ||
| The variable arts_id corresponds to the actionId from the SwordArtsParam and thus our variable " | The variable arts_id corresponds to the actionId from the SwordArtsParam and thus our variable " | ||
| - | < | + | < |
| 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(" | ExecEventAllBody(" | ||
| Line 448: | 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. | ||
| - | < | + | < |
| function ChargeContinue3_onUpdate() | function ChargeContinue3_onUpdate() | ||
| if SwordArtsCommonFunction(" | if SwordArtsCommonFunction(" | ||
| Line 464: | Line 457: | ||
| </ | </ | ||
| - | == 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. | ||
| Line 470: | Line 463: | ||
| 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 " | 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 " | ||
| - | == Creating hkbVariables and Setting Them in Hks == | + | === Creating hkbVariables and Setting Them in Hks === |
| - | As mentioned in the [# | + | 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 " | ||
| + | - Reference the variable from an hkbVariableBindingSet | ||
| + | - Set the variable in hks. | ||
| - | # Add entries to the " | + | First, we will look for the " |
| - | # Reference the variable from an hkbVariableBindingSet | + | |
| - | # Set the variable in hks. | + | |
| - | + | ||
| - | First, we will look for the " | + | |
| Next we will set the minimum and maximum values of our variable in the " | Next we will set the minimum and maximum values of our variable in the " | ||
| Line 490: | 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 " | 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 IsNodeActive(" | elseif IsNodeActive(" | ||
| val = " | val = " | ||
| Line 497: | Line 489: | ||
| 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 | TODO | ||
| - | [[# Advanced-Behavior-Systems]] | ||
| ===== Advanced Behavior Systems ===== | ===== Advanced Behavior Systems ===== | ||
| Line 510: | Line 500: | ||
| 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, " | HalfBlend is a state machine under Master_SM with 2 child SMs, " | ||
| - | ^ 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 | | ||
| Line 529: | 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 " | 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 " | ||
| - | The hkbBlenderGenerator " | + | The hkbBlenderGenerator " |
| - | == 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: " | When adding half blend animations to existing state machines you want to make two versions of the same CMSG, named as follows: " | ||
| Line 537: | Line 527: | ||
| To add state machines to HalfBlend_SM a specific system of variable binding must be observed. Each state machine after HalfBlend_SM, | To add state machines to HalfBlend_SM a specific system of variable binding must be observed. Each state machine after HalfBlend_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: | ||
| - | < | + | < |
| ExecEventHalfBlend(event_table, | ExecEventHalfBlend(event_table, | ||
| </ | </ | ||
| Line 549: | Line 539: | ||
| Event tables consist of the base event name and a state Id for every state machine between HalfBlend_SM and the behavior' | Event tables consist of the base event name and a state Id for every state machine between HalfBlend_SM and the behavior' | ||
| - | < | + | < |
| GESTURE_DEF0 = 22 | GESTURE_DEF0 = 22 | ||
| GESTURESTART_DEF1 = 0 | GESTURESTART_DEF1 = 0 | ||
| Line 557: | 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. | ||
| - | < | + | < |
| function ExecEventHalfBlend(event_table, | function ExecEventHalfBlend(event_table, | ||
| if blend_type == ALLBODY then | if blend_type == ALLBODY then | ||
| Line 585: | Line 575: | ||
| As we can see the name of the upper event is obtained from the event table by appending " | As we can see the name of the upper event is obtained from the event table by appending " | ||
| - | [[# 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 " | 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 " | ||
| Line 600: | Line 589: | ||
| 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 " | 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 " | ||
| - | == ID-Based Selection == | + | === ID-Based Selection |
| TODO | TODO | ||
| - | + | ==== Layer Blends | |
| - | [[# Layer-Blends]] | + | |
| - | === Layer Blends === | + | |
| TODO | TODO | ||