Mod Compatibility
Updated 2 weeks agoAdding Quality for modded items or equipment
Important types
Before we can get into adding items, you should know about a few types commonly used by Quality.
QualityTier
An enum for all the quality tiers added by the mod, frequently used for interfacing with the QualityCatalog.
ItemQualityGroup
Contains all quality versions of an item for easy access, typically consisting of a base item reference and 4 auto-generated ItemDefs. You can think of it like an ItemDef that represents every quality version of an item simultaneously. Also has an ItemIndex equivalent of ItemQualityGroupIndex.
ItemQualityCounts
This helper struct contains an item count for every item in a quality group, used in place of an int item count for things like the GetItemCountsEffective extension method for Inventory.
QualityCatalog
This is the main catalog for the mod, it contains all quality groups and mappings of items/equipment to QualityTiers.
A common use-case is using it to convert a ItemIndex/PickupIndex into a specific QualityTier. This is done via the GetItemIndexOfQuality/GetPickupIndexOfQuality methods respectively, these return either the given item or pickup index as a certain quality, or the base item/pickup index if that quality isn't registered for it. It will not return an invalid index (assuming a valid index was given to begin with).
Language tokens
Another important thing you should be aware of is how languages tokens are handled by Quality. Tokens are mostly auto-generated in the format: ITEM/EQUIP_[ItemName]_[QualityTier]_NAME/DESC/PICKUP. So for example the description token for Rare Backup Magazine is ITEM_SECONDARYSKILLMAGAZINE_RARE_DESC.
For pickup and description tokens, you can use {0} to insert the base description or pickup string, if you only need to add some more text to the start or end of a description/pickup string to describe your quality effect:
"ITEM_SECONDARYSKILLMAGAZINE_RARE_PICKUP": "{0}\n<sprite name=\"Quality\"> Chance to immediately recharge your Secondary skill.",
"ITEM_SECONDARYSKILLMAGAZINE_RARE_DESC": "{0}\nActivating your <style=cIsUtility>Secondary skill</style> has a <sprite name=\"Quality\"> <style=cIsUtility>25%</style> <style=cStack>(+25% per stack)</style> chance to immediately recharge it.",
Sprites
Quality adds a couple inline sprites you can use in most text displayed in the game.
| Sprite | Usage |
|---|---|
![]() |
<sprite name="Quality"> |
![]() |
<sprite name="QualityUncommon"> |
![]() |
<sprite name="QualityRare"> |
![]() |
<sprite name="QualityEpic"> |
![]() |
<sprite name="QualityLegendary"> |
![]() |
<sprite name="QualityUncommonConsumed"> |
![]() |
<sprite name="QualityRareConsumed"> |
![]() |
<sprite name="QualityEpicConsumed"> |
![]() |
<sprite name="QualityLegendaryConsumed"> |
Adding Quality Items
Adding Quality versions of your own modded items is done via the QualityContentManager class. To register your mod in the initialization, subscribe to the QualityContentManager.LoadContentAsync event.
internal static class QualityCompat
{
static ItemQualityGroup ExampleItemGroup;
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
public static void Init()
{
QualityContentManager.LoadContentAsync += loadContent_Basic;
RecalculateStatsAPI.GetStatCoefficients += RecalculateStatsAPI_GetStatCoefficients;
}
private static IEnumerator loadContent_Basic(QualityContentLoadArgs args)
{
ExampleItemGroup = args.CreateItemQualityGroup(QualityCompatTesterPlugin.Instance.ExampleItem);
yield break;
}
private static void RecalculateStatsAPI_GetStatCoefficients(CharacterBody sender, RecalculateStatsAPI.StatHookEventArgs args)
{
if (sender.inventory)
{
ItemQualityCounts exampleItem = sender.inventory.GetItemCountsEffective(ExampleItemGroup);
args.armorAdd += (10 * exampleItem.UncommonCount) +
(30 * exampleItem.RareCount) +
(50 * exampleItem.EpicCount) +
(100 * exampleItem.LegendaryCount);
}
}
}
In this example the base ItemDef is created through code in Awake, but these coroutines are run during LoadStaticContentAsync, so you can yield wait until your asset bundle is loaded before creating your item group(s).
Quality will automatically generate all assets needed for your item to have quality variants. But you can also assign the individual quality ItemDefs by using the SetItemDef method on the ItemQualityGroup if you need more control over how the ItemDef is created, but note that this means Quality will hand every part of initialization off to you, meaning you will have to populate the item yourself and also add it to a content pack.
Void item relationships are automatically handled if given an item that is part of a void corruption pair.
IMPORTANT:
If you're using a class like above, with fields that use types in the Quality assembly, you need to take a few extra steps in order to have soft compatibility. The big rule you must follow is that no method that contains any reference to your type(s) with these fields can be executed before you have confirmed Quality is present in the current domain.
So for example this:
void Awake()
{
if (BepInEx.Bootstrap.Chainloader.PluginInfos.ContainsKey(ItemQualities.ItemQualitiesPlugin.PluginGUID))
{
QualityCompat.Init();
}
}
Will not work because the type is scanned by the runtime regardless of if the Init method its actually executed or not. Instead, you must do something like this:
void Awake()
{
if (BepInEx.Bootstrap.Chainloader.PluginInfos.ContainsKey(ItemQualities.ItemQualitiesPlugin.PluginGUID))
{
qualityCompatInit();
}
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
static void qualityCompatInit()
{
QualityCompat.Init();
}
This prevents the runtime from trying to initialize your type unless its absolutely okay to do so. This pattern has to be used anywhere you reference your Compat type (assuming it has the Quality typed fields).
Adding Quality Equipment
Not yet.
Contact
If you think something here is not covered well or at all, please contact me at gorakh (Gorakh#0821) on Discord.








