Hook Reference

Hooks are the main way of interacting with Hollow Knights code, and we can use them to wait for an event to happen in game, then intercept data related to that event and trigger some code and potentially return different data to what we recieved.

Hooks are set up like so ModHooks.Instance.SetPlayerBoolHook += MyCodeThatRunsWhenPlayerDataBoolsAreSet if put this code in public override void Initialize() your MyCodeThatRunsWhenPlayerDataBoolsAreSet function will be ran everytime the game tries to set any of the bool variables in PlayerData

Note

Visual Studio and most other IDEs will autocomplete hook functions for you, in Visual Studio you just need to write ModHooks.Instance.SetPlayerBoolHook += and then press tab and it’ll create a function with the correct type signature.

SetPlayerBool

ModHooks.Instance.SetPlayerBoolHook += PlayerBoolSet

public void PlayerBoolSet( string target, bool val )

This code can be used to check when certain events happen in game such as obtaining a charm by checking if target == "gotCharm_X" where X is the id of the charm

GetPlayerBool

ModHooks.Instance.GetPlayerBoolHook += PlayerBoolGet

public bool PlayerBoolGet( string target )

This can be used to override the games request for bools stored in saves, such as charm equipped states.

//This snippet makes Wayward Compass be permanently equipped.
public bool PlayerBoolGet( string target ){
    if( "target" == "equippedCharm_1") return true;
    return PlayerData.instance.InternalBoolGet(target);
}

Warning

If you use PlayerData.instance.GetBool, instead of InternalBoolGet you’ll call your function again and crash the game.

SetPlayerInt

ModHooks.Instance.SetPlayerIntHook += PlayerIntSet

public void PlayerIntSet( string target, int val )

See SetPlayerBool, but with an int instead of a bool.

GetPlayerInt

ModHooks.Instance.GetPlayerIntHook += PlayerIntGet

public bool PlayerIntGet( string target )

See SetPlayerInt, but with an int instead of a bool.

NewPlayerDataHook

ModHooks.Instance.NewPlayerDataHook += NewPlayerData

public void NewPlayerData( PlayerData instance )

Warning

Do Not Use - Due to a bug in the game this is called way too often.

BlueHealthHook

ModHooks.Instance.BlueHealthHook += BlueHPRestored

public int BlueHPRestored()

This is called whenever PlayerData.UpdateBlueHealth() is called, which handles lifeblood heart/core, but DOES NOT handle Joni’s or lifeblood bugs.

//This snippet makes the lifeblood charms twice as effective.
public int BlueHPRestored(){
    int retValue = 0;
    if(PlayerData.instance.GetBool("equippedCharm_8")) retValue += 2;
    if(PlayerData.instance.GetBool("equippedCharm_9")) retValue += 4;
    return retValue;
}

Note

It’s important to note here the use of PlayerData.instance.GetBool(“equippedCharm_8”) instead of the simpler PlayerData.instance.equippedCharm_8 this is to maximize mod compatibility. If another mod overrides the equippedCharm_8 for whatever reason we want to respect that.

TakeHealthHook

ModHooks.Instance.TakeHealthHook += TakeDamage

public int TakeDamage( int damage )

This is called whenver the Knight takes damage via the HeroController.TakeHealth function.

//This snippet doubles all damage dealt to the Knight
public int TakeDamage( int damage ){
    return damage*2;
}
//This snippet forces the Knight to die if he takes any damage.
public int TakeDamage( int damage ){
    PlayerData.instance.health = 0;
    return damage;
}

AfterTakeDamageHook

ModHooks.Instance.AfterTakeDamageHook += AfterTakeDamage

public int AfterTakeDamage( int hazardType, int damageAmount )

This is called in HeroController.TakeDamage after invincibility is accounted for, but before damage is applied.

Note

Invincibility in this case doesn’t include Baldur Shell, so that could negate damage after this hook is called.

BeforePlayerDeadHook

ModHooks.Instance.BeforePlayerDeadHook += BeforePlayerDied

public void BeforePlayerDied()

Called at the beginning of the GameManger.PlayerDead function.

