A quick tutorial of xpathing and patching

Started by minimurgle, May 24, 2017, 08:27:11 PM

Previous topic - Next topic

Garwel

#90
I'm trying to write a patch to add a comp to all items (i.e. ThingDefs with category set to "Item"). I use what seems to be an obvious solution:
<Operation Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationTest">
<xpath>/Defs/ThingDef[category="Item"]/comps</xpath>
<success>Invert</success>
</li>
<li Class="PatchOperationAdd">
<xpath>/Defs/ThingDef[category="Item"]</xpath>
<value>
<comps/>
</value>
</li>
</operations>
</Operation>
<Operation Class="PatchOperationAdd">
<success>Always</success>
<xpath>/Defs/ThingDef[category="Item"]/comps</xpath>
<value>
<li>
<compClass>DestroyItem.CompDestructible</compClass>
</li>
</value>
</Operation>


However, it mysteriously only applies to few items such as Apparel_ShieldBelt. I also tried to target abstract ThingDefs (e.g. with @Name="ApparelBase"), but the result is the same. Any ideas?

PS: I did some research and found that this code actually kind of works. But there are some ThingDefs it won't affect no matter what you do, because they are apparently not present in any XML files and are generated dynamically by the game. These include various types of meat, corpses, skill trainers etc. So I had to use C# instead to add the comp in code:
foreach (ThingDef def in DefDatabase<ThingDef>.AllDefs.Where(def => typeof(ThingWithComps).IsAssignableFrom(def.thingClass) && def.category == ThingCategory.Item).ToList())
    def.comps.Add(new CompProperties(typeof(CompDestructible)));

It seems to work ok.

LWM

A couple of notes on your xpaths:

