[B18-1.0] Monster Mash (v4.0.4) (Changed Frameworks and Dependancies)

Started by ilikegoodfood, February 01, 2018, 09:40:33 AM

Previous topic - Next topic

wwWraith

#60
Quote from: ilikegoodfood on February 06, 2018, 12:59:34 PM
Quote from: wwWraith on February 06, 2018, 12:57:20 PM
Quote from: ilikegoodfood on February 06, 2018, 12:37:57 PM
FIXED: It doesn't cause an error when it is left in the definitions, so I don't need that PatchOperationAdd at all.
I was just going to write it :) But there could be other way: still patching but using UnfinishedAnimalPart from ADS instead of MonsterMash_ADSP_UnfinishedAnimalPart which can be removed (it will be safe as UnfinishedAnimalPart won't be abstract anyway).

The problem isn't that it won't work when A Dog Said is installed, but I need it to work even when A Dog Said ISN'T installed.
This means that I cannot directly reference anything in A Dog Said...
But you'll do it in the patch sequence (as it was) so it'll be used only if ADS is loaded. If not, your MonsterMash_ADSP_ProstheticAnimalCraftBase and MonsterMash_ADSP_BionicAnimalCraftBase will just remain without <unfinishedThingDef> but it won't matter.


<li Class="PatchOperationAdd">
<xpath>Defs/RecipeDef[@Name = "MonsterMash_ADSP_ProstheticAnimalCraftBase" or @Name = "MonsterMash_ADSP_BionicAnimalCraftBase"]</xpath>
<value>
<unfinishedThingDef>UnfinishedAnimalPart</unfinishedThingDef>
<recipeUsers>
<li>TableAnimalProsthetics</li>
</recipeUsers>
</value>
</li>


But hopefully it all won't matter if you'll get rid of these bases entirely :)
Think about it. Think around it. Perhaps you'll get some new good idea even if it would be completely different from my words.

ilikegoodfood

I had accidental not enable my version of the Unfinished Animal Item. It was using A Dog Said...'s all along.
Also, that line of code that you are quoting at me didn't need to exist either. It's long gone.

I'm working my way through it and so far I haven't found a base that couldn't be swapped back to A Dog Said...'s version. I just have to spend a few minutes going through and vigorously testing them all as I go.

I'll let you know when I'm done.

ilikegoodfood

#62
Okay, all duplicate bases have been removed and everything still works perfectly both with and without A Dog Said... installed.

I have updated Monster Mash (WIP).zip again and here is the new link.
Dropbox doesn't seem to like keeping a link when the file is over-written.

I need to add a Bionic Beak and Siphon and then it's ready for release as V1.1.1.

I should probably also add a MM_ suffix to everything to prevent compatibility issues, but I'm not sure it's worth the effort. What do you think?

ilikegoodfood

For the information of others, and so that we can sort out formatting, wording etc. before publishing a guide for people.

How to Make an All-Inclusive Compatibility Patch
Create all of the definitions for the new items exactly as you normally would, assuming that the mod it aimed at is installed.
Make sure to set ALL definitions to Abstract, using Abstract = "True":
<ExampleDef Abstract="True">...</ExampleDef>

Create a Patch file with the usual PatchOperationSequence.
Make sure to include the ModCheck.isModLoaded and ModCheck.loadOrder tests.
<Patch>

<Operation Class="PatchOperationSequence">
<success>Always</success>
<operations>
<!-- Continue if A Dog Said... exists -->
<li Class="ModCheck.isModLoaded">
<modName>Example Target Mod</modName>
<yourMod>Example Mod</yourMod>
<customMessageSuccess>Example Mod :: Example Target Mod found...</customMessageSuccess>
</li>

<li Class="ModCheck.loadOrder">
<modName>Example Target Mod</modName>
<yourMod>Example Mod</yourMod>
<errorOnFail>true</errorOnFail>
</li>


Create two new PatchOperationSequence nested inside the primary PatchOperationSequence.
The 1st Sequence will contain the PatchOperationAttributeAdd commands.
<li Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationAttributeAdd">
<xpath>Defs/ExampleDef[defName = "ExampleDef"]</xpath>
<attribute>ParentName</attribute>
<value>exampleParentDef</value>
</li>
</operations>
</li>


