This tutorial is for Dark Souls III.
The same concepts can be applied with Elden Ring and Sekiro behavior files, however HKXPack does not currently support these two games.
For Elden Ring, you can use HKLib, this however will require a C# program to make use of the library and is not covered in this tutorial.
Tools
To complete this tutorial, the following tools are needed:
- HKXPack
- WitchyBND
- Notepad++ or any other text editor.
Glossary
HKS: means Havok Script, these files are denoted by the .hks file extension.
HavokParam: a section with a decompiled behavior file within <hkparam> tags.
HavokObject: a section with a decompiled behavior file within <hkobject> tags.
Adding a Havok Variable
A havok variable functions similarly to a variable as in any other programming language. However, it's usefulness is that it permits the transfer of information for a HavokScript (such as c0000.hks) into a Behavior file (such as the one contains in c0000.behbnd.dcx).
This allows any of the attributes used within the various Havok classes (such as HavokParam, HavokObject, HavokClipGenerator, etc) to be affected by the c0000.hks.
For example, allowing the 'playbackSpeed' attribute within a HavokClipGenerator to be altered, allowing for animation speed to be controlled within a HavokScript, allowing for dynamic modification of animation speed.
Preparation
First, you will need to unpack the behavior file. During this tutorial, we will be editing the player behavior file, which is c0000.behbnd.dcx. However, this tutorial can be applied to any character behavior file.
1. Unpack the c0000.behbnd.dcx (folder: /Game/chr/) with WitchyBND.
2. Navigate to the c0000.hkx file that contains the behaviors within the unpacked c0000.behbnd.dcx (folder: Behviors/c0000.hkx)
3. Decompile the c0000.hkx file with HKXPack.
4. Open the resulting c0000.xml file with a text editor.
Adding a Variable
To define a havok variable, you will need to extend four HavokParam lists within the behavior file. These are:
- variableInfos
- variableBounds
- wordVariableValues
- variableNames
To do this, simply search for the word within the file. This will bring you to the list within the HavokObject. To extend each list, duplicate the last entry at the bottom of the list, and increment the numelements value by 1. Repeat as necessary if implementing more than one variable.
More detail about each list can be found in the collapsible sections below:
Additionally, you need to define the Havok Variable within the variablenameid.txt file (folder: Game/action/).
Remeber to increment the Num value at the top of the file.
Using a Variable
Having setup a Havok Variable, you now need to make use of it. To do this, you will need to create a HavokVariableBindingSet (hkbVariableBindingSet).
A variable binding set entry should look similar to the following, substituting the #ID, #MEMBERNAME and #VARINDEX text with the appropriate values.
<hkobject class="hkbVariableBindingSet" name="#ID" signature="0xe942f339">
<hkparam name="bindings" numelements="1">
<hkobject>
<hkparam name="memberPath">#MEMBERNAME</hkparam>
<hkparam name="variableIndex">#VARINDEX</hkparam>
<hkparam name="bitIndex">255</hkparam>
<hkparam name="bindingType">BINDING_TYPE_VARIABLE</hkparam>
</hkobject>
</hkparam>
<hkparam name="indexOfBindingToEnable">-1</hkparam>
</hkobject>
#ID is the ID that every havok class object requires. Make sure it is unique.
#VARINDEX is the index position of the variable string within the variableNames list.
For example, the first entry has an index of 0, the next 1, etc. This means the last entry will be (numelements - 1).
#MEMBERNAME is the attribute name (for example: 'playbackSpeed').
In some circumstances, the attribute may be nested within a sublist. To point towards such an attribute, you need to add :<index>/<attribute>, where index is the index of the Havok class object that the attribute resides in. (Example: hands:0/controlData/targetPosition).
Having defined a hkbVariableBindingSet, you can now point to it in any Havok class object by adding it's unique ID to the variableBindingSet attribute of the target Havok class object.
Example of Usage
Here is an example of using where a Havok Variable within the name of "WeaponAnimSpeed" has been defined at the 223 index.
Added hkbVariableBindingSet:
<hkobject class="hkbVariableBindingSet" name="#14995" signature="0xe942f339">
<hkparam name="bindings" numelements="1">
<hkobject>
<hkparam name="memberPath">playbackSpeed</hkparam>
<hkparam name="variableIndex">223</hkparam>
<hkparam name="bitIndex">255</hkparam>
<hkparam name="bindingType">BINDING_TYPE_VARIABLE</hkparam>
</hkobject>
</hkparam>
<hkparam name="indexOfBindingToEnable">-1</hkparam>
</hkobject>
Usage in hkbClipGenerator:
<hkobject class="hkbClipGenerator" name="#530" signature="0xd4cc9f6">
<hkparam name="variableBindingSet">#14995</hkparam>
<hkparam name="userData">0</hkparam>
<hkparam name="name">a020_030000_hkx_AutoSet_00</hkparam>
<hkparam name="animationBundleName"/>
<hkparam name="animationName">a020_030000</hkparam>
<hkparam name="triggers">null</hkparam>
<hkparam name="userPartitionMask">0</hkparam>
<hkparam name="cropStartAmountLocalTime">0.0</hkparam>
<hkparam name="cropEndAmountLocalTime">0.0</hkparam>
<hkparam name="startTime">0.0</hkparam>
<hkparam name="playbackSpeed">1.0</hkparam>
<hkparam name="enforcedDuration">0.0</hkparam>
<hkparam name="userControlledTimeFraction">0.0</hkparam>
<hkparam name="animationBindingIndex">65535</hkparam>
<hkparam name="mode">MODE_SINGLE_PLAY</hkparam>
<hkparam name="flags">0</hkparam>
</hkobject>
Finishing
You should now:
1. Recompile the c0000.xml with HKXPack to produce a new c0000.hkx
2. Replace the c0000.hkx in the decompiled c0000.behbnd.dcx folder with the new c0000.hkx.
3. Repack the c0000.behbnd.dcx.
Usage in Script
Having done the following, the Havok Variable is now in place for usage within the c0000.hks file.
For exaple, you can now add the following code to set the animation speed of all hkbClipGenerators that make use of the hkbVariableBindingSet that targets playbackSpeed and uses the WeaponAnimSpeed variable.
SetVariable("WeaponAnimSpeed", 0.5)
This will multiple the playbackSpeed of all the hkbClipGenerators that use the hkbVariableBindingSet we setup by 0.5, causing them to play at 50% speed.
You can then combine this with conditions to create new in-game effects, here for example a ring could provide +10% weapon swing speed:
--- 111121 is the SpEffect given by the ring
if env(GetSpEffectID, 111121) == TRUE then
SetVariable("WeaponAnimSpeed", 1.1)
end