First, the PatchOperationTest is mostly superfluous - the PatchOperationAdd will only affect things that match the xpath (and if you set <success>Always</success> you won't get an error message if nothing matches)

Second...what happens when something already has a set of comps?  You might want to do something like

  <PatchOperation Class="PatchOperationAdd">
    <xpath>/Defs/ThingDef[category="Item" and not(comps)]</xpath><!--only add comps if not there-->
    <value><comps /></value>
  </PatchOperation>

and then add your comp.

...I have often found that just going through the DefDatabase in C# is the easier approach :shrug:

Joedox

Hello Modders,
I have a question regarding the translation of xpaths. I would like to be able to display a translation only if the mod is present and without generating an error.

Example:
The mod Vanilla Factions Expanded - Mechanoids adds a new coffee factory if the mod Vanilla Brewing Expanded - Coffees and Teas is present.

If I simply translate the lines from the Patches folder and add those lines to DefInjected in the Vanilla Factions Expanded - Mechanoids mod it will give me errors if the Vanilla Brewing Expanded - Coffees and Teas mod is not present. The game will tell me that the coffee factory does not exist.
If I use PatchOperationReplace, I am unnecessarily doubling the modification and will generate a missing translation error.

Is it possible to use the xpath for the translation? So that it is only loaded if the mod is present and without error.

Thanks for your answers.

Shinzy

One way you can do what you want is with the LoadFolders.xml
clicky here for the official google docs instructions how to use them

anyhow, you can specify a folder full of patches and xmls and whatnot to be loaded only if a specific mod is active

Joedox

#94
Hello Shinzy,

Thank you for your answer, I'm going to test this famous : LoadFolders.xml


Edit :
Wouhou ! Thanks for your help with the LoadFolders.xml file, the translations in the Patches folder are only injected when the Mod is present and I don't have any error Found no ...
Thanks a lot for your help !

WinnieTheJinping

Hi there,

So I'm trying to patch the statBases of the Human pawn, but so far while the xpath is correct, it doesn't patch it nor throw up any errors.

<?xml version="1.0" encoding="utf-8" ?>
<Patch>
<Operation Class="PatchOperationSequence">
      <operations>
        <!-- Makes ALL pawns superhumans -->
        <li Class="PatchOperationAdd">
          <xpath>
            Defs/ThingDef[defName="Human"]/statBases
          </xpath>
          <value>
            <!-- General -->
            <GlobalLearningFactor>10000</GlobalLearningFactor>
            <RestRateMultiplier>100</RestRateMultiplier>
            <EatingSpeed>10000</EatingSpeed>
            <ImmunityGainSpeed>10000</ImmunityGainSpeed>
            <InjuryHealingFactor>10000</InjuryHealingFactor>
            <CarryingCapacity>10000</CarryingCapacity>
            <ForagedNutritionPerDay>100</ForagedNutritionPerDay>
            <FilthRate>0</FilthRate>
            <!-- Social Stats -->
            <NegotiationAbility>10000</NegotiationAbility>
            <PawnBeauty>10000</PawnBeauty>
            <ArrestSuccessChance>10000</ArrestSuccessChance>
            <TradePriceImprovement>10000</TradePriceImprovement>
            <DrugSellPriceImprovement>10000</DrugSellPriceImprovement>
            <SocialImpact>10000</SocialImpact>
            <TameAnimalChance>10000</TameAnimalChance>
            <TrainAnimalChance>10000</TrainAnimalChance>
            <BondAnimalChanceFactor>10000</BondAnimalChanceFactor>
            <!-- Medical -->
            <MedicalTendSpeed>10000</MedicalTendSpeed>
            <MedicalTendQuality>10000</MedicalTendQuality>
            <MedicalOperationSpeed>10000</MedicalOperationSpeed>
            <MedicalSurgerySuccessChance>10000</MedicalSurgerySuccessChance>
            <!-- General Work -->
            <WorkSpeedGlobal>1</WorkSpeedGlobal> <!-- I don't recommend making this higher-->
            <MiningSpeed>10</MiningSpeed> <!-- Also this one -->
            <DeepDrillingSpeed>10000</DeepDrillingSpeed>
            <MiningYield>10</MiningYield> <!-- Any higher and it might flood all your stockpiles -->
            <SmoothingSpeed>10000</SmoothingSpeed>
            <ResearchSpeed>10</ResearchSpeed> <!-- And this one -->
            <AnimalGatherSpeed>10000</AnimalGatherSpeed>
            <AnimalGatherYield>100</AnimalGatherYield>
            <PlantWorkSpeed>10000</PlantWorkSpeed>
            <PlantHarvestYield>10</PlantHarvestYield>
            <DrugHarvestYield>10</DrugHarvestYield>
            <HuntingStealth>100</HuntingStealth>
            <ConstructionSpeed>10000</ConstructionSpeed>
            <ConstructSuccessChance>10000</ConstructSuccessChance>
            <FixBrokenDownBuildingSuccessChance>10000</FixBrokenDownBuildingSuccessChance>
            <!-- Crafting -->
            <SmeltingSpeed>10000</SmeltingSpeed>
            <GeneralLaborSpeed>10000</GeneralLaborSpeed>
            <DrugSynthesisSpeed>10000</DrugSynthesisSpeed>
            <CookSpeed>10000</CookSpeed>
            <FoodPoisonChance>0</FoodPoisonChance>
            <DrugCookingSpeed>10000</DrugCookingSpeed>
            <ButcheryFleshSpeed>10000</ButcheryFleshSpeed>
            <ButcheryMechanoidSpeed>10000</ButcheryMechanoidSpeed>
            <ButcheryFleshEfficiency>10</ButcheryFleshEfficiency>
            <ButcheryMechanoidEfficiency>1000</ButcheryMechanoidEfficiency>
          </value>
        </li>
      </operations>
</Operation>
</Patch>

Joedox

Hello,

Question a little more advanced, with the loadFolders can we do logic?

Let me explain:
<li IfModActive="Ludeon.RimWorld.Royalty, Ludeon.RimWorld.Ideology">Mods/DLC</li>
The loadFolders will check if Royalty OR Ideology is present, it will load the DLC folder if either is present.
My question is it possible to tell it, it needs Royalty AND Ideology to load the DLC folder?

Thank you for your answers.

Arco Frio

Hello, new here. I'm a late beginner at patching and while going ham creating patches, one of them happens to not work at all.

<Operation Class="PatchOperationSequence">
<operations>
<li Class="PatchOperationReplace">
<xpath>Defs/ResearchProjectDef[defName="Yokai_R_Basic"]/techLevel</xpath>
<value>
<techLevel>Neolithic</techLevel>
</value>
</li>
</operations>
</Operation>


I have absolutely no idea why, but it seems the research will still show up as Spacer, even though I'm trying to change it to Neolithic. Any ideas?

Joedox

Hello,

If in the LoadFolders.xml, it is not possible to put an AND, then I have another idea, but I will have to call on people who know how to program.
I will take concrete cases:

If Vanilla Weapons Expanded - Grenades is present then load :
  - if present Vanilla Weapons Expanded - Frontier
  - if present Vanilla Weapons Expanded - Non-Lethal
  - if present Vanilla Factions Expanded - Settlers

I tested :

<li IfModActive=" Vanilla Expanded.VWEG">
  <li IfModActive=" Vanilla Expanded.VWEF">Mods/Frontier</li>
  <li IfModActive=" Vanilla Expanded.VWENL">Mods/Non-Lethal</li>
  <li IfModActive=" Vanilla Expanded.VFES">Mods/Settlers</li>
</li>

I am missing a condition if not present then do nothing.

Thank you for your answers.

Joedox

Hello,

I must not be far from the solution.

<li IfModActive="OskarPotocki.VanillaFactionsExpanded.SettlersModule">Vanilla/Vanilla Factions Expanded 2 - Settlers - 2052918119</li>
<li IfModActive="VanillaExpanded.VWE">Vanilla/Vanilla Weapons Expanded - 1814383360</li>
<li IfModActive="VanillaExpanded.VWEG">Vanilla/Vanilla Weapons Expanded - Grenades - 2194472657</li>


<!-- +++++ If the Vanilla Weapons Expanded - Grenades mod is present +++++ -->

<li IfModActive="VanillaExpanded.VWEG">
<li IfModActive="OskarPotocki.VanillaFactionsExpanded.SettlersModule">Mods/Vanilla Factions Expanded - Settlers</li>
</li>
<li IfModActive="VanillaExpanded.VWEG">
<li IfModActive="VanillaExpanded.VWENL">Mods/Vanilla Weapons Expanded - Non-Lethal</li>
</li>
<li IfModActive="VanillaExpanded.VWEG">
<li IfModActive="VanillaExpanded.VWE">Mods/Vanilla Weapons Expanded</li>
</li>


For my tests, the activated mods are :
Vanilla Factions Expanded – Settlers
Vanilla Weapons Expanded
My translation mod

The problem is that it still translates the Vanilla Weapons Expanded - Non-Lethal Mod but it is not present. This gives me errors in the Def-injected translations load errors report:
Found no Verse.ThingDef named VWE_Apparel_GrenadeTearGasBelt to match VWE_Apparel_GrenadeTearGasBelt.label

If anyone can think of an idea to modify the code so that the Vanilla Weapons Expanded - Non-Lethal Mod is not translated if it is not present.

Arco Frio

Quote from: benniii on May 27, 2017, 12:34:23 PM
Hi,

Is it possible to use the patching-method to change things added from other mods? I did read somewhere that this process is used before adding the mods.

Just for clarification

Patching comes after reading the defs, but I also believe it depends on load order. So yes, patching is intended exactly to change things added by other mods.

Cammy

Can this method be used on the language xmls?