Editing Modding Tutorials/Modifying defs

Jump to navigation Jump to search

Warning: You are not logged in. Your IP address will be publicly visible if you make any edits. If you log in or create an account, your edits will be attributed to your username, along with other benefits.

The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then save the changes below to finish undoing the edit.

Latest revision Your text
Line 1: Line 1:
 
{{BackToTutorials}}
 
{{BackToTutorials}}
  
This tutorial shows you several ways to modify existing ''Defs'' and alternatives to changing the XML format for them.<br/>
+
This tutorial shows you several ways to modify defs' (ThingDef, PawnKindDef) C# classes and alternatives to changing the XML format for them.<br/>
  
There are several ways to achieve this goal. The table below is the [https://en.wikipedia.org/wiki/TL;DR TL;DR] version of this article. C# related operations are marked '''bold'''.<br/><br/>
+
There's several ways to achieve this goal. Three are listed here.<br/><br/>
  
{| class="wikitable"
+
=Requirements=
|-
+
 
! Method !! Pros !! Cons !! When to use
+
* [[Modding Tutorials/Decompiling source code|Decompiling source code]] is required.
|-
+
* This tutorial requires you to know [[Modding Tutorials/Writing custom code|how to write custom code]].
| Overwrite the Def || Braindead easy || So incompatible you gotta be braindead || When you don't care about the original, or anyone else
+
* [[Modding Tutorials/Def classes|Introduction to Def Classes]] is highly recommended.<br/><br/>
|-
+
 
| XPath change || Highly specific, highly compatible || Limited to XML-defined Defs || When you need to change a few XML values
+
==Custom def class==
|-
+
 
| Adding a '''(self-made)''' Comp || Very flexible, well-supported, highly-compatible || Does not work on every Def || When you want to add functionality
+
This is where you create custom defs for your mod and C# code to go along with them.
|-
+
 
| '''DefModExtension''' || Very simple, well-supported, highly-compatible || Static data || When you want to add fields/data
+
The use of def classes is very popular in small mods: as long as the defs in the mod aren't overwriting core defs, it's very compatible.
|-
+
 
| '''SubClassing''' || Fairly powerful, half the work is already done || Compatibility issues, not very flexible || When neither a Comp nor a DefModExtension works
+
So if you have your own objects that you've created, and have C# code to go with them, it's fairly straightforward to add a custom def.  On the other hand, if you want to add a custom def to a core def that gets used everywhere in the code, it will be extremely difficult and you might be better off with a different approach.
|-
+
 
| '''Custom Def''' || Full control || Very specific to your mod || When the given Defs don't suffice for you
+
===XML/Code Requirements===
|}
+
To make a custom def, you need:
 +
# The XML must have <code><thingDef ... Class="myNamespace.MyCustomDefName></code>
 +
# There must be a definition of the <code>class MyCustomDefName : ThingDef</code>
 +
# Code needs to access the new fields - probably using casting, such as <code>var def=thing.def as MyCustomDefName;</code>
 +
 
 +
===Simple Example===
 +
Suppose you add a new gun to RimWorld (assuming you're violent), that has a special feature.
 +
 
 +
Your XML might look something like this:
 +
 
 +
<source lang="xml"><?xml version="1.0" encoding="utf-8"?>
 +
<ThingDefs>
 +
<thingDef ParentName="BaseHumanGun" Class="myNamespace.MyCustomDef_ThingDef">
 +
<defName>MyCoolNewGun</defName>
 +
<thingClass>myNamespace.MyCoolNewGun</thingClass>
 +
        <etc/>
 +
<myNewFloat>1.1</myNewFloat>
 +
<myNewBool>true</myNewBool>
 +
<myNewThingDefList>
 +
<li>Silver</li>
 +
<li>Pistol</li>
 +
</myNewThingDefList>
 +
</thingDef>
 +
</ThingDefs></source>
 +
 
 +
Two things were added to the XML for the custom defs: the Class="myNamespace.MyCustomDef_ThingDef" and the new definitions.
 +
 
 +
Corresponding C# code might look like this:
 +
 
 +
<source lang="csharp">using RimWorld;
 +
using System;
 +
using System.Collections.Generic;
 +
using System.Diagnostics;
 +
using System.Linq;
 +
using UnityEngine;
 +
 
 +
 
 +
namespace myNamespace
 +
{
 +
public class MyCustomDef_ThingDef : ThingDef // Here we are adding our custom defs
 +
{
 +
public float myNewFloat;
  
=Requirements=
+
public bool myNewBool;
 +
 
 +
public List<ThingDef> myNewThingDefList = new List<ThingDef>();
 +
}
 +
public class MyCoolNewGun : etc //etc etc
 +
{
 +
  //etc
 +
}
 +
}</source>
 +
 
 +
And now that that exists you can call on it with any ThingDef as input like so:<br/>
 +
 
 +
<source lang="csharp">
 +
  // maybe var weapon=new myNamespace.MyCoolNewGun();
 +
  // or maybe a function was called with a parameter "weapon"
 +
 
 +
  if (weapon as myNamespace.MyCoolNewGun != null) // make sure it actually IS a MyCoolNewGun, HAS the code, etc:
 +
  {
 +
      // use the thingdef:
 +
      var def = weapon.def as myNamespace.MyCustomDef_ThingDef;
 +
      var theFloat = def.myNewFloat;
 +
 
 +
      // or access directly:
 +
      var myBool = ((myNamespace.MyCustomDef_ThingDef)weapon.def).myNewBool;
 +
      //    this will throw an exception if somehow the weapon isn't a MyCoolNewGun!
 +
 
 +
      // use the defs, etc
 +
      // this works in beta 0.18.1722
 +
  }
 +
</source>
 +
 
 +
 
 +
===Further Example===
 +
You might want to add a whole class of guns with this new feature:
 +
 
 +
Your XML might look something like this:
 +
 
 +
<source lang="xml"><?xml version="1.0" encoding="utf-8"?>
 +
<ThingDefs>
 +
<thingDef Name="MyCoolNewGun" ParentName="BaseHumanGun" Class="myNamespace.MyCustomDef_ThingDef" Abstract="true">
 +
<thingClass>myNamespace.MyCoolNewGun</thingClass>
 +
        <etc/>
 +
<myNewFloat>1.1</myNewFloat>
 +
<myNewBool>true</myNewBool>
 +
<myNewThingDefList>
 +
<li>Silver</li>
 +
<li>Pistol</li>
 +
</myNewThingDefList>
 +
</thingDef>
 +
 
 +
<thingDef ParentName="MyCoolNewGun">
 +
<defName>MyCoolNewPistol</defName>
 +
<myNewFloat>2.2</myNewFloat>
 +
</thingDef>
 +
 
 +
<thingDef ParentName="MyCoolNewGun">
 +
<defName>MyCoolWaterPistol</defName>
 +
<myNewBool>false</myNewBool>
 +
<etc />
 +
</thingDef>
 +
</ThingDefs></source>
 +
 
 +
Each will inherit the parent's def class and link in with your code, and you can have a whole class of things using your cool new weapon mod.
 +
 
 +
===When Not To Use It===
 +
 
 +
There are two big catches:
 +
 
 +
#  If you modify an existing definition to use your class, and someone else does too, there might be mod conflicts.  Oops.
 +
#  Your defs only work for your own code.  An example:
 +
 
 +
====Problem Example====
 +
''The following method shows you '''why not to use''' custom defs. In some cases you could attempt to use this method but it's generally considered the least favourable way to deal with things.''
 +
 
 +
For this example we're gonna '''try to''' change the <race> tag to contain more tags. The value of these tags isn't exactly important to this example.
 +
 
 +
<source lang="xml"><?xml version="1.0" encoding="utf-8"?>
 +
<ThingDefs>
 +
<thingDef Name="BaseRace" Class="myNamespace.ThingDefWithCustomRaceProps"><!-- May not be needed in recent versions-->
 +
</thingDef>
 +
 
 +
<thingDef Name="BaseAnimalRace" ParentName="BaseRace" Class="myNamespace.ThingDefWithCustomRaceProps"><!-- may not be needed -->
 +
</thingDef>
 +
 
 +
<thingDef ParentName="BaseAnimalRace" Class="myNamespace.ThingDefWithCustomRaceProps">
 +
<race Class="myNamespace.RacePropertiesCustom">
 +
<myNewFloat>1.1</myNewFloat>
 +
<myNewBool>true</myNewBool>
 +
<myNewThingDefList>
 +
<li>Silver</li>
 +
<li>Pistol</li>
 +
</myNewThingDefList>
 +
</race>
 +
</thingDef>
 +
</ThingDefs></source><br/>
  
* [[Modding Tutorials/XML Defs|Understanding Defs]] helps.
+
(earlier versions) Without assigning the Class to each thingDef inheriting from and being inherited by a certain thingDef with a certain Class, '''the mod will cause errors'''.
* For the C# portions:
 
** [[Modding Tutorials/Decompiling source code|Decompiling source code]] is required.
 
** This tutorial requires you to know [[Modding Tutorials/Writing custom code|how to write custom code]].
 
** [[Modding Tutorials/Def classes|Introduction to Def Classes]] is highly recommended.<br/><br/>
 
  
=Overwriting Defs=
+
Now for the code we will have to create two C# classes, namely myNamespace.ThingDefWithCustomRaceProps and myNamespace.RacePropertiesCustom:
{{Main|Modding_Tutorials/Compatibility_with_defs}}
 
If two mods share the same defName for a single type of ''Def'', the last mod wins. If mod A adds a ResearchDef with defName Pemmican and mod B also adds a ResearchDef with defName Pemmican, the game will use mod B's Pemmican.
 
==Pros==
 
Braindead easy.
 
  
==Cons==
+
<source lang="csharp">using RimWorld;
No compatibility.
+
using System;
 +
using System.Collections.Generic;
 +
using System.Diagnostics;
 +
using System.Linq;
 +
using UnityEngine;
  
==When to use==
 
Not.
 
  
=XPath=
+
namespace myNameSpace
{{Main|Modding Tutorials/PatchOperations}}
+
{
XPath allows you to change specific values of Defs (multiple in a single operation, if you want) across mods with surgical precision.
+
public class ThingDefWithCustomRaceProps : ThingDef
 +
{
 +
new public RacePropertiesCustom race; /* requires the new keyword to overwrite the existing race */
 +
}
 +
}</source><br/>
  
==Pros==
+
And for the RacePropertiesCustom we make the following script:<br/>
Highly flexible, highly compatible.
 
  
==Cons==
+
<source lang="csharp">using RimWorld;
Limited to Defs defined in XML (no meat, corpses or other generated Defs). More complex operations require more fiddly syntax.
+
using System;
 +
using System.Collections.Generic;
 +
using System.Diagnostics;
 +
using UnityEngine;
  
==When to use==
+
namespace myNameSpace
All the time.
+
{
 +
public class RacePropertiesCustom : RaceProperties
 +
{
 +
public float myNewFloat;
  
=Adding a (self-made) Comp=
+
public bool myNewBool;
{{Main|Modding Tutorials/ThingComp}}
 
ThingComps are like little modules you can add to any ThingWithComp to give them added functionality.
 
  
===Pros===
+
public List<ThingDef> myNewThingDefList = new List<ThingDef>();
Very flexible, well-supported and highly-compatible. There are many ready-made (example) Comps available that can be employed to do a wide variety of things.
+
}
 +
}</source><br/>
  
===Cons===
+
One might think that's all required to make it work. However!
Not suited for every type of functionality.
 
  
===When to use===
+
Wherever the race variable tag is called in the game's code, it's called like <code>((ThingDef)someVariable).race.someMethod()</code>.  It's NOT calling your code!  It's calling the base game's code.
When you want to add functionality, non-static data or behaviour.
 
  
=DefModExtensions=
+
Everywhere you need someMethod() to use your code, you need to cast the race:  ThingDefWithCustomRaceProps like <code>(ThingDefWithCustomRaceProps)((ThingDef)someVariable).race.someMethod()</code>.  '''EVERY''' single time.  It means copying and rewriting large portions of code, finding out they're called by certain other methods that also require the same treatment to call the new custom method to fix the custom thingDef class you made.
{{Main|Modding Tutorials/DefModExtension}}
 
DefModExtensions can be seen as a way to add fields to Defs.
 
  
===Pros===
+
If your mod is restricted to something very small in scope, this might be okay.  But if it affects a large part of the game...you will have to do a lot of work to make it fit, and you might want to go another route.
Very simple, very lightweight, highly compatible.
 
  
===Cons===
 
Static data only.
 
  
===When to use===
 
When you want to add (static) fields/data to Defs.
 
  
=Subclassing=
+
===Older Versions===
{{Main|Modding_Tutorials/Def_classes}}
+
***NOTE: This seems to work fine in 0.18.1722, so you can ignore this section***
Inherit from a Def and explicitly tell RimWorld to use that specific Type.  
 
  
==Pros==
+
Def classes require a pretty hefty overhaul of XML code to work. First of all you will have to change anything in the inheritance to refer to the same class:<br/>
They're you're own Type, so you can extend their functionality until your heart is content.
 
  
==Cons==
+
<source lang="xml"><?xml version="1.0" encoding="utf-8"?>
* Still bound to the base Def without access to its private methods
+
<ThingDefs>
* Using them requires a lot of casting
+
<thingDef Name="BaseThing" Class="myNamespace.myCustomClass">
* Only one custom class per Def
+
</thingDef>
* They don't offer a lot of extra functionality over DefModExtensions or [https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods C# Extension methods]
 
  
==When to use==
+
<thingDef Name="BaseSpecificThing" ParentName="BaseThing" Class="myNamespace.myCustomClass">
When neither a Comp nor a DefModExtension works.
+
</thingDef>
  
=Custom Def=
+
<thingDef ParentName="BaseSpecificThing" Class="myNamespace.myCustomClass">
{{Main|Modding Tutorials/Def_classes}}
+
</thingDef>
(Optionally) inherit from a Def and tell RimWorld to use your custom Def.
+
</ThingDefs></source><br/>
  
==Pros==
+
And in case you have more thingDefs which require the changes that come with myNamespace.myCustomClass you'll have to set all of their classes for it to work.<br/>
Fully your own Type, complete control. No incompatibility issues, because they're all yours.
+
Applied to core defs this way of doing things introduces incompatibilities with other mods that modify the same def. Creating compatibility patches is inevitable.<br/><br/>
  
==Cons==
+
On the plus side you make it possible to change the root thingDef tags. In most cases you don't need this but certain things such as Pawn code might have no alternatives.<br/><br/>
Implementation from scratch.
 
  
==When to use==
+
==Custom comp class==
If your Def is really unique to your mod or specific goal.
 
  
=Other ways=
+
Comp classes are a more advanced method of introducing compatibility to mods. Large mods might prefer comps to defs because it requires less XML editing in most cases. Along with that it's possible to inject comps into almost everything (because almost everything inherits from ThingWithComps) without having to change the comp while defs require a new class for each SomeDef they overwrite (E.g PawnKindDef and ThingDef require different classes)<br/><br/>
Sometimes creative, sometimes hacky:
 
==Checking for tags==
 
  
Instead of using custom defClasses and comps you could also use tags. This is especially useful for lightweight features and simple compatibility.<br/><br/>
+
===Pros and cons===
  
Some tags are never used by a certain Thing such as ApparelTag on a Building. If a tag is never used it doesn't throw an error and therefore you could introduce as many useless tags as you want to a mod without the game complaining. When other mods check for these tags they can also do it without a problem. This way you could add tags of whatever name you want and let others check for this tag for compatibility.<br/><br/>
+
Comps are easier to add than defClasses and can be injected by C# code. It's lighter on the XML code (doesn't require inheritance to be consistent) and C# code in most cases because it's more of a contained system. There's many hooks implemented which call for C# comp classes, such as CompTick() and PostDestroy(). Just like defs they can introduce incompatibilities. Adding another mod's comps requires the game to have both mods loaded, which can be a problem. Implementing smarter injection might fix this<sup>[?]</sup> in case a mod isn't loaded, but it's not possible to guarantee that the comp is actually added.<br/><br/>
  
===Pros===
+
===Method===
Lightweight, easy.
+
See [[Modding Tutorials/Custom Comp Classes|Custom Comp Classes]]
  
===Cons===
+
==Checking for tags==
Risk of potential side-effects. Kinda hacky.
 
  
==Changing the class used by the Def==
+
Instead of using custom defClasses and comps you could also use tags. This is especially useful for lightweight features and simple compatibility.<br/><br/>
A lot of ''Defs'' have a field entry that specifies the C# class they are tied to. For instance, the ''GameConditionDef'' for the Eclipse has a ''conditionClass'' of ''GameCondition_Eclipse''. Modders who wish to add some sparkles to the Eclipse could create a new GameCondition class called ''GameCondition_EclipseWithSparkles'' and use XPath to change the conditionClass to MyNameSpace.GameCondition_EclipseWithSparkles.
 
  
===Pros===
+
===Pros and cons===
Pretty easy. Keeps most values of the original Def intact as well.
 
  
===Cons===
+
Some tags are never used by a certain Thing such as ApparelTag on a Building. If a tag is never used it doesn't throw an error and therefore you could introduce as many useless tags as you want to a mod without the game complaining. When other mods check for these tags they can also do it without a problem. This way you could add tags of whatever name you want and let others check for this tag for compatibility.<br/><br/>
Subverts any Harmony patches on the original Class, so potential issues with compatibility.
 
  
==Harmony patching==
+
===Method===
{{Main|Modding Tutorials/Harmony}}
 
If you still want to use Harmony in the face of all these alternatives, you might have fallen for the [https://exceptionnotfound.net/the-golden-hammer-anti-pattern-primers/ Golden Hammer Design Pattern]. But sure, go ahead.
 
  
 
=See also=
 
=See also=

Please note that all contributions to RimWorld Wiki are considered to be released under the CC BY-SA 3.0 (see RimWorld Wiki:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!

Cancel Editing help (opens in new window)