NOTE: If the Def that you created is intended to remain abstract, it should be named using the Name="ExampleDef" Attribute and you will need to replace the "[defName =" with "[@Name =". This tells the xpath to check the name attribute instead of the sub-node "defName".

If a Def has a parent that is contained in the Example Target Mod, remove the ParentName="exampleParentDef" attribute from the ExampleDef and set it to be added by the patch using the above method.
If the exampleParentDef is in Core or your own mod (Example Mod), it doesn't need to be removed.

Next, the second nested PatchOperationSequence is to remove the Abstract = "True" Attribute from the Example Def.
<li Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationAttributeRemove">
<xpath>Defs/ExampleDef[defName = "ExampleDef"]</xpath>
<attribute>Abstract</attribute>
</li>
</operations>
</li>


Repeat this process of removing and patching the ParentName Attribute if the Parent is from the target mod (Example Target Mod) and removing the Abstract = "True" property.
Remember to be specific when removing this tag, since you may have your own baseDefs that should remain abstract even after patching.

Finally, certain cross-references will fail, flagging errors on load, due to the order in which they are converted from Abstract to not-Abstract.
For each instance of this error, remove the offending cross-reference from the Def in question and re-add it as part of the patch, creating a new PatchOperationSequence at the very end of the ExamplePatch.xml document.
<li Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationAdd">
<xpath>Defs/ExampleDef[defName = "ExampleDef"]</xpath>
<value>
<ExampleCrossReference>exampleCrossReferenceDef</ExampleCrossReferance>
</value>
</li>
</operations>
</li>


Tidy up any typos, missing lines, close the document and enjoy your all inclusive mod patch.

It is very strongly advised to add each item individually if you can and test them thoroughly.
Do not include multiple types of PatchOperation inside of each PatchOperationSequence.
Ensure that you are familiar with your mod, the target mod and how to perform advanced xpath patches.
There are a number of good resources available on the subject of xpath patching, including this guide by Zhentar.
Also, if you are unsure what your xpath should look like, you can test it using online xpath checkers. A search engine will provide you with several good ones to choose from.
Make sure to properly read and implement the sections on optimization and multi-selection in your patch, otherwise the vast number of patch operations will hugely increase loading times for RimWorld (I used one sequence for ParentNames and one sequence for Abstract removal per xml file).

And now, for reference, here is my ADogSaidExpansionPatch.xml (06 Feb 2018 - 19:12GMT) from this mod:
<?xml version="1.0" encoding="utf-8" ?>

<Patch>

<Operation Class="PatchOperationSequence">
<success>Always</success>
<operations>
<!-- Continue if A Dog Said... exists -->
<li Class="ModCheck.isModLoaded">
<modName>A Dog Said...</modName>
<yourMod>Monster Mash</yourMod>
<customMessageSuccess>Monster Mash :: A Dog Said... found: Generating cephalopod prosthetics...</customMessageSuccess>
</li>

<li Class="ModCheck.loadOrder">
<modName>A Dog Said...</modName>
<yourMod>Monster Mash</yourMod>
<errorOnFail>true</errorOnFail>
</li>

<!-- ====== Hediffs_AnimalBodyParts ====== -->

<li Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationAttributeAdd">
<xpath>Defs/HediffDef[defName = "MonsterMash_ADSP_SimpleProstheticTentacleAnimal" or defName = "MonsterMash_ADSP_BionicTentacleAnimal" or defName = "MonsterMash_ADSP_BionicAnimalGill"]</xpath>
<attribute>ParentName</attribute>
<value>addedPartAnimal</value>
</li>
</operations>
</li>

<li Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationAttributeRemove">
<xpath>Defs/HediffDef[defName = "MonsterMash_ADSP_SimpleProstheticTentacleAnimal" or defName = "MonsterMash_ADSP_BionicTentacleAnimal" or defName = "MonsterMash_ADSP_BionicAnimalGill"]</xpath>
<attribute>Abstract</attribute>
</li>