AfterPlayerDeadHook

ModHooks.Instance.AfterPlayerDeadHook += AfterPlayerDied

public void AfterPlayerDied()

Called at the end of the GameManger.PlayerDead function.

AttackHook

ModHooks.Instance.AttackHook += OnAttack

public void OnAttack( AttackDirection dir )

Called whenever the Knight attacks, this is called at the beginning of HeroController.Attack.

DoAttackHook

ModHooks.Instance.DoAttackHook += OnDoAttack

public void OnDoAttack()

Called whenever the Knight attacks, this is called at the beginning of HeroController.DoAttack, effectively identical to AttackHook except its called before attack_cooldown is set.

AfterAttackHook

ModHooks.Instance.AfterAttackHook += AfterAttack

public void AfterAttackHook( AttackDirection dir )

Called whenever the Knight attacks, this is called at the end of HeroController.Attack, this is called after all the objects related to the slash have been created, possible uses include recolouring/resizing the Knights slash SFX or conditionally setting nail damage.

SlashHitHook

ModHooks.Instance.SlashHitHook += OnSlashHit

public void OnSlashHit( Collider2D otherCollider, GameObject gameObject )

Called whenever the nail hits anything with a collider, this includes doors, levers, npcs and of course enemies. Potential uses include overriding the 0 damage feature the game has to allow 0 damage nail mods to hit levers.

CharmUpdateHook

ModHooks.Instance.CharmUpdateHook += OnCharmUpdate

public void OnCharmUpdate()

This is called after any charm is equipped and if that charms effects are done in code (Which at the time of writing are Nailmaster’s Glory, Fragile Heart, Joni’s Blessing and Carefree Melody) it’ll be called after their effects run.

//This snippet buffs Joni's Blessing back to 1.5x max HP and also fixes it to use PlayerData.instance.GetBool()
public void OnCharmUpdate() {
    if (PlayerData.instance.GetBool("equippedCharm_27")){
        PlayerData.instance.SetInt("joniHealthBlue", (int)((float)PlayerData.instance.GetInt("maxHealthBase") * 1.5f));
        PlayerData.instance.MaxHealth();
    }
}

HeroUpdateHook

ModHooks.Instance.HeroUpdateHook += OnHeroUpdate

public void OnHeroUpdate()

This is called everyframe, as long as the Knight exists. Useful for running code every frame during gameplay without having to create a monobehaviour.

BeforeAddHealthHook

ModHooks.Instance.BeforeAddHealthHook += BeforeAddHealth

public int BeforeAddHealth( int amount )

This is called before health is added to the Knights health pool, amount is the amount to be added to the health pool.

//This snippet makes healing cost 100 geo.
public int BeforeAddHealth( int amount ) {
    if( PlayerData.instance.GetBool("geo") > 100 ){
        PlayerData.instance.geo -= 100;
        return amount;
    }
    return 0;
}

HeroUpdateHook

ModHooks.Instance.HeroUpdateHook += OnHeroUpdate

public void OnHeroUpdate()

This is called everyframe, as long as the Knight exists. Useful for running code every frame during gameplay without having to create a monobehaviour.

FocusCostHook

ModHooks.Instance.FocusCostHook += FocusCostCalc

public float FocusCostCalc()

This is called when you begin to heal, and returns a float that multiplies how much soul is drained.

//This snippet makes focusing use all your soul.
//note to make this work with less than 33 soul, you will need to change the focus MP amount
public float FocusCostCalc(){
    return (float)PlayerData.instance.GetInt("MPCharge") / 33.0f;
}

SoulGainHook

ModHooks.Instance.SoulGainHook += GainSoul

public int GainSoul( int amount )

This is called whenever the Knight gains souls from hitting an enemy with his nail, it passes it the amount it would gain and returns how much it will gain.

//This snippet doubles soul gain from nail hits whenever the knight is on 1 hp.
public int GainSoul( int amount ){
    if( PlayerData.instance.GetInt("health") == 1 )
       return amount * 2;
    return amount;
}

DashVectorHook

ModHooks.Instance.DashVectorHook += ChangeDashVel

