-
Notifications
You must be signed in to change notification settings - Fork 43
Creating an Accessory
So you've decided to take a ticket for creating a custom accessory for starlight river, great! This page is intended to walk you through the unique architecture and utilities we use to create your accessory easily and as self-contained as possible. You'll want to make sure you're familiar with the SmartAccessory
abstract class, as well as the delegation-based architecture used to take advantage of ModPlayer and ModNPC hooks from within it.
SmartAccessory is an abstract subclass of ModItem in StarlightRiver, intended to facilitate the easy creation of accessories by providing it with self-awareness (hence the 'smart' in it's name). A SmartAccessory can tell if it is equipped on a given player via it's Equipped
method. This method takes a Player
parameter to check against, and will return if it is equipped on that player.
This is primarily useful when employing the use of StarlightPlayer and StarlightNPC events, detailed below, in which you will want to have an accessory check it's own equip status before executing code for it's effects.
Starlight River provides classes which contain Event
s for some common TModLoader hooks, named StarlightPlayer and StarlightNPC. From the Load
hook of other ILoadable
or IOrderedLoadable
s, you can add to or 'hook' these events. For the sake of consistency, we will call this 'hooking' and the method being added the 'hooked method'. When creating an accessory, it is reccomended to take advantage of this to implement effects which would normally require ModPlayer or ModNPC classes.
To use these events, the first thing you will want to do is add to them in the Load
hook of SmartAccessory
. this will look something like this:
public override void Load()
{
StarlightPlayer.ModifyHitByNPCEvent += MyNewMethod;
}
This means that the behavior defined in the MyNewMethod
hooked method will execute when the ModifyHitByNPC
hook runs in TModLoader. If you write the event addition first, Visual Studio should offer a quick-fix option when hovering over the error for MyNewMethod
not being defined to auto-generate it. Else you should write the method to have a signature matching the delegate type of the event you are hooking.
From there you will need to write the contents of the hooked method. The first thing you should do in an accessory is to check if it is actually equipped! Remember that the code in your hooked method will always run, so if you don't check if an accessory is equipped, the player will seem to always have it's effects.
public void MyNewMethod(Player player, NPC NPC, ref int damage, ref bool crit)
{
if (Equipped(player))
{
//Effects here!
}
}
If you are storing instance-specific data about your accessory, you can create an instance field on your accessory class. Note that when retrieving instance data inside of a hooked method, you will need to use an overload of the GetEquippedInstance
method of SmartAccessory, as the instance that these hooked methods see is the dummy that is loaded on mod load by default.
public int charge = 0;
public void MyNewMethod(Player player, NPC NPC, ref int damage, ref bool crit)
{
if (Equipped(player))
{
//Incorrect!
Main.NewText($"The dummy instance loaded on mod load has {charge} points of charge!");
//Correct!
var instance = GetEquippedInstance(player);
Main.NewText($"{player.name}'s accessory has {instance.charge} points of charge!");
}
}
SmartAccessory
has one final, extremely powerful feature: accessory simulation. Any SmartAccessory
can intelligently simulate any amount of other SmartAccessory
s. This is done by creating a tree-like structure of 'child' accessory types, which is resolved to a collection of instances of items stored seperately from the player's standard accessories. This also automatically prevents duplicate compounding effects, as both parent accessories will resolve to the same child. Children are automatically removed if they have no remaining viable parents. Additionally, equipping up or down a simulation chain is automatically disabled (You cant equip X along with an accessory that simulates X, it would be redundant anyways!)
In order to create a compound accessory, you simply need to override the virtual ChildTypes
property in SmartAccessory
, returning a new list containing the item IDs of the SmartAccessory
s you want your accessory to emulate. For example:
public override List<int> ChildTypes =>
new List<int>()
{
ModContent.ItemType<MyCoolItem>(),
ModContent.ItemType<MyOtherCoolItem>()
};
This will make your accessory perfectly combine the effects of MyCoolItem
and MyOtherCoolItem
seamlessly with no extra effort.