<li Class="ModCheck.isModLoaded">
<modName>A Dog Said...</modName>
<yourMod>Monster Mash</yourMod>
<customMessageSuccess>Monster Mash :: Enabling cephalopod prosthetics: HediffDefs unlocked...</customMessageSuccess>
</li>
</operations>
</li>

<!-- ========== Items_BodyParts ========== -->

<li Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationAttributeAdd">
<xpath>Defs/ThingDef[defName = "MonsterMash_ADSP_SimpleProstheticTentacleAnimal" or defName = "MonsterMash_ADSP_BionicTentacleAnimal" or defName = "MonsterMash_ADSP_BionicAnimalGill"]</xpath>
<attribute>ParentName</attribute>
<value>BodyPartAnimalArtificialBase</value>
</li>
</operations>
</li>

<li Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationAttributeRemove">
<xpath>Defs/ThingDef[defName = "MonsterMash_ADSP_SimpleProstheticTentacleAnimal" or defName = "MonsterMash_ADSP_BionicTentacleAnimal" or defName = "MonsterMash_ADSP_BionicAnimalGill"]</xpath>
<attribute>Abstract</attribute>
</li>

<li Class="ModCheck.isModLoaded">
<modName>A Dog Said...</modName>
<yourMod>Monster Mash</yourMod>
<customMessageSuccess>Monster Mash :: Enabling cephalopod prosthetics: Item versions of BodyParts ThingDefs unlocked...</customMessageSuccess>
</li>
</operations>
</li>

<!-- ======== Recipes_AnimalParts ======== -->

<li Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationAttributeAdd">
<xpath>Defs/RecipeDef[defName = "MonsterMash_ADSP_MakeSimpleProstheticTentacleAnimal"]</xpath>
<attribute>ParentName</attribute>
<value>ProstheticAnimalCraftBase</value>
</li>

<li Class="PatchOperationAttributeAdd">
<xpath>Defs/RecipeDef[defName = "MonsterMash_ADSP_MakeBionicTentacleAnimal" or defName = "MonsterMash_ADSP_MakeBionicAnimalGill"]</xpath>
<attribute>ParentName</attribute>
<value>BionicAnimalCraftBase</value>
</li>
</operations>
</li>

<li Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationAttributeRemove">
<xpath>Defs/RecipeDef[defName = "MonsterMash_ADSP_MakeSimpleProstheticTentacleAnimal" or defName = "MonsterMash_ADSP_MakeBionicTentacleAnimal" or defName = "MonsterMash_ADSP_MakeBionicAnimalGill"]</xpath>
<attribute>Abstract</attribute>
</li>

<li Class="ModCheck.isModLoaded">
<modName>A Dog Said...</modName>
<yourMod>Monster Mash</yourMod>
<customMessageSuccess>Monster Mash :: Enabling cephalopod prosthetics: Item body part crafting recipe RecipeDefs unlocked...</customMessageSuccess>
</li>
</operations>
</li>

<!-- ========== Recipes_Surgery ========== -->

<li Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationAttributeAdd">
<xpath>Defs/RecipeDef[@Name = "MonsterMash_ADSP_TentacleBaseAnimal"]</xpath>
<attribute>ParentName</attribute>
<value>SurgeryFleshAnimal</value>
</li>
</operations>
</li>

<li Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationAttributeRemove">
<xpath>Defs/RecipeDef[defName = "MonsterMash_ADSP_InstallSimpleProstheticTentacleAnimal" or defName = "MonsterMash_ADSP_InstallBionicTentacleAnimal" or defName = "MonsterMash_ADSP_InstallBionicAnimalGill"]</xpath>
<attribute>Abstract</attribute>
</li>

<li Class="ModCheck.isModLoaded">
<modName>A Dog Said...</modName>
<yourMod>Monster Mash</yourMod>
<customMessageSuccess>Monster Mash :: Enabling cephalopod prosthetics: Installation surgeries RecipeDefs unlocked...</customMessageSuccess>
</li>
</operations>
</li>

<!-- ===== Implementing out-of-order Cross References ===== -->

