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
interfaceRogueLibsPatcher.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 MyAwesomeHook : HookBase<InvItem>{ private string Data; protected override void Initialize() { } public void StoreInfo(string data) { Debug.Log($"Stored {data}."); Data = data; } public string LoadInfo() { Debug.Log($"Loaded {Data}."); return Data; }}
You can use hooks to store various stuff:
MyAwesomeHook hook = item.AddHook<MyAwesomeHook>();hook.StoreInfo("some-information");
Then you can use that stuff somewhere else:
MyAwesomeHook? hook = item.GetHook<MyAwesomeHook>();if (hook != null){ string data = hook.LoadInfo();}
You can attach more than one hook of a type too:
MyAwesomeHook hook = item.AddHook<MyAwesomeHook>();hook.StoreInfo("some-information");hook = item.AddHook<MyAwesomeHook>();hook.StoreInfo("some-other-stuff");hook = item.AddHook<MyAwesomeHook>();hook.StoreInfo("something-else");
foreach (MyAwesomeHook hook in item.GetHooks<MyAwesomeHook>()){ string data = hook.LoadInfo();}
If you want to attach hooks to instances right when they are initialized, use Hook Factories.
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.
A great example with custom hooks keeping track of seasoned items.
See more about combinable items here.
using UnityEngine;using RogueLibsCore;namespace MyAwesomeMod{ [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 == 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 == 3) return; Spiciness++; Instance.healthChange += HealthBonus; } } }}