Hooks
A hook is an object that is attached to another object ("hook" is a really vague word, jsyk). In RogueLibs hook types derive from IHook
and IHook<T>
, and RogueLibs provides a mechanism to attach these hooks to vanilla types, such as InvItem
, PlayfieldObject
, Unlock
, Trait
and etc. Most custom content classes are based on hooks in one way or another.
IHook
interface
RogueLibsPatcher.dll creates fields called __RogueLibsHooks
in all hookable types. An instance of IHookController
class is then assigned to the __RogueLibsHooks
field to manage the attached hooks. It provides methods to get, attach and detach hooks from the current instance. Think of it as a collection of hooks.
You can create your own hooks by deriving either from IHook<T>
or from HookBase<T>
:
public class MyCustomHook : HookBase<InvItem>
{
protected override void Initialize() { }
public void StoreInfo(string data)
{
Debug.Log($"Stored {data}.");
Data = data;
}
public string LoadInfo()
{
Debug.Log($"Loaded {Data}.");
return Data;
}
private string Data;
}
Usage
You can use hooks to store various stuff:
MyCustomHook hook = item.AddHook<MyCustomHook>();
hook.StoreInfo("some-information");
Then you can use that stuff somewhere else:
MyCustomHook hook = item.GetHook<MyCustomHook>();
if (hook != null)
{
string data = hook.LoadInfo();
}
You can attach more than one hook of a type too:
MyCustomHook hook = item.AddHook<MyCustomHook>();
hook.StoreInfo("some-information");
hook = item.AddHook<MyCustomHook>();
hook.StoreInfo("some-other-stuff");
hook = item.AddHook<MyCustomHook>();
hook.StoreInfo("something-else");
foreach (MyCustomHook hook in item.GetHooks<MyCustomHook>())
{
string data = hook.LoadInfo();
}
If you want to attach hooks to instances right when they are initialized, use Hook Factories.
Examples
Custom content classes (CustomItem
, CustomTrait
, CustomEffect
, CustomAbility
and others) are hooks, by the way. You can see the custom classes' implementation in RogueLibs' source code.
- Spice Rack
A great example with custom hooks keeping track of seasoned items.
See the combinable item example here.
using UnityEngine;
namespace RogueLibsCore.Test
{
[ItemCategories(RogueCategories.Food, RogueCategories.Health)]
public class SpiceRack : CustomItem, IItemCombinable
{
[RLSetup]
public static void Setup()
{
RogueLibs.CreateCustomItem<SpiceRack>()
.WithName(new CustomNameInfo("Spice Rack"))
.WithDescription(new CustomNameInfo("Combine with any food item to increase its healing properties."))
.WithSprite(Properties.Resources.SpiceRack)
.WithUnlock(new ItemUnlock
{
UnlockCost = 10,
LoadoutCost = 3,
CharacterCreationCost = 2,
Prerequisites = { VanillaItems.FoodProcessor },
});
SeasonCursorText = RogueLibs.CreateCustomName("SeasonItem", NameTypes.Interface, new CustomNameInfo("Season"));
}
private static CustomName SeasonCursorText = null!;
public override void SetupDetails()
{
Item.itemType = ItemTypes.Combine;
Item.itemValue = 4;
Item.initCount = 10;
Item.rewardCount = 15;
Item.stackable = true;
Item.hasCharges = true;
}
public bool CombineFilter(InvItem other)
{
if (other.itemType != ItemTypes.Food || other.healthChange is 0
|| !other.Categories.Contains(RogueCategories.Food)) return false;
SpicedHook? hook = other.GetHook<SpicedHook>();
return hook is null || hook.Spiciness < 3;
}
public bool CombineItems(InvItem other)
{
if (!CombineFilter(other)) return false;
SpicedHook hook = other.GetHook<SpicedHook>() ?? other.AddHook<SpicedHook>();
hook.IncreaseSpiciness();
Count--;
gc.audioHandler.Play(Owner, VanillaAudio.CombineItem);
return true;
}
public CustomTooltip CombineCursorText(InvItem other) => SeasonCursorText;
public CustomTooltip CombineTooltip(InvItem other)
{
if (!CombineFilter(other)) return default;
SpicedHook? hook = other.GetHook<SpicedHook>();
int bonus = hook is null ? (int)Mathf.Ceil(other.healthChange / 4f) : hook.HealthBonus;
return new CustomTooltip($"+{bonus}", Color.green);
}
private class SpicedHook : HookBase<InvItem>
{
protected override void Initialize()
=> HealthBonus = (int)Mathf.Ceil(Instance.healthChange / 4f);
public int HealthBonus { get; private set; }
public int Spiciness { get; private set; }
public void IncreaseSpiciness()
{
if (Spiciness is 3) return;
Spiciness++;
Instance.healthChange += HealthBonus;
}
}
}
}