<li Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationAdd">
<xpath>Defs/HediffDef[defName = "MonsterMash_ADSP_SimpleProstheticTentacleAnimal"]</xpath>
<value>
<spawnThingOnRemoved>MonsterMash_ADSP_SimpleProstheticTentacleAnimal</spawnThingOnRemoved>
</value>
</li>

<li Class="PatchOperationAdd">
<xpath>Defs/HediffDef[defName = "MonsterMash_ADSP_BionicTentacleAnimal"]</xpath>
<value>
<spawnThingOnRemoved>MonsterMash_ADSP_BionicTentacleAnimal</spawnThingOnRemoved>
</value>
</li>

<li Class="PatchOperationAdd">
<xpath>Defs/HediffDef[defName = "MonsterMash_ADSP_BionicAnimalGill"]</xpath>
<value>
<spawnThingOnRemoved>MonsterMash_ADSP_BionicAnimalGill</spawnThingOnRemoved>
</value>
</li>

<li Class="ModCheck.isModLoaded">
<modName>A Dog Said...</modName>
<yourMod>Monster Mash</yourMod>
<customMessageSuccess>Monster Mash :: Cephalopod prosthetics completed.</customMessageSuccess>
</li>
</operations>
</li>

</operations>
</Operation>
</Patch>

wwWraith

#64

XML error: Could not find parent node named "MonsterMash_ADSP_SurgeryFleshAnimal" for node "RecipeDef". Full node: <RecipeDef ParentName="MonsterMash_ADSP_SurgeryFleshAnimal" Abstract="True"><defName>MonsterMash_ADSP_InstallBionicAnimalGill</defName>
but I hope you already dealt with it :)

And those 2 mysterious errors revealing with <success>Normal</success> are still there. It's not good but I don't know what else can be done with them now.

About prefixes: it'll matter only in the case that some other mod will use the same names so their contents will depend on the loading order. It may be both for good (there won't be, for example, several different tentacles that could confuse player which one is for which creature) and for bad (overwritten stats could lead to issues). I think the bad consequences in such situations are more probable and would be harder to fix, while dealing with several similar items won't be hard by patching after revealing this issue. So I'd say the prefixes are more good than bad, though are not really required. But at least I'd suggest you to change existing "MonsterMash_" to "MM_" :) And if you'll decide to use them you'd better to do it earlier than later so it won't take the extra efforts in the future.

Regarding the guide:
1. It'd be good to add an introduction describing what's the point of all this and why working with "dynamically/conditionally created defs" by XML-only means is not trivial.
2. Mention that this method requires using ModCheck (however I think in most cases the checks could be made without ModCheck, by PatchOperationTest based on some unique def existing in Example Target Mod) and ModCheck.isModLoaded is sometimes used for custom messages.
3. Mention the probability of concurrent execution of checks and/or sequences.
4. Mention that using <success>Normal</success> reveals errors that seem to not be harmful, but maybe someone will have some more info on that.
5. Some more investigation could be made for details about what exact differences in operations prevent them from being used in one sequence ("Do not include multiple types of PatchOperation inside of each PatchOperationSequence." but we saw that sometimes different operations worked fine). And probably this info should be put higher, so it'll explain why the several sequences must be created.
6. Other instruments for working with xpath include plugins for popular text editors such as Notepad++ and SublimeText.
Think about it. Think around it. Perhaps you'll get some new good idea even if it would be completely different from my words.

ilikegoodfood

#65
The guide was a prototype for the exact reason of you providing feedback.

All of the feedback you have given is valid and I will implement it, however, I am unlikely to get to that today.

Thank you again for the immense amount of time and effort you have put into helping me make this a reality.

EDIT: And thanks for that bug. I had missed it somehow, but it is fixed now.

wwWraith

No problem, it was interesting and providing good experience for us both :)
Think about it. Think around it. Perhaps you'll get some new good idea even if it would be completely different from my words.

ilikegoodfood

#67
If I systematically add a "MM_" prefix to all of my defNames and @Names, how will that effect the 100 subscribers who may be using it in game?

Will the game recognize the change and adjust accordingly, or will it break their saves?

