User:BinaryDoubts/RelicTutorial
This tutorial covers adding new weapon types to the pool of options for relics, allowing them to spawn as historical relics with boosted stats and special relic-only traits. It assumes you have already created a set of weapon blueprints for tiers 0-8 and that the weapon skill is new for your mod. The relic generation functions live in RelicGenerator.cs
and can be viewed via ILSpy.
Getting Started
To hook your new weapon type into the generator, you'll need the following:
- New
<populations>
entries inPopulationTables.xml
for every tier of weapon so the relic generator knows what blueprints to use as the base item to modify - New
<relictype>
and<relictypemapping>
entries inRelics.xml
to add your weapon type to the pool of options considered by the relic generator - Harmony patches to
RelicGenerator.GetType(GameObject Object)
andRelicGenerator.GetSubtype(string type)
so that the generator can correctly understand that your weapons should be treated as such by the generator and be given weapon-only boosts - New weapon name synonyms added to
HistorySpce.json
so the relic generator can give your relic weapon an appropriate name
This tutorial will use the example of a mod called BigSwords
that adds the GreatKatana
weapon type. Replace BigSwords_GreatKatana
with the name used by your new weapon type/skill when copy+pasting code from this page.
XML Edits
The first step is adding some new tags into your mod's XML so that the relic generator knows about your new weapon type and what blueprints to use as the base item when a relic is generated.
PopulationTables.xml
When the game generates a relic, after picking the item's type, it will look for a population containing a set of blueprints that's named in a specific way: BaseRelic_
+ WeaponType
+ tier number
. If the weapon has a two-handed variant, the relic can also spawn in a two-handed version, and will look for a population named following the same method but with th
added at the end.
Using the BigSwords mod as an example, the populations for a tier 1 weapon would be named BaseRelic_BigSwords_GreatKatana1
or BaseRelic_BigSwords_GreatKatana1th
for the two-handed version. The game will always roll (20% chance) to see if a two-handed version is being selected, even if your weapon type doesn't have two-handed variants — if there's no th
version of the population table, it will fall back and use the base, non-th
population instead. Once the relic generator finds the correct population, it will search the subsidiary Blueprint pick-one group and choose a blueprint as the base item.
If you don't already have it, create a new XML file in your mod's folder named PopulationTables.xml
, then fill it with population tables set up as discussed above. For example, here's what our BigSwords mod's tables might look like for tier 1:
<population Name = "BaseRelic_BigSwords_GreatKatana1">
<group Name = "Blueprint" Style = "pickone">
<object Blueprint = "BigSwords_GreatKatana1"/>
</group>
</population>
<population Name = "BaseRelic_BigSwords_GreatKatana1th">
<group Name = "Blueprint" Style = "pickone">
<object Blueprint = "BigSwords_GreatKatana1th"/>
</group>
</population>
Adding multiple variants is easy, and they even support weights to make one more likely than the other. Here's an example showing a table with multiple variants:
<population Name = "BaseRelic_BigSwords_GreatKatana1">
<group Name = "Blueprint" Style = "pickone">
<object Blueprint = "BigSwords_GreatKatana1" Weight="20"/>
<object Blueprint = "BigSwords_GreatKatana1_FlameBlade" Weight="10"/>
<object Blueprint = "BigSwords_GreatKatana1_IceBlade" Weight="10"/>
</group>
</population>
</code>
Relics can spawn from tiers 1-8, so you'll need 8 versions of the table(s), one for each tier. Base (bronze) items cannot spawn as relics so there's no need to create a BaseRelic_BigSwords_GreatKatana
(with no number, representing tier 0) population table.
Relics.xml
If you don't already have it, create a new XML file in your mod's folder named Relics.xml
. Set up a file that looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<relics>
<relictypes>
</relictypes>
<relictypemappings>
</relictypemappings>
</relics>
The new weapon type needs an entry in the <relictype>
table and the <relictypemappings>
table. Under relictype
, add <relictype Name = "BigSwords_GreatKatana" />
. The Name should be exactly the same as your weapon's skill/type name.
Under relictypemappings
, add <relictypemapping Name = "great katana" Type = "BigSwords_GreatKatana"/>
. The Name field should just be the name of your weapon type in plain lowercase text, while Type should be the name of your weapon type/skill name.
Your final Relics.xml
should look like this:
<?xml version="1.0" encoding="UTF-8"?>
<relics>
<relictypes>
<relictype Name = "BigSwords_GreatKatana" />
</relictypes>
<relictypemappings>
<relictypemapping Name = "great katana" Type = "BigSwords_GreatKatana"/>
</relictypemappings>
</relics>
Harmony Patches
You've set up the infrastructure for your new relic, but the game still needs a little help figuring out if your new relics count as weapons or not. If the game can't figure out an item's type, it will default to treating it like a curio, meaning it won't get any bonuses to damage, to-hit, stats, or other traits that can only roll on weapon relics. You can easily tell if a weapon is set up correctly by checking its effects: if the game thinks it's a curio, the effect will always be either "summon a friendly creature" or "destroy all creatures of a faction."
If you don't already have a C# file for Harmony patches, create a new Patches.cs
(name can be anything) file and set it up like this:
using XRL.World;
namespace BigSwords.HarmonyPatches{
}
You'll need to patch the RelicGenerator.GetType(GameObject Object)
and RelicGenerator.GetSubtype(string type)
methods to make sure your weapon type is treated correctly.
GetType
Add a new block of code to the HarmonyPatches namespace:
[HarmonyPatch(typeof(XRL.World.RelicGenerator))]
class BigSwords_RelicGetTypePatch{
[HarmonyPatch("GetType")]
[HarmonyPostfix]
static void Postfix(ref GameObject Object, ref string __result){
MeleeWeapon wep = Object.GetPart<MeleeWeapon>();
if (wep != null && wep.Skill == "BigSwords_GreatKatana"){
__result = "BigSwords_GreatKatana";
}
}
}
All we're doing here is using a Harmony patch to access the results of the GetType
method. Whenever GetType
is run, it looks at the actual created GameObject and returns a string containing the object's Skill name. In this case, we're looking for a weapon that has the skill BigSwords_GreatKatana (since that's what our mod uses) and, if present, return that skill name. If your weapon type is ranged, use GetPart<MissileWeapon>()
instead of GetPart<MeleeWeapon>()
.
GetSubtype
GetSubtype
is a simple method that boils down multiple item types to their overarching "class." ShortBlades, LongBlades, Cudgels, and Axes all return "weapon" as their subtype, while Pistols and Rifles return "ranged." All that's needed for this patch is to check if the provided type string matches our new weapon type, and if so, return "weapon" instead of "curio" (the default result if GetSubtype
can't figure out the type). Add a new block of code to your HarmonyPatches namespace:
[HarmonyPatch(typeof(XRL.World.RelicGenerator))]
class BigSwords_RelicGetSubtypePatch{
[HarmonyPatch("GetSubtype")]
[HarmonyPostfix]
static void Postfix(ref string type, ref string __result){
if (type == "BigSwords_GreatKatana"){
__result = "weapon";
}
}
}
If your weapon type is ranged, set __result
to "ranged"
instead of "weapon"
.
History Spice
The last step is modifying the game's HistorySpice.json file so that it knows how to name your relic. You can write your own methods to do this, or (recommended) use Tyrir's [Hot 'n Spicy History mod] to achieve the same goal. This tutorial will assume you're using the mod, so make sure you're subscribed to it, that it's enabled in-game, and that it's set as a dependency if your mod is published to the Workshop.
With the Hot 'n Spicy History mod enabled, all you need to do is create a new file named HistorySpice.json
and place it in your mod's root directory. When the game is launched, the contents of your file will be merged with the game's default version, allowing the relic generator to find names for your weapon.
Here's what the file should look like:
{
"spice": {
"itemTypes":{
"BigSwords_GreatKatana": ["ultra-katana", "really big sword", "slicer", "giant razor"]
}
}
}
When RelicGenerator
tries to name a relic, it picks a random name that corresponds to the relic's type from the itemTypes
table. You can have any number of synonyms in the list, although most base-game items only have 4.
Testing
After you've completed all of the above steps, your new weapon type should be fully integrated into the game's relic generation system. To quickly test if you've correctly set it up, load into a new game and use the wish relic
to generate a relic of every type. If your type has been added, you should find at least one relic of your new type in the pile of items that spawn. Double-check that it has:
- A correct name that includes one of your synonyms from HistorySpice (if it can't find a word, the name will noticeably contain a blank where the word should go).
- Weapon bonuses, which could include to-hit, damage, and/or added mutations (if the weapon can summon creatures or wipe out all creatures of a given faction, it has been treated as a curio, not a weapon).
Assuming all is well, that's it. Enjoy your new relics!