public Vector2 ChangeDashVel( Vector2 velocity )

Sets the velocity of a dash, called every frame.

//This snippet makes you accelerate during dashes
public Vector2 ChangeDashVel( Vector2 velocity ){
    return velocity * (1 + (2*time.deltaTime));
}

DashPressedHook

ModHooks.Instance.DashPressedHook += DashPressed

public bool DashPressed()

Called whenever dash is pressed, return overrides vanilla inputs: true to not dash, false to dash.

//This snippet makes it only possible to dash if you are also rising in your jump.
public bool DashPressed(){
    return !HeroController.instance.cState.jumping;
}

Note

It might seem weird to NOT dash if you return true, but the intended functionality is for modded implementations of the entire dash, in which case you’d want to disable vanilla code entirely, and becaue of this you return true if you have a modded implementation.

SavegameLoadHook

ModHooks.Instance.SavegameLoadHook += SaveLoaded

public void SaveLoaded( int id )

Called directly after a save is loaded, id is the save slot of the loaded file.

SavegameSaveHook

ModHooks.Instance.SavegameSaveHook += SaveSaved

public void SaveSaved( int id )

Called directly after a save is saved, id is the save slot of the saved file.

NewGameHook

ModHooks.Instance.NewGameHook += NewGameStarted

public void NewGameStarted( int id )

Called directly after a new game is started, id is the save slot of the created file.

SavegameClearHook

ModHooks.Instance.SavegameClearHook += SaveDeleted

public void SaveDeleted( int id )

Called directly before a save is deleted, id is the save slot of the file to be deleted.

AfterSavegameLoadHook

ModHooks.Instance.AfterSavegameLoadHook += AfterSaveLoaded

public void AfterSaveLoaded( Patches.SaveGameData data )

Called directly after a save is loaded, however gives you access to the SaveGameData.

BeforeSavegameSaveHook

ModHooks.Instance.BeforeSavegameSaveHook += BeforeSave

public void BeforeSave( Patches.SaveGameData data )

Called directly before a save is saved and allows you to edit the data that will be written to the save file.

GetSaveFileNameHook

ModHooks.Instance.GetSaveFileNameHook += SaveName

public string SaveName( int saveSlot )

Used to have custom filenames, designed to allow per mod savefiles but can also be used to disable saves or add more saveslots.

public string SaveName( int saveSlot ){
    return "myModName" + saveSlot.toString() + ".dat";
}

AfterSaveGameClearHook

ModHooks.Instance.AfterSaveGameClearHook += AfterSaveCleared

public void AfterSaveCleared( int saveSlot )

Called after a save has been deleted, saveSlot is the slot of the deleted save.

LanguageGetHook

ModHooks.Instance.LanguageGetHook += LanguageGet

public string LanguageGet( string key, string sheet )

Called whenever a localized string is requested, useful for injecting custom text into the game

public string LanguageGet( string key, string sheet )
    if( key == "myKey" )
        return "some profound text";
    return Language.GetInternal(key, sheet);
}
public string LanguageGet( string key, string sheet )
    return "KEY: " + key + " displays this text: " + Language.GetInternal(key, sheet);
}

Note

To find out what keys point to what strings of text, you can either use a unity asset extractor such as UABE and look for EN_*.txt, or putting logging inside your hook.

SceneChanged

ModHooks.Instance.SceneChanged += SceneLoaded

public void SceneLoaded( string targetScene )

Called after a scene is loaded, scene is unity’s internal name for levels and is any area in game aswell as several cutscenes and the main menu.

Note

It’s recommended to use the internal SceneLoaded hook instead of the the api’s by using UnityEngine.SceneManagement.SceneManager.sceneLoaded However you’ll probably want to delay this by a frame by using a coroutine.

BeforeSceneLoadHook

ModHooks.Instance.BeforeSceneLoadHook += BeforeSceneLoad

public string BeforeSceneLoad( string sceneName )

Called directly before a scene starts to load, returning a different string will load a different scene.

Note

If you transition normally, the game expects a scene the Knight could possibly access from that direction, i.e. if you left a room on the right the new scene requires an entrance on the left, dreamgating is glitchy as expected, and going into dreams requires a scene with an object to say where to spawn.

