Skip to content

Custom Hooks

modargo edited this page Oct 23, 2026 · 3 revisions

When you make a new mechanic, you might want to add a hook for it too, like how the base game has all sorts of useful hooks. This page outlines a general technique for making custom hooks.

The listener interface

The first step is to just define what a listener (sometimes called a subscriber) looks like, usually using an interface.

//The name of this class doesn't matter, but pick something meaningful if you want to understand your code later
public interface ICustomHookListener
{
    //This is the function that will get called when the hook is triggered. 
    //There's no rules about what this function looks like, but a good
    //starting point is to return a Task and pass in a PlayerChoiceContext (if you have one)
    //and whatever model triggered the hook
    public Task AfterCustomHook(PlayerChoiceContext choiceContext, Power power); 
}

Implement this interface on any model where you want to receive a callback when something occurs, and define what happens the same way you would a base game hook.

Notifying the listeners

Then you need to create a function that notifies the hook listeners stuff has happened.

//You don't have to put all your notifiers in the same static class, but the base game does and it's as good a place as any
public static class YourCustomHooks
{
    //You need to pass in a runstate to notify runstate models and a combatstate to notify combat models
    //If it doesn't matter which you notify, just pass in both
    public static async Task OnCustomHook(IRunState runState, ICombatState? combatState, PlayerChoiceContext choiceContext, Power power)
    {
        //RunState and CombatState both have a method to iterate hook listeners.
        //In order to call our function we defined in the interface, we need to check if the model
        //implements the interface and cast to the interface if it does.
        //Calling `.OfType<>` is a very easy way to achieve both.
        foreach (ICustomHookListener listener in runState.IterateHookListeners(combatState).OfType<ICustomHookListener>())
        {
            //Inside the foreach loop, we just call each method, awaiting it if you returned a Task in ICustomHookListener
            await listener.AfterCustomHook(choiceContext, power);
        }
    }
}

Then you call YourCustomHooks.OnCustomHook whenever the thing occurs in your code.

Adding custom hooks for base game functionality

You can follow a nearly identical process for adding a custom hook for something that occurs in the base game, with the exception that you will need to patch the base game to call your notifier function instead of calling it yourself directly in your code base

Clone this wiki locally