Credit to the resurrection mod author ionian for helping me with the basics.
be sure to check the Modding Basics Notes
if you have any questions, you can ask on the souls modding discord. link is in the Souls Modding Wiki's "about" section to your right.
What is HKS? put simply, hks handles when animations are allowed to play
to start working on HKS editing:
- download UXM and use it to unpack the game files
- go to the game directory/action/script, and you'll find the hks files (typically the player hks files are labeled c0000)
- download DSLuaDecompiler, and extract the zip to a folder
- take the hks file you wanna edit, copy and paste it into the DSLuaDecompiler folder
- drag the hks to DSLuaDecompiler.exe, and a file with the ".lua" extension will appear and you can open it with something like VScode and start editing.
- when you're done, you can rename the file back to its original name (so you'll be changing its extention back to ".hks", for example, "c0000_transition.dec.lua" to "c0000_transition.hks"), then just put it in \mods\action\script
- to make comment (a personal note, that doesn't count as code or mess with the code) in the hks file, start a new line with a double hyphen "--"
---------------------------------------------------------------------------------------------------
- c0000_cmsg.hks notes:
- cmsg.hks registers animation states, as hks doesn't transition between the animation IDs themselves , but rather from an animation state to an animation event.
- animation IDs are categorized by animation names/event names/CMSG entries...whatever you wanna call them, and each animation event has a corresponding state.
- the hkb states' naming scheme is : HKB_STATE_"animation name", where the animation name used after "HKB_STATE_" is the same as the event name, but without a "W_", there's an underscore between each word, AND the whole thing is in uppercase so "W_CrouchAttackToStand" becomes "HKB_STATE_CROUCH_ATTACK_TO_STAND"
- a CMSG entry can hold multiple animation IDs. this helps give multiple animations the same behavior, without too much clutter
- you'll need this file, if you want to register new animation names through HKB.
- g_paramHkbState (CTRL+F to search it) state types and style types that you assign to hkb states give special properties to their corresponding anim events. I haven't delved more into this part yet
- you can give custom events the same UpdateState as their vanilla variant, if you want them to behave like their vanilla variants
----------------------------------------------------------------------------------------------------
c0000_transition.hks notes:
- transition.hks handles most conditions and functions that decide when animations are allowed to play
SetVariable("StartTime_00", env(3063, 0) / 1000)
- sets the amount of time the animation in "FireEvent" waits for, before it's allowed to play. you specify the time after "StartTime_"
---------------------------------------------------------------------------------------------------
elseif env(1116, SpEffectID) == TRUE then
- this is a conditional statement that checks for the specified SpEffectID number to perform a specified action, like a FireEvent. (you replace "SpEffectID" with the number you specify, so you'll be creating a SpEffect param row with that SpEffectID number you choose, using smithbox. make sure its ID is not used by another speffect).
- FireEvent syntax goes like this: FireEvent("animation name")
you write it beneath conditions like the one I just mentioned, and it's used to make the specified animation play (you specify the animation with its name, not its ID)
- to know the animation name used in FireEvent, open the game, open the debug menu (with insert key), go to GAME -> CHR INS -> WorldChrMan -> c0000 -> Behavior -> Fired Event History, then perform the animation in the game and its name will appear in the debug menu
- another way to find its name is to look up the ID and check under which CMSG object its hkbClipGen is referenced in hkb. if you've read this article, you should know how to search that big ahh hkb file.
---------------------------------------------------------------------------------------------------
- you can check for multiple conditions by putting "and" between each condition.
- you can check for one of several conditions by putting "or" between each condition.
- in the case where there are three conditions, and only one of two of them is required alongside the third, you'll need to set it up like this:
"Required condition" and ("optional condition" or "optional condition")
- in this example, one of the two optional conditions is required, and you MUST put parentheses around the two optional conditions
---------------------------------------------------------------------------------------------------
- if you want the condition for an animation to be after another animation, use the hkb state check.
- hkb state syntax goes like this: if current_hkb_state == HKB_STATE_"animation name" then
- underneath the hkb state condition, you type an action like: FireEvent("animation name")
- as mentioned before, the animation name used after "HKB_STATE_" is the same as the one in FireEvent, but without a "W_", there's an underscore between each word, AND the whole thing is in uppercase
so "W_CrouchAttackToStand" becomes "HKB_STATE_CROUCH_ATTACK_TO_STAND"
---------------------------------------------------------------------------------------------------
- to check for which combat art you have equipped, use this condition: spAtkType == SP_ATK_TYPE_"combat art tae number"
- to check for which prosthetic you have equipped, use this condition: subWeaponCategory == WEP_MOTION_CATEGORY_"prosthetic tae number"
- the tae number can be seen in DSanimStudio. for example, whirlwind slash is a100, sakura dance is a110, shuriken is a070...etc
be sure to type the number without the "a", and make sure it's 3 numbers long (e.g. type 070 instead of 70).
---------------------------------------------------------------------------------------------------
- to check for r1 release (if you want releasing r1 to trigger the action), use this condition: next_behavior == BEH_A_GROUND_RELEASE_ATTACK
- to check for combat art r1 release (only tested with one mind so far), use this condition: next_behavior == BEH_A_GROUND_SP_ATTACK_RELEASE
- next_behavior seems to somehow work differently from other conditions. it's best if you look for an existing next_behavior condition (choose the appropriate one, as there are different ones for different purposes. the two above are an example of that) and type your condition and its action under it
example: underneath "elseif next_behavior == BEH_A_GROUND_SP_ATTACK_RELEASE then", typing the following allows me to do the no-emblem version of one mind, even if I have spirit emblems.
elseif env(1116, YourSpEffectID) == TRUE then
FireEvent("W_GroundSpacialAttackHoldActionNoResource")
- if you find an "if" statement (not elseif) underneath the next_behavior condition, type your elseif condition under it.
- you then create a speffect param with smithbox, open DSanimStudio, find the sheathe anim (a104_316510), put a speffect jumptable on the animation timeline and give it the speffect ID you chose for the speffect param, and whenever the sheathe anim reaches the frames that have the speffect, you can do the no-emblem version of one mind by releasing r1 (could add a playSound jumptable above or below the speffect to signal when the speffect is active)
---------------------------------------------------------------------------------------------------
- if you want to cancel an animation into the unsheathe animation that plays when wolf leaves non-combat areas (like kuro's room), on movement input, type the following underneath FireEvent("W_DeflectGuardMove"):
elseif env(1116, YourSpEffectID) == TRUE then
FireEvent("W_GroundNonCombatAreaMoveLeave")
- after that, make a speffect param and add it to the animation you want in DSAS
---------------------------------------------------------------------------------------------------
- to understand how to integrate your code as additional conditions to an existing code block, you can use this demonstration as an example:
this code handles mist raven follow-up attack:
elseif subWeaponCategory == WEP_MOTION_CATEGORY_074 then
if current_hkb_state == HKB_STATE_LAND_AIR_SUB_ATTACK_MOVE then
FireEvent("W_LandAirSubAttackMoveDeriveAttack")
else
--- other code ---
- and here's how I modified it with a condition that plays a different animation from W_LandAirSubAttackMoveDeriveAttack, if the player has SP_ATK_TYPE_105 (floating passage/spiral cloud passage) or 110 (sakura dance).
elseif subWeaponCategory == WEP_MOTION_CATEGORY_074 then
if current_hkb_state == HKB_STATE_LAND_AIR_SUB_ATTACK_MOVE then
local spAtkType = env(345, HAND_RIGHT)
if spAtkType == SP_ATK_TYPE_105 or spAtkType == SP_ATK_TYPE_110 then
FireEvent("W_LandAirSubAttackMoveDeriveCustomAttack1")
else
FireEvent("W_LandAirSubAttackMoveDeriveAttack")
end
else
--- other code ---
- there are a couple of things to note:
1. local spAtkType = env(345, HAND_RIGHT) seems necessary to allow the game to check for your equipped combat art
2. the combat art condition starts with an if instead of elseif, because it's nested within a bigger if statement, which means it it's a condition that gets checked if its parent condition is checked to be true. it also means that it must end with an "end" statement.
---------------------------------------------------------------------------------------------------
- with certain conditions, you can implement custom button presses for any action you want. for example, if you want to implement Guard input+Prosthetic input:
edit this:
elseif subWeaponCategory == WEP_MOTION_CATEGORY_072 then
if g_forceCrouch == TRUE then
ResetRequest()
elseif currentStyle == STYLE_TYPE_SPRINT or env(3036, SP_EF_REF_ENABLE_SPRINT_ACTION) == TRUE then
FireEvent("W_SprintSubAttack")
elseif env(3036, SP_EF_REF_WEP_FORCE_SUB_ATTACK_RELEASE) == FALSE and env(3036, SP_EF_REF_WEP_ENABLE_SUB_ATTACK_HOLD) == TRUE and enableCombo == TRUE then
FireEvent("W_GroundSubAttackHoldStart")
else
FireEvent("W_GroundSubAttackCombo1")
end
by adding an additional condition that checks for the guard start event or guard loop event. here I'll have it check for the guard loop event:
elseif subWeaponCategory == WEP_MOTION_CATEGORY_072 then
if g_forceCrouch == TRUE then
ResetRequest()
elseif currentStyle == STYLE_TYPE_SPRINT or env(3036, SP_EF_REF_ENABLE_SPRINT_ACTION) == TRUE then
FireEvent("W_SprintSubAttack")
elseif env(3036, SP_EF_REF_WEP_FORCE_SUB_ATTACK_RELEASE) == FALSE and env(3036, SP_EF_REF_WEP_ENABLE_SUB_ATTACK_HOLD) == TRUE and enableCombo == TRUE then
FireEvent("W_GroundSubAttackHoldStart")
elseif current_hkb_state == HKB_STATE_DEFLECT_GUARD_IDLE then
FireEvent("Any event goes here")
else
FireEvent("W_GroundSubAttackCombo1")
end
- with this, the prosthetic attack input plays a different animation, if you press it while holding guard input. keep in mind that this block of code is for WEP_MOTION_CATEGORY_072 (flame vent), so you'll want to add the block input check condition for other prosthetics, if you want it to work for them too. there is also prosthetic input for failing, if you don't have spirit emblems or a prosthetic equipped.
- the WEP_MOTION_CATEGORY number is the same as the tae category number. you can see it with DSAS
----------------------------------------------------------------------------------------------------
- it's ideal to write your code for generic speffect conditions for ground attack anims underneath these lines:
else
FireEvent("W_GroundSubAttackDeriveAttackCombo1")
end
else
FireEvent("W_GroundSubAttackDeriveAttackCombo1")
end
- to find them, just copy the lines and search for them in your text editor with CTRL+F
---------------------------------------------------------------------------------------------------
- script for hks hotloading (allows you to load your hks changes in-game by drinking your gourd without having to close and open it everytime):
local scriptFilepath = "your hks file path in the mods folder"
local aiScript = loadfile(ScriptFilePath)
if aiScript ~= nil then
aiScript()
end
- you type the script underneath the gourd FireEvent in c0000_transition.hks:
SetVariable("StartTime_01", 0)
FireEvent("W_ItemGourdDrink")
- search for this with CTRL+F
- to add more files, simply copy the lines again and put the path for the other files, and increase index number by one
for example: to add a second file,
change "local aiScript", "loadfile(scriptFilepath)", "if aiScript" and "aiScript()"
into "local aiScript1", "loadfile(scriptFilepath1)", "if aiScript1" and "aiScript1()",
and increase the index number by one for each file, so the third file has an index of 2, the fourth has an index of 3, and so on...
- what is the index number? put simply, it's basically the numbering of items in a list. it starts with 0 instead of 1,
that's why the first file you hotload has "local aiScript" instead of "local aiScript1"
- the syntax looks like this, if you want to hotload multiple hks files:
local scriptFilepath = ".hks file path goes here"
local aiScript = loadfile(ScriptFilePath)
local scriptFilepath1 = ".hks file path goes here"
local aiScript1 = loadfile(ScriptFilePath1)
local scriptFilepath2 = ".hks file path goes here"
local aiScript2 = loadfile(ScriptFilePath2)
if aiScript ~= nil then
aiScript()
end
if aiScript1 ~= nil then
aiScript1()
end
if aiScript2 ~= nil then
aiScript2()
end
2 comments
edit;
i searched on 3 different search engines, finally Qwant came through with this possibly dodgy site/link. the exe seems to work as you discuss. it produces a LUA file. scanned the DL with Malwarebytes and says its ok. tested it a couple times.
be warned:
http://soulsmodding.wikidot.com/topics:havok-behavior-editing
most links for modding tools are github links