And if it will break their saves, is there any way other than removing my mod from the Steam Workshop entirely, which may also break their saves, to ensure that they all follow safe removal procedures prior to the update?

ilikegoodfood

#68
2nd draft of the guide:

xml-Only Dynamic Definitions
What are dynamic definitions and why should I (the reader) care?
Currently, using ModCheck and the xpath operations that it provides and expands upon, it is possible to perform simple patches to a mod's definitions at run-time, in response to the presence or absence of certain other mods.

Elements of this conversation will be easier to explain using an example. This example will be persistent throughout this guide:
Let's say that you have made a mod that adds a new fruit to RimWorld and that you want it to be compatible with a wildly popular mod that adds Jam.
This hypothetical Jam mod adds a new workstation, a recipe to turn each fruit (there are only three in vanilla RimWorld) into its very own jam, and one additional recipe for mixed-fruit jam.

Using normal patching techniques, it is fairly easy to detect the Jam mod and add your fruit to the list of allowed ingredients for the mixed jam.

This guide is for an advanced and experimental mod-patching method. There are many good guides and decent examples of patches using ModCheck already available.

The limitations with standard patching don't start to emerge until you try to add a recipe for the jam made from your own fruit.
You might first encounter the issue that you can't patch in root definitions, such as a RecipeDef, from scratch. A targetable Def with an appropriate name is required first.
You might then try adding a the RecipeDef (to make the jam) and ThingDef (to have the jam item) to your own mod, that uses the Jam mod's baseJam definition.

This is all very well and good until you then run a test without the Jam mod installed and find that your new definitions are throwing up cross-referencing errors, such as: Cannot find ParentName.

This error occurs because the Parent is a file from another mod, one which isn't currently installed. Many modders may be tempted to copy its base definitions into their own mod (which is very bad practice and hasn't been required since a16), or to create their own version of the parent (which is also bad practice).
While this might solve the immediate problem, the game still won't be able to cross reference the workstation that is supposed to receive the recipeDef or find the graphic that is added by the Jam mod.

This is where "dynamic definitions" come in.
What I mean by a dynamic definition, is a definition that is selectively edited down to remove all cross-referencing issues with other mods and then patched at run-time to a working state only when the other mod is detected.

How to Make a Dynamic Definition
First off, a word of warning: Working with dynamic definitions is by no means a trivial task, requiring more work than creating a compatibility sub-mod. There are a number of unknown behaviors, mechanisms and errors at play, which are all solvable, that I will attempt to explain and justify as we work through this guide.

Your first Dynamic Definition
The first step is to create the definition, exactly as you normally would and assuming that the Jam mod (or other compatibility target) is installed.
It might look something like this:
<RecipeDef ParentName="makeJamBase">
<defName>makeBlackberryJam</defName>
<label>make blackberry jam</label>
<description>Make a delicious blackberry jam.</description>
<jobString>Making delicious blackberry jam.</jobString>
<ingredients>
<li>
<filter>
<thingDefs>
<li>blackberry</li>
</thingDefs>
</filter>
<count>25</count>
</li>
</ingredients>
<fixedIngredientFilter>
<thingDefs>
<li>blackberry</li>
</thingDefs>
</fixedIngredientFilter>
<products>
<BlackberryJam>1</BlackberryJam>
</products>
<skillRequirements>
<Cooking>5</Cooking>
</skillRequirements>
<workSkill>Cooking</workSkill>
</RecipeDef>


If you tried to run your mod at this stage, it would cause several errors. makeJamBase is from a mod that isn't necessarily installed and we haven't made the item BlackberryJam yet (We will ignore this error in this guide, since you should be able to comment it out for testing and create the ThingDef for BlackberryJam).

