ModHooks
ModHooks are hooks that are built into the Modding API that allow us to interact will Hollow Knight code. To use a Modhook, you need to subscribe to the hook. For example:
public class MyFirstMod:Mod
{
// The method that will be called by Modding API when the game first opens
public override void Initialize()
{
// We subscribe to HeroUpdateHook which is an hook that is triggered when the 'Update' Function is called for the player (once every frame)
ModHooks.HeroUpdateHook += OnHeroUpdate;
}
//This event's method doesn't take any Parameters. normally the IDE can generate this function for you with the correct parameters
public void OnHeroUpdate()
{
//code to run
}
}
Note: Your IDE (Visual Studio Community/Jetbrains Rider) can generate this function for you with the correct parameters. To do this, see example video or type in
ModHooks.HeroUpdateHook += OnHeroUpdate;
, Then right click on the now red highlightedOnHeroUpdate
and click on the light bulb icon (called 'Quick actions and Refactoring') and choose 'Generate Method'.
There are many modhooks available to be used.
Here you can find a list of some hooks and an explanation of what they do and how to use it.
However, not all hooks are listed here. Please refer to the API Documentation for a list of all hooks.
HeroUpdateHook
This event is called in HeroController.Update
.
It can be used to run code every frame the player exists. Useful for running code every frame during gameplay without having to create a MonoBehaviour. Can be used to check for conditions and run code (example: check Input.GetKeyDown(KeyCode.Space))
and run code depending on that.
ModHooks.HeroUpdateHook += HeroUpdate;
public string HeroUpdate()
{
if Input.GetKeyDown(KeyCode.Space))
{
Log("Space was pressed");
}
}
LanguageGetHook
This hook is the main way to change ingame text. The way it works is that each time the game wants to find what text to display, it uses Language.Language.Get
(which is where this hook is called) to get the text based on the text's "key". The text is also organized in sheets so the key and sheet uniquely identify the text. To see what key and sheet a text is in, refer to the full list of all keys and sheets. or add logging to the hook To use the hook, check the key
and sheetTitle
and return the new text you want to display if the key/sheetTitle is the one you want to change. Alternatively, you can use orig
to check for text and return new text. However this is not recommended because it may cause unexpected text changes. An example for both is given below
ModHooks.LanguageGetHook += LanguageGet;
public string LanguageGet(string key, string sheetTitle, string orig)
{
//Check for the key and sheet for MainMenu "Yes" text
if (key == "NAV_YES" && sheetTitle == "MainMenu")
{
//return the new text you want to display.
return "Yee";
}
//a way to replace all Yes with Yee
if (orig.Contains("Yes"))
{
return orig.Replace("Yes", "Yee");
}
//make sure to return orig for all the other texts you dont want to change
return orig;
}
This hook can also be used to return text for custom keys you create. (An example being using SFCore to add custom charms). To do this, check for if(key == "MyCustomKey") return "MyCustomText
.
Parameter type | Parameter name | Explanation |
---|---|---|
string | key | The key of the text in the sheet. This combined with sheetTitle is the way the game uniquely identifies the text required to be shown |
string | sheetTitle | The sheet that the text is in |
string | orig | The original string. To be returned if nothing is changed |
Return type | Explanation |
---|---|
string | The new text that should be displayed. return orig if no changes are required |
BeforeSceneLoadHook
Note: This hook should normally NOT be used for the following reasons:
- It only allows you to change scene name and not other relevant data (like entry gate name)
- If your goal is to only see what new scene is being entered, use
UnityEngine.SceneManagement.SceneManager.activeSceneChanged
instead- If your goal is to see what scene is being entered and change it, use
On.GameManager.BeginSceneTransition
instead (because it can solve problem 1).
This event is called in GameManager.BeginSceneTransition
. It is called before a new scene is loaded and allows you to change what scene is loaded. However it is not recommended to use (see note above).
ModHooks.BeforeSceneLoadHook += BeforeSceneLoad;
public string BeforeSceneLoad(string newSceneName)
{
Log($"new scene is {newSceneName}");
return newSceneName;
}
Parameter type | Parameter name | Explanation |
---|---|---|
string | newSceneName | The name of the new scene to be loaded |
Return type | Explanation |
---|---|
string | The new scene that the game should load instead |
SavegameLoadHook and NewGameHook
Although these 2 hooks together can provide the ability to check for save opened, it is best to avoid these two hooks because
- NewGameHook is not called by Randomizer when creating a new game
- Using both hooks is clunky in order to check for save opened
Instead we would recommend to use
On.UIManager.StartNewGame
to check for new game loadOn.HeroController.Awake
to check for save opened
FinishedLoadingModsHook
This event is called after all mod's Initialize have been called. Useful to run code that is dependant on other mods being loaded.
ModHooks.FinishedLoadingModsHook += FinishedLoadingMods;
public string FinishedLoadingMods()
{
//checks whether the mod named DebugMod exists
if (ModHooks.GetMod("DebugMod") is Mod)
{
Log("Debug Mod exists");
}
}
AfterTakeDamageHook
This event is called in HeroController.TakeDamage
. It is called after invincibility is accounted for, but before damage is applied. Can be used to change the amount of damage taken by player
Note: Unlike the 1.4 MAPI, AfterTakeDamageHook is called when protected by baldur shell but the damage amount that is passed in is 0
ModHooks.AfterTakeDamageHook += AfterTakeDamage;
public int AfterTakeDamage(int hazardType, int damageAmount)
{
Log($"Damage amount is {damageAmount}");
//make player take double damage
return damageAmount * 2;
}
Parameter type | Parameter name | Explanation |
---|---|---|
int | hazardType | Even though it is an integer it represents a value from 0-4 of the enum HazardType . the possible values are => (1) NON_HAZARD ,(2) SPIKES ,(3) ACID ,(4) LAVA , (5) PIT , |
int | damageAmount | The amount of damage that was going to be applied to player. return back this value to not change damage taken |
Return type | Explanation |
---|---|
int | The new damage that should be applied to the player |
BeforeAddHealthHook
This event is called in PlayerData.AddHealth
. It is called before normal health is added. Can be used to change the amount of health added on heal
ModHooks.BeforeAddHealthHook += BeforeAddHealth;
public int BeforeAddHealth(int amount)
{
Log("Amount of health to be healed is {amount}");
//make healing heal 2 times more health
return amount * 2;
}
Parameter type | Parameter name | Explanation |
---|---|---|
int | amount | the amount of health that will be added to the player |
Return type | Explanation |
---|---|
int | The new amount of health that should be added to player |
TODO
- Explain Get/Set Player Int/Float/Bool/String/Vector3 Hooks