User:BinaryDoubts/RelicTutorial: Difference between revisions

From Caves of Qud Wiki
Jump to navigation Jump to search
No edit summary
Line 164: Line 164:
}
}
</syntaxhighlight>
</syntaxhighlight>
When <code>RelicGenerator</code> tries to name a relic, it picks a random name that corresponds to the relic's type from the <code>itemTypes</code> table. You can have any number of synonyms in the list, although most base-game items only have 4.  
When <code>RelicGenerator</code> tries to name a relic, it picks a random name that corresponds to the relic's type from the <code>itemTypes</code> table. You can have any number of synonyms in the list, though most base-game items only have 4-5 options.


== Testing ==
== Testing ==

Revision as of 15:48, 28 January 2025

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.

Weapon Types

Weapon objects in Caves of Qud are classified by the skill required to use them. For example, all sword-ish weapon blueprints have Skill="LongBlades" in their MeleeWeapon part. The game checks the weapon's skill (WeaponObject.Skill) to detect if, say, your wielded weapon is a long blade and can be used to trigger Lunge. As a shorthand, whenever this tutorial refers to a weapon's "type," it's referring to the skill required to wield it.

Custom Weapons

The process to add custom weapons as relics depends on what kind of weapon you're adding.

  • If the weapon uses a new weapon skill added for a mod (such as Spears, GreatKatanas, FireMagicTomes, etc.), the process is more difficult (but still manageable). Begin at #Getting Started.
  • If the weapon uses an existing weapon skill, such as LongBlade or Cudgel, adding it in as a relic option is simple, see below.

Adding Weapon Variants as Relics

If the new weapon you've added in ObjectBlueprints.xml uses an existing (base-game) skill, you can insert it into the relic drop tables as an alternative to the default option (which is generally just the most basic form of the weapon for each tier, such as LongSword2, LongSword3, LongSword4, etc.).

Create or modify PopulationTables.xml and add the following lines:

<population Name="BaseRelic_WEAPONCLASS+TIER" Load="Merge">
    <group Name="Blueprint" Load="Merge">
        <object Blueprint="CUSTOMBLUEPRINTNAME" />
    </group>
</population>

Replace WEAPONCLASS with your weapon's type: LongBlade, ShortBlade, Cudgel, or Axe, and tier with a number from 1-8. For example, if you wanted to add your new weapon as a tier 3 axe relic: Name="BaseRelic_Axe3". Replace CUSTOMBLUEPRINTNAME with your new weapon's blueprint name (e.g., MyMod_MyCoolAxe3).

By default, this will give your addition and the original blueprint an equal chance of being chosen when a relic of that tier is generated. If you want it to be more likely, add Weight="2" to the object Blueprint tag.

Getting Started

This tutorial is for mods that add a new weapon skill to the game and that have a set of custom weapons that use the skill in ObjectBlueprints.xml.

To hook your new weapon type into the generator, you'll need the following:

  • Blueprints for your new weapon type in your ObjectBlueprints.xml, ideally with a full spread of tier 0-8 weapons
  • New <populations> entries in PopulationTables.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 in Relics.xml to add your weapon type to the pool of options considered by the relic generator
  • Harmony patches to RelicGenerator.GetType(GameObject Object) and RelicGenerator.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. Finally, if you'd like to understand the relic generation system on a deeper level, I recommend reading through RelicGenerator.cs via ILSpy.

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. Note that the relic population names must follow the format laid out above, but the individual object blueprints can be named however you'd like.

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 category. 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, though most base-game items only have 4-5 options.

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:

  1. 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).
  2. 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!