To prevent the recipe from causing errors, you will need to remove the ParentName attribute (don't forget what the parent should be! You will need it soon). This will lead to the RecipeDef being incomplete, since it is no longer inheriting properties from its parent.

The game already has a method for handling incomplete data, but it is primarily used for the base definitions themselves; Abstract = "True"

Abstract = "True"
Abstract definitions are a bit of a special case. These definitions are loaded while the game is loading, but they are not error-checked nearly as rigorously, they do not need to be complete and, most importantly of all, they are discarded when loading completes.

Once the game is loaded, that definition essentially doesn't exist within the game.
You won't get errors from it, unless the error is passed on to its children, you won't be able to use it and you won't find it in the developer-mode tools.

This is key to the implementation of Dynamic Definitions.
If the Jam Mod isn't installed, the BlackberryJam simply doesn't exist.

At this stage, your RecipeDef would look like this:
<RecipeDef Abstract="True">
<defName>makeBlackberryJam</defName>
<label>make blackberry jam</label>
<description>Make a delicious blackberry jam.</description>
<jobString>Making delicious blackberry jam.</jobString>
<ingredients>
<li>
<filter>
<thingDefs>
<li>blackberry</li>
</thingDefs>
</filter>
<count>25</count>
</li>
</ingredients>
<fixedIngredientFilter>
<thingDefs>
<li>blackberry</li>
</thingDefs>
</fixedIngredientFilter>
<!--<products>
<BlackberryJam>1</BlackberryJam>
</products>-->
<skillRequirements>
<Cooking>5</Cooking>
</skillRequirements>
<workSkill>Cooking</workSkill>
</RecipeDef>


The Patch
I'm assuming here that you have followed other people's guides and already know how to patch.
If this is not the case, there are several good resources, including A quick tutorial of xpathing and patching and [A17] A warning to modders: xpath performance, which are both pinned at the top of the Help sub-section of the modding forum, and Introduction to PatchOperation by Zhentar.

Any mod-dependent patch should contain a basic test to make sure that the target mod is installed and is above this one in the loading order. The opening section of a patch file usually looks something like this:
<?xml version="1.0" encoding="utf-8" ?>

<Patch>

<Operation Class="PatchOperationSequence">
<success>Always</success>
<operations>
<!-- Continue if Yummy Jams exists -->
<li Class="ModCheck.isModLoaded">
<modName>Yummy Jams</modName>
<yourMod>Blackberry Bonanza</yourMod>
<customMessageSuccess>Blackberry Bonanza :: Yummy Jams detected: Patching...</customMessageSuccess>
</li>

<li Class="ModCheck.loadOrder">
<modName>Yummy Jams</modName>
<yourMod>Blackberry Bonanza</yourMod>
<errorOnFail>true</errorOnFail>
</li>


There are several things here that need explaining:
Quote<success>Always</success>
In a PatchOperationSequence, it will usually run until there is an error, report it, and then stop. That would be if <success>Normal</success> was used. However, when set to <success>Normal</success>, errors are logged that don't appear to do any harm or prevent the patch from working and the PatchOperationSequence is stopped unnecessarily.
This is why most mod patches unquestioningly use <success>Always</success>.

Quote<customMessageSuccess>Blackberry Bonanza :: Yummy Jams detected: Patching...</customMessageSuccess>
ModCheck.isModLoaded is used to test if the required mod is installed, but it also allows you to send a custom message to the Debug Log.
It can be very useful to add duplicates, each with different custom messages, for testing purposes. If the message doesn't reach the Debug Log, then you know that there is a serious error between the last message that was sent and the one that wasn't sent.
Be aware though, that these messages do not appear in the Debug Log in the order that they are written in the patch. Check carefully.
There may be a solution for this (Combat Extended seems to have a strict message order), but I do not know what it is.

Next, we create two sub-sequences.

Wait, but can't it be done in a single sequence?
Well, the answer to that is "Sometimes".
In some patches and in some combinations it seems to work just fine, but at other times it will fail if there are different PatchOperations in the same sequence. The behavior appears highly inconsistent, so its best to make sure that each PatchOperationSequence only contains one type of PatchOperation.
The primary PatchOperationSequence, the one that spans the entire page, is therefore a sequence of PatchOperationSequence(s), and each sub-sequence handles a specific task.

So, back to our two sub-sequences.
The 1st sub-sequence is there to add the ParentName attribute back in to our definitions, and will look like this:
<li Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationAttributeAdd">
<xpath>Defs/RecipeDef[defName = "makeBlackberryJam"]</xpath>
<attribute>ParentName</attribute>
<value>makeJamBase</value>
</li>
</operations>
</li>


The 2nd sub-sequence then removes the Abstract="True" attribute from the definition, allowing it to be loaded into the game, and will look like this:
<li Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationAttributeRemove">
<xpath>Defs/RecipeDef[defName = "makeBlackberryJam"]</xpath>
<attribute>Abstract</attribute>
</li>
</operations>
</li>


We remove the Abstract attribute after defining the ParentName to avoid receiving errors from the error-check that is performed on a non-abstract definition as a result of the definition still being incomplete
When removing the Abstract attribute, it is important to select the definitions specifically. If you have created your own base definition in that file, it should remain Abstract, otherwise it will cause you a whole new set of errors.

Repetition, repetition, repetition...
From this point forward, you test, develop the remaining required definitions, the patches for them and re-test.
Make sure to un-comment any incomplete cross-references as you complete the definitions, since we still have BlackberryJam as the product commented-out in our example.

Closing Information and Notes
You will also, the exact reasons are unknown, but I believe that it is related to the order by which PatchOperations are performed, occasionally get a persistent cross-reference error with regards to one of your other dynamic definitions. In the case of our example, the ThingDef for BlackberryJam would be a likely error-point.
When that happens, remove the offending reference from the definition and patch them back into the definition using a final PatchOperation sub-sequence at the bottom of your Patch.
It might look like this:
<li Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationAdd">
<xpath>Defs/RecipeDefDef[defName = "makeBlackberryJam"]</xpath>
<value>
<products>
<BlackberryJam>1</BlackberryJam>
</products>
</value>
</li>
</operations>
</li>


Some additional unknowns are associated with the order of file-patching.
The custom messages sent to the Debug Log from different mods do not necessarily appear in the order that the mods are loaded (listed in the mod menu, top-bottom) and sometimes messages from other patch files within your own mod will appear in-between message that are half-way through an ongoing sequence.
While this could be a result of the message order only, it may also be that different patches from mods, different files or possibly even different sequences are performed out of order in some way.

To avoid this unknown operation order from impacting your dynamic definitions, they should all be patched inside of the Primary PathOperationSequence and all in the same file.

Further xpath resources available to you include online xpath checkers and xpath plugins for popular text editors such as Notepad++ and SublimeText.
You can also use my Monster Mash mod as an example framework (Steam Workshop) (Forum). It is the mod that I, ilikegoodfood, along with significant time and assistance from wwWraith, developed this patching system for and it uses the system to dynamically implement new artificial animal organs and prosthetics for A Dog Said... compatibility with my Land Kraken.
If you do you use this system, a reference would be highly appreciated, but it is not necessary.

ilikegoodfood

#69
And the 2nd draft of my guide is complete!
What do you think?

I'm also quoting myself so as to prevent this question I need answering from getting berried behind the guide:
Quote from: ilikegoodfood on February 07, 2018, 03:42:58 AM
If I systematically add a "MM_" prefix to all of my defNames and @Names, how will that effect the 100 subscribers who may be using it in game?

Will the game recognize the change and adjust accordingly, or will it break their saves?

And if it will break their saves, is there any way other than removing my mod from the Steam Workshop entirely, which may also break their saves, to ensure that they all follow safe removal procedures prior to the update?

EDIT:
And does creating a custom event still require a .dll assembly?
I'm very much a complete novice at C# and haven't done anything with it in years to boot... :(

wwWraith

Quote from: ilikegoodfood on February 07, 2018, 06:26:28 AM
And the 2nd draft of my guide is complete!
What do you think?

I think it looks fine to be published standalone :)

Quote
If I systematically add a "MM_" prefix to all of my defNames and @Names, how will that effect the 100 subscribers who may be using it in game?

Will the game recognize the change and adjust accordingly, or will it break their saves?

And if it will break their saves, is there any way other than removing my mod from the Steam Workshop entirely, which may also break their saves, to ensure that they all follow safe removal procedures prior to the update?

It will break saves if there are any instances of those defs actually used in the savefile. Of course it could be fixed by renaming them in the savefile but not many of the users would figure it out. So, well, probably it will be better not to rename existing defs for now. The other way could be adding defs with new names but keeping old ones for compatibility, but I don't think it'd worth the effort and will make the code confusing. And I don't know anything regarding Steam.
Think about it. Think around it. Perhaps you'll get some new good idea even if it would be completely different from my words.

ilikegoodfood

I did a little bit of searching earlier to see what would be required to implement a new event, essentially a clone of the Thrumbo event, that could spawn Monsters instead of Thrumbo.

Much to my surprise, all of the information I found is from way back in alphas 3 and 4, but it seems like it'll need xml modding.
Do you happen to know if that's still true or not?

I'll continue my research too, but I've somewhat over-worked myself with the dynamic definitions, so I'll likely give it only a little time and sort of take a few days off.

Harry_Dicks

Good work guys! It's been interested following your journey of discovery :) I'm sure this work will be invaluable to others in the future!

ilikegoodfood

Version 1.1.3 is now out!

The update adds the "Monster Sighting" event, which works just like the "Thrumbo Passes", but for my monsters!
This should allow you to interact with them beyond their normal biomes.

Currently there are only monster for all Forests (Inferno Beetle) and all Swamps (Land Kraken).
I'm trying to think of new monsters for Arid Shrubland, Desert & Extreme Desert, Tundra and Ice Sheet.
Inspiration and suggestions are welcome, just be aware that I'm very likely to not use it, especially if it overly common either thematically or mechanically. :P
And yes, I am overly aware that there simply isn't that much mechanical variation possible for creatures.

Harry_Dicks

#74
I'm having trouble thinking of any mammals that really have some sort of ranged thing they do. Maybe this has to do with a lot of the stuff mammals have equipped on our bodies is meant to be more long term. Like how we can't regenerate limbs, but some reptiles can, and I can't really think of how to word this and I don't want to type a wall but this is sort of a gross oversimplification of how I think about different classes of critters.

Anyway, here's some random terrible spitball ideas:

-Creature that spits a type of "tar" that will slow down whatever it hits for X time

-An animal that has a type of "black ink" like an octopus/squid. This could greatly decrease accuracy of melee and/or ranged attacks for X time.

-Some kind of insectoid that sprays a type of "corrosive acid", similar those ants that can spray something almost a foot away. I'm thinking it would be awesome if this could but a temporary debuff on the target, and will increase any damage they receive from any source for X time. The idea is that this corrosive acid is eating away at whatever apparel/armor/whatever is on this target.

-Some sort of poison dart creature, like certain frogs.

-A type of quill shot from most likely a mammal.

-Some type of finger bone that is shot from an animal? There are these frogs that I saw in a documentary, that will purposefully break the bones near their hands, and then pierce these bones through their skin to act like claws. I can't remember if the claws were then used for mating or defense or what. I can't remember if they could grow them back, either, but I am 100% confident that something like this exists.

-Some kind of "bio-conductive" animal, maybe it's slimy like an eel,  that could zap it's target with electric from a certain distance. Or maybe it creates a small electric field around it, that will harm anything in the area that is not a member of it's species?

-Blood stream that shoots from the eyeball. Like the reptile that does it. Maybe instead of blood it will make the target have a chance to catch a specific disease, or some other interesting hediffs/comps/verbs/whatever they are ::)

Might be interesting if we could have some longer term effects, too. I know it's a lot more interesting to see anything from battles happen while the battle is going on. But it would make for interesting game play, in my opinion, if for example anyone squirted with this blood (or any type of attack from any monster or mod) might have a chance to catch some new diseases. Maybe this will mess with their blood filtration and/or pumping. Speaking of which, I don't think there really are too many things in vanilla game that touch on blood filtration/pumping that really caught my eye. I guess I would need to say I would like to see new diseases/hediffs/comps/verbs or whatever it is you call them. I did download Diseases Overhauled last night. However, it needs the base of Extended Human Body Simulation, which isn't playing nice with RBSE. Guess I'm going to need to go in and modify one of the two, that will probably end up being my project for the day!