Modding Tutorials/DefModExtension

From RimWorld Wiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

Modding Tutorials

A DefModExtension is a way to add fields to Defs. DefModExtensions are a simple way to extend functionality to a Def. A DefModExtension doesn't directly add fields to classes as that's technically impossible to do at runtime. The implementation is that the Def class has a public List<DefModExtension> which can easily be extended.

Benefits:
+ Is really simple and light-weight to use.
+ Works on any Def.
+ Exposes its functionality to XML.
+ Compatible with savefiles, other mods, you name it.
+ Does not come with the compatibility issues and pitfalls of creating a custom Def class.
+ Does not have the overhead of a ThingComp.
+ More broadly applicable than a ThingComp (not every Def is a ThingWithComps!).

Downsides:
- Static and global data.
- Can't save data.
- Only works on Defs.
- A DefModExtension does not know what Def it belongs to. (There are ways around this, like setting a Def field in your DefModExtension and populating it in XML/C#)

Requirements

You know the deal by now.

  • Solution
  • Custom code
  • C#
  • XML

The Code

using Verse;

namespace ExampleNameSpace
{
    public class ExampleModExtension : DefModExtension
    {
        public bool canBeARimQuest = true;
    }
}

That's it. DefModExtensions can of course contain any field you wish: a C# Type, a Thingfilter, a list -- but for the sake of a simple example, we'll use a bool.

Using the DefModExtension

You can use the DefModExtension on any Def with that extension. Their usage is much the same way as any other field in a Def. The biggest difference is in the syntax; instead of bool value = def.field, it's bool value = def.GetModExtension<ExampleModExtension>().field

using Verse;
using RimWorld;

namespace ExampleNameSpace
{
    public class OurExampleClass
    {
        private static bool IsAcceptableQuest(IncidentDef incidentDef)
        {
            if (incidentDef.letterDef == LetterDefOf.NegativeEvent)
                return false;

            if (incidentDef.HasModExtension<ExampleModExtension>())
                return incidentDef.GetModExtension<ExampleModExtension>().canBeARimQuest;

            return true;
        }
    }
}

Or if you like your code shorter, you can use null conditionals and null coalescing:

            return incidentDef.letterDef != LetterDefOf.NegativeEvent
                && (incidentDef.GetModExtension<ExampleModExtension>()?.canBeARimQuest ?? true); 
                //will return value of mod extension if not null, otherwise assumed true.

Adding it to your def

<Defs>
    <ExampleDef>
        <defName>Example</defName>
        <modExtensions>
            <li Class="ExampleNameSpace.ExampleModExtension">
                <canBeARimQuest>false</canBeARimQuest>
            </li>
        </modExtensions>
    </ExampleDef>
</Defs>

Using xpath to add it to a def

If you wanted to xpath patch a mod extension onto an existing def, you'd use PatchOperationAddModExtension, which is like PatchOperationAdd but saves having to check if modExtensions has already been added/having to include it in your value field:

<Patch>
    <Operation Class="PatchOperationAddModExtension">
        <xpath>Defs/ExampleDef[defName="Example"]</xpath>
        <value>
            <li Class="ExampleNameSpace.ExampleModExtension">
                <canBeARimQuest>false</canBeARimQuest>
            </li>
        </value>
    </Operation>
</Patch>

See also

Ludeon thread on Mod Extensions
The pull request on which the code in this article is based on