CursorHook

ModHooks.Instance.CursorHook += Cursor

public void Cursor()

Called whenever the game tries to show the cursor.

ColliderCreateHook

ModHooks.Instance.ColliderCreateHook += ColliderCreated

public void ColliderCreated( GameObject go )

Called whenever a gameobject is created with both a PlayMakerFSM and a Collider2D, useful for obtaining references to gameplay objects.

ObjectPoolSpawnHook

ModHooks.Instance.ObjectPoolSpawnHook += ObjectCreated

public GameObject OnObjectPoolSpawn( GameObject go )

Called whenever the game attempts to create a gameobject.

Warning

This happens a lot and you should be careful about the complexity of code contained in your hook.

OnGetEventSenderHook

ModHooks.Instance.OnGetEventSenderHook += SpellCreated

public GameObject SpellCreated( GameObject go, HutongGames.PlayMaker.Fsm fsm )

Called whenever OnGetEvent fsm action is ran, this action only exists inside of attacks/spells so is a decent way to get references to them.

Note

This has several issues with the games global object pool, and anything you try to do will likely not work how you intend to without major work arounds.

ApplicationQuitHook

ModHooks.Instance.ApplicationQuitHook += ApplicationQuit

public void ApplicationQuit()

Called when the game is closed, probably no general use for this, it was implemented for the PlayerDataTracker so a websocket message could be sent whenever the game was closed.

Note

This code won’t run if the game unexpectedly terminates.

SetFontHook

ModHooks.Instance.SetFontHook += FontSet

public void FontSet()

Called whenever the game changes language with a different font.

TextDirectionHook

ModHooks.Instance.TextDirectionHook += GetTextDirection

public bool GetTextDirection( bool direction )

Called when the text renderer requests write/read direction of text. Useful for fan translations to RTL languages.

HitInstanceHook

ModHooks.Instance.HitInstanceHook += HitInstanceCreated

public HitInstance HitInstanceCreated( HutongGames.PlayMaker.Fsm owner, HitInstance hitInst )

Called whenever a hit instance is created, hit instances are used to set the damage and damage type of any collisions that occur, so you can override this to set damage on awkward to adjust values such as flukenest damage.

DrawBlackBordersHook

ModHooks.Instance.DrawBlackBordersHook += BlackBordersDrawn

public void BlackBordersDrawn( List<GameObject> borders )

Called when the SceneManager loads a scene, specifically as it calls DrawBlackBorders, passes references to all the objects used to create the border. Useful to delete the vignette if its not your style.

OnEnableEnemyHook

ModHooks.Instance.OnEnableEnemyHook += EnemyEnabled

public bool EnemyEnabled( GameObject enemy, bool isDead )

Called whenever an enemy attempts to spawn, if isDead is true this enemy is persistent and has already been killed, return isDead to mimic default behaviour.

//This snippet will make all enemies spawn regardless of if they have been previously defeated (except bosses).
public bool EnemyEnabled( GameObject enemy, bool isDead )
    return false;
}

OnRecieveDeathEventHook

ModHooks.Instance.OnRecieveDeathEventHook += EnemyDied

public void EnemyDied( EnemyDeathEffects enemyDeathEffects, bool eventAlreadyRecieved, ref float? attackDirection, ref bool resetDeathEvent, ref bool spellBurn, ref bool isWatery )

Called whenever an enemy dies.

Note

Possibly triggered multiple times per death, be careful not to trigger code more often than expected.

OnRecordKillForJournalHook

ModHooks.Instance.OnRecordKillForJournalHook += RecordJournalKill

public void RecordJournalKill( EnemyDeathEffects enemyDeathEffects, string playerDataName, string killedBoolPlayerDataLookupKey, string killCountIntPlayerDataLookupKey, string newDataBoolPlayerDataLookupKey )

Called whenever an enemy dies, and it’s recorded to the journal. The strings are lookup values you can use with PlayerData.GetBool()/PlayerData.GetInt() to obtain relevant data from the save data.