There are 11 new "PatchOperation" actions we can perform to modify Defs without copying the whole thing. This in turn improves mod compatability dramatically - it allows two mods to modify different parts of the same Def without conflict. This guide only describes the basics of each operation available to us.
Patches go in xml files in a subfolder named "Patches" in your mod folder (So "MyMod\Patches", just like you might have "MyMod\Defs" or "MyMod\Textures"). Example.
Each PatchOperation has an "xpath" node, which should contain an xpath selector for the xml node(s) that the operation should affect. This is not an xpath tutorial. If you don't know xpath, not my problem. But minimurgle has you covered!
To illustrate the operations, I'll use this simple example def:
<DesignationCategoryDef>
<defName>ExampleCategory</defName>
<label>Example</label>
<specialDesignatorClasses>
<li>Designator_Cancel</li>
</specialDesignatorClasses>
</DesignationCategoryDef>
And my xpath selector in each example will select the specialDesignatorClasses
node.
There are four basic operations: Add, Insert, Remove, Replace.
PatchOperationAdd
adds a child node to the selected node.
<Operation Class="PatchOperationAdd">
<xpath>*/DesignationCategoryDef[defName = "ExampleCategory"]/specialDesignatorClasses</xpath>
<value>
<li>ZhentarExample.ExampleDesignator</li>
</value>
</Operation>
transforms the def into this:
<DesignationCategoryDef>
<defName>ExampleCategory</defName>
<label>Example</label>
<specialDesignatorClasses>
<li>Designator_Cancel</li>
<li>ZhentarExample.Example Designator</li>
</specialDesignatorClasses>
</DesignationCategoryDef>
PatchOperationInsert
adds a sibling to the selected node.
<Operation Class="PatchOperationInsert">
<xpath>*/DesignationCategoryDef[defName = "ExampleCategory"]/specialDesignatorClasses</xpath>
<value>
<description>Pointless example designators.</description>
</value>
</Operation>
transforms the def into this:
<DesignationCategoryDef>
<defName>ExampleCategory</defName>
<label>Example</label>
<specialDesignatorClasses>
<li>Designator_Cancel</li>
</specialDesignatorClasses>
<description>Pointless example designators.</description>
</DesignationCategoryDef>
PatchOperationRemove
removes the selected node.
<Operation Class="PatchOperationRemove">
<xpath>*/DesignationCategoryDef[defName = "ExampleCategory"]/specialDesignatorClasses</xpath>
</Operation>
transforms the def into this:
<DesignationCategoryDef>
<defName>ExampleCategory</defName>
<label>Example</label>
</DesignationCategoryDef>
PatchOperationReplace
replaces the selected node.
<Operation Class="PatchOperationReplace">
<xpath>*/DesignationCategoryDef[defName = "ExampleCategory"]/specialDesignatorClasses</xpath>
<value>
<specialDesignatorClasses/>
</value>
</Operation>
transforms the def into this:
<DesignationCategoryDef>
<defName>ExampleCategory</defName>
<label>Example</label>
<specialDesignatorClasses/>
</DesignationCategoryDef>
There are three operations for attributes: Add, Set, and remove.
PatchOperationAttributeAdd
and PatchOperationAttributeSet
will both add an attribute and set it to the provided value. The difference is that Add will set the value if and only if the attribute is not already present, while Set will overwrite an existing value.
<Operation Class="PatchOperationAttributeSet">
<xpath>*/DesignationCategoryDef[defName = "ExampleCategory"]/specialDesignatorClasses</xpath>
<attribute>ExampleAttribute</attribute>
<value>ExampleValue</value>
</Operation>
transforms the def into this:
<DesignationCategoryDef>
<defName>ExampleCategory</defName>
<label>Example</label>
<specialDesignatorClasses ExampleAttribute="ExampleValue">
<li>Designator_Cancel</li>
</specialDesignatorClasses>
</DesignationCategoryDef>
PatchOperationAttributeRemove
removes an attribute. Our example def doesn't have any attributes to remove, so you'll just have to use your imagination ;-)
Finally, there are four more advanced operations: PatchOperationAddModExtension
, PatchOperationSetName
, PatchOperationSequence
, and PatchOperationTest
.
PatchOperationAddModExtension
is fairly straightforward... it adds a ModExtension. If you want to know what a ModExtension is... you'll need a different guide.
PatchOperationSetName
changes the name of a node to a different name. This operation was only included for completeness; there is no known use case for it.
PatchOperationSequence
and PatchOperationTest
can be used to conditionally perform patches. This is useful for mod compatibility, because it can be used to prevent adding the same element twice. PatchOperationSequence will perform each operation in its list in sequence, aborting if an operation fails. PatchOperationTest simply determines if the xpath selector matches any elements.
This example adds the costList element if it is not already present:
<Operation Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationTest">
<xpath>*/ThingDef[defName = "DiningChair"]/costList</xpath>
<success>Invert</success>
</li>
<li Class="PatchOperationAdd">
<xpath>*/ThingDef[defName = "DiningChair"]</xpath>
<value>
<costList />
</value>
</li>
</operations>
</Operation>
This section will explain some situations where we can apply some more advanced techniques.
This will be our reference.
<Defs>
<BlahDef Name="BlahBase" Abstract="True">
<label>Blah</label>
</BlahDef>
<BlahDef>
<defName>Zip<defName>
<bleh>50<bleh>
</BlahDef>
<BlahDef>
<defName>Zop<defName>
<bleh>50<bleh>
</BlahDef>
<BlahDef>
<defName>Zap<defName>
<bleh>50<bleh>
</BlahDef>
</Defs>
What if you wanted to patch an abstract like LocalInjuryBase
or BuildingBase
? Obviously the xpath would need a few modifications. Let's try adding <category>Item</category>
to the above BlahBase
.
<Operation Class="PatchOperationAdd">
<xpath>*/BlahDef[@Name="BlahBase"]</xpath>
<value><category>Item</category></value>
</Operation>
Result:
<Defs>
<BlahDef Name="BlahBase" Abstract="True">
<label>Blah</label>
<category>Item</category>
</BlahDef>
<BlahDef>
<defName>Zip<defName>
<bleh>50<bleh>
</BlahDef>
<BlahDef>
<defName>Zop<defName>
<bleh>50<bleh>
</BlahDef>
<BlahDef>
<defName>Zap<defName>
<bleh>50<bleh>
</BlahDef>
</Defs>
The @Name="BlahBase"
selector tests a BlahDef to see if it has an attribute called Name, and if so, if the value of that attribute is BlahBase
.
If you're going to do the same thing to multiple defs, did you know that you can condense your xpath into 1 patch?
This example below sets the bleh
value on all 3 BlahDef
s to 20.
<Operation Class="PatchOperationReplace">
<xpath>*/BlahDef[defName="Zip" or defName="Zop" or defName="Zap"]/bleh</xpath>
<value><bleh>20</bleh></value>
</Operation>
Result:
<Defs>
<BlahDef Name="BlahBase" Abstract="True">
<label>Blah</label>
</BlahDef>
<BlahDef>
<defName>Zip<defName>
<bleh>20<bleh>
</BlahDef>
<BlahDef>
<defName>Zop<defName>
<bleh>20<bleh>
</BlahDef>
<BlahDef>
<defName>Zap<defName>
<bleh>20<bleh>
</BlahDef>
</Defs>
Notice how the or
operator is used here.
Condensing 3 PatchOperations into 1 can have a dramatic impact on your mod's load time, as the game only has to pass through the xml database once.
Hiya, great stuff
The examples show working with an individual record (specified by defName) within a collection; is it possible to select a node that represents a collection and then add or insert child nodes that are new records to the collection?