MayRequire

From RimWorld Wiki
Revision as of 15:14, 29 April 2024 by EmpressOfAbyss (talk | contribs) (added anomaly to offical DLC list)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Modding Tutorials

MayRequire is an XML attribute introduced alongside the Royalty DLC that allows for easy conditional loading of XML content. MayRequire makes it easier to load content that is optionally dependent on DLCs or other mods.

Details

MayRequire is inserted as an XML attribute in a supported XML node with a value being one or more packageId values separated by commas.

The packageId for official DLCs are:

  • Royalty: Ludeon.RimWorld.Royalty
  • Ideology: Ludeon.RimWorld.Ideology
  • Biotech: Ludeon.RimWorld.Biotech
  • Anomaly: Ludeon.RimWorld.Anomaly

The packageId for mods can be found in their About.xml file.

MayRequireAnyOf

By default, MayRequire will only allow the use of that node if all of the DLCs or mods it designates are loaded. If you instead want to load the specified node if any of the specified DLCs or mods are loaded, you can use MayRequireAnyOf instead.

Specifying Multiple Targets

Both MayRequire and MayRequireAnyOf can accept a comma-delimited list of packageId values. For example, if you wanted to create a ThingDef that is only loaded if both the Royalty and Ideology DLCs are loaded, you could use the following syntax:

<ThingDef MayRequire="Ludeon.RimWorld.Royalty,Ludeon.RimWorld.Ideology">

List Entries

MayRequire can be on any li node to only load that entry if the specified DLC or mod is loaded:

XML Example Description
<SurgeryOutcomeEffectDef Name="SurgeryOutcomeBase">
  <defName>SurgeryOutcomeBase</defName>
  <outcomes>
    <li Class="SurgeryOutcomeSuccess" />
    
    <!-- irrelevant nodes omitted -->
    
    <li Class="SurgeryOutcome_FailureWithHediff"  MayRequire="Ludeon.RimWorld.Biotech">
      <chance>0.03</chance>
      <failedHediff>Sterilized</failedHediff>
      <applyToRecipes>
        <li MayRequire="Ludeon.RimWorld.Biotech">ImplantIUD</li>
        <li MayRequire="Ludeon.RimWorld.Biotech">RemoveIUD</li>
      </applyToRecipes>
      <failure>true</failure>
      <totalDamage>10</totalDamage>
      <applyEffectsToPart>true</applyEffectsToPart>
      <letterLabel>Surgery failed on {PATIENT_labelShort}: Sterilized</letterLabel>
      <letterText>{SURGEON_labelShort} has failed while operating on {PATIENT_labelShort} ({RECIPE_label}), leaving {PATIENT_objective} sterile.</letterText>
    </li>
    
    <!-- irrelevant nodes omitted -->
    
  </outcomes>
  
  <!-- irrelevant nodes omitted -->
  
</SurgeryOutcomeEffectDef>

MayRequire is used in the list of possible surgery outcomes to add the possibility of accidentally sterilizing the patient as a result of a surgery failure.

Note that the use of MayRequire on the list nodes of the <applyToRecipes> is technically unnecessary as the entire outcome node would not have loaded without Biotech active.

<DesignationCategoryDef>
  <defName>Zone</defName>
  <label>zone</label>
  <order>800</order>
  <specialDesignatorClasses>
    <li>Designator_Cancel</li>
    <li>Designator_Deconstruct</li> 
    <li>Designator_ZoneAddStockpile_Resources</li>
    <li>Designator_ZoneAddStockpile_Dumping</li>
    <li>Designator_ZoneAdd_Growing</li>
    <li>Designator_ZoneDelete</li>
    <li>Designator_AreaHomeExpand</li>
    <li>Designator_AreaHomeClear</li>
    <li>Designator_AreaAllowedExpand</li>
    <li>Designator_AreaAllowedClear</li>
    <li>Designator_AreaBuildRoof</li>
    <li>Designator_AreaNoRoof</li>
    <li>Designator_AreaIgnoreRoof</li>
    <li>Designator_AreaSnowClearExpand</li>
    <li>Designator_AreaSnowClearClear</li>
    <li MayRequire="Ludeon.RimWorld.Biotech">Designator_AreaPollutionClearExpand</li>
    <li MayRequire="Ludeon.RimWorld.Biotech">Designator_AreaPollutionClearClear</li>
  </specialDesignatorClasses>
</DesignationCategoryDef>

MayRequire is used in the DesignationCategoryDef for the "Zone" architect menu to add designators for pollution clearing areas.

<AlienRace.ThingDef_AlienRace Name="ARR_RaceBase" ParentName="HumanRace" Abstract="True">

  <!-- irrelevant nodes omitted -->

  <comps>
    <li Class="ARimReborn.CompProperties_ClassUser" MayRequire="Aelanna.ARimReborn.ClassesAndJobs" />
  </comps>

  <inspectorTabs>
    <li MayRequire="Aelanna.ARimReborn.ClassesAndJobs">ARimReborn.ITab_Pawn_Classes</li>
  </inspectorTabs>

</AlienRace.ThingDef_AlienRace>

MayRequire can be used to omit entire code components.

In this modded example, both a ThingComp and an inspector tab are only added if a specific sub-mod is loaded.

<Operation Class="PatchOperationSequence">
  <operations>
    <li Class="PatchOperationAdd" MayRequire="Ludeon.Rimworld.Biotech"> <!-- Only runs if Biotech is active -->
      <xpath>Defs/ThingDef[defName="MechGestator"]/recipes<xpath>
      <value>
        <li>MyCustomMech</li>
      </value>
    </li>
    <li Class="PatchOperationAdd" MayRequire="MyProject.OtherModPackageId"><!-- Only runs if the specific mod is active -->
      <xpath>Defs/ThingDef[defName="OtherModWorkbench"]/recipes</xpath>
      <value>
        <li>MyCustomResource</li>
      </value>
    </li>
  </operations>
</Operation>

MayRequire can be used in lieu of PatchOperationFindMod in a PatchOperationSequence.

Warning: PatchOperationSequence can obfuscate errors, so it is strongly recommended that you individually test patches first before you place them in a sequence. Please see PatchOperations for more information.

Def References

MayRequire can be used on any field that references Defs by their defName, including single fields, lists, and special parsed lists such as stat blocks and item count lists.

Note: When used in direct references or in a list of direct references, Def references have a special behavior in that MayRequire only serves to suppress cross-resolution errors if the targeted DLC or mod is not loaded. If RimWorld finds the targeted Def, it will be loaded into the list regardless of whether the specified DLC or mod is loaded or not. This does not apply to special parsed lists (see below).

XML Example Description
<LifeStageDef ParentName="HumanlikeAdolescent">
  <defName>HumanlikeBaby</defName>
  <label>baby</label>

  <!-- irrelevant nodes omitted -->

  <thinkTreeMainOverride MayRequire="Ludeon.RimWorld.Biotech">HumanlikeBaby</thinkTreeMainOverride>
  <thinkTreeConstantOverride MayRequire="Ludeon.RimWorld.Biotech">HumanlikeBabyConstant</thinkTreeConstantOverride>

  <!-- irrelevant nodes omitted -->

</LifeStageDef>

MayRequire is used in human LifeStageDefs for Def references such as the ThinkTreeDefs for certain lifestages. These Defs only exist in the Biotech DLC, and thus must have errors suppressed if the DLC is not loaded.

<ThingDef ParentName="BasePawn">
  <defName>Human</defName>
  <label>human</label>
  
  <!-- irrelevant nodes omitted -->
  
  <recipes>
    <li>ExciseCarcinoma</li>
    <li>AdministerMechSerumHealer</li>
    <li>RemoveBodyPart</li>
    <li>Euthanize</li>
    <li>Anesthetize</li>
    <li>CureScaria</li>
    <li MayRequire="Ludeon.RimWorld.Biotech">Vasectomy</li>
    <li MayRequire="Ludeon.RimWorld.Biotech">ReverseVasectomy</li>
    <li MayRequire="Ludeon.RimWorld.Biotech">TubalLigation</li>
    <li MayRequire="Ludeon.RimWorld.Biotech">ExtractOvum</li>
    <li MayRequire="Ludeon.RimWorld.Royalty">CureBloodRot</li>
    <li MayRequire="Ludeon.RimWorld.Royalty">CureAbasia</li>
    <li MayRequire="Ludeon.RimWorld.Biotech">ExtractHemogenPack</li>
    <li MayRequire="Ludeon.RimWorld.Biotech">BloodTransfusion</li>
    <li MayRequire="Ludeon.RimWorld.Biotech">ImplantXenogerm</li>
    <li MayRequire="Ludeon.RimWorld.Biotech">ImplantIUD</li>
    <li MayRequire="Ludeon.RimWorld.Biotech">RemoveIUD</li>
    <li MayRequire="Ludeon.RimWorld.Biotech">TerminatePregnancy</li>
  </recipes>
  
  <!-- irrelevant nodes omitted -->
  
</ThingDef>

MayRequire is used in the Human ThingDef to set surgery recipes that are only relevant to specific DLCs. Without MayRequire, these would cause errors as the specified Defs only exist in their respective DLCs.

<ThingDef Name="TorchLamp" ParentName="BuildingBase">
  <defName>TorchLamp</defName>
  <label>torch lamp</label>
  
  <!-- irrelevant nodes omitted -->
  
  <statBases>
    <MaxHitPoints>75</MaxHitPoints>
    <WorkToBuild>100</WorkToBuild>
    <Flammability>0</Flammability>
    <MeditationFocusStrength>0.0</MeditationFocusStrength>
    <StyleDominance MayRequire="Ludeon.RimWorld.Ideology">5</StyleDominance>
  </statBases>
  
  <!-- irrelevant nodes omitted -->
  
</ThingDef>

MayRequire can be used in "special parsers" such as those for statBases. These are usually used to create shorthand notation wherein the node name is the defName and the value is the stat value. The vanilla torch lamp uses the StyleDominance stat from Ideology to affect the room it is placed in.

<ThingDef ParentName="ApparelMakeableBase">
  <defName>Apparel_Duster</defName>
  
  <!-- irrelevant tags omitted -->
  
  <equippedStatOffsets>
    <SlaveSuppressionOffset MayRequire="Ludeon.RimWorld.Ideology">-0.05</SlaveSuppressionOffset>
  </equippedStatOffsets>
  
  <!-- irrelevant tags omitted -->
</ThingDef>

equippedStatOffsets is another example of a stat block. In this example, the vanilla Duster apparel has a SlaveSuppressionOffset stat offset from Ideology.

<BiomeDef>
  <defName>TemperateForest</defName>
  <wildPlants>
    <Plant_Grass>5.0</Plant_Grass>
    <Plant_GrayGrass MayRequire="Ludeon.RimWorld.Biotech">2</Plant_GrayGrass>
    <Plant_TallGrass>2.0</Plant_TallGrass>
    <Plant_Brambles>1.0</Plant_Brambles>
    <Plant_Ripthorn MayRequire="Ludeon.RimWorld.Biotech">0.8</Plant_Ripthorn>
    <!-- many entries omitted -->
  </wildPlants>
  <pollutionWildAnimals MayRequire="Ludeon.RimWorld.Biotech">
    <Toxalope MayRequire="Ludeon.RimWorld.Biotech">0.4</Toxalope>
    <WasteRat MayRequire="Ludeon.RimWorld.Biotech">0.1</WasteRat>
    <!-- many entries omitted -->
  </pollutionWildAnimals>
  <!-- irrelevant nodes omitted -->
</BiomeDef>

MayRequire is used in BiomeDef entries to specify wild plants and animals that are only present in DLCs.

<FactionDef ParentName="FactionBase">
  <defName>Mechanoid</defName>
  <label>mechanoid hive</label>
  <pawnGroupMakers>
    <li>
      <!-- All types-->
      <kindDef>Combat</kindDef>
      <commonality>100</commonality>
      <options>
        <Mech_Scyther>10</Mech_Scyther>
        <Mech_Pikeman>10</Mech_Pikeman>
        <Mech_Lancer>10</Mech_Lancer>
        <Mech_CentipedeBlaster>10</Mech_CentipedeBlaster>
        <Mech_Militor MayRequire="Ludeon.RimWorld.Biotech">20</Mech_Militor>
        <Mech_Centurion MayRequire="Ludeon.RimWorld.Biotech">2</Mech_Centurion>
        <Mech_Warqueen MayRequire="Ludeon.RimWorld.Biotech">1</Mech_Warqueen>
        <Mech_Apocriton MayRequire="Ludeon.RimWorld.Biotech">1</Mech_Apocriton>
      </options>
    </li>
    <!-- many other nodes omitted -->
  </pawnGroupMakers>
  <!-- many other nodes omitted -->
</FactionDef>

MayRequire is used by official content FactionDef entries to specify PawnKindDef types that only exist in DLCs.

<FactionDef ParentName="FactionBase" Name="OutlanderFactionBase" Abstract="True">
  <disallowedMemes>
    <li MayRequire="Ludeon.RimWorld.Ideology">Structure_Animist</li>
    <li MayRequire="Ludeon.RimWorld.Ideology">Nudism</li>
    <li MayRequire="Ludeon.RimWorld.Ideology">Blindsight</li>
  </disallowedMemes>
  <structureMemeWeights>
    <Structure_TheistEmbodied MayRequire="Ludeon.RimWorld.Ideology">1</Structure_TheistEmbodied>
    <Structure_TheistAbstract MayRequire="Ludeon.RimWorld.Ideology">2</Structure_TheistAbstract>
    <Structure_Ideological MayRequire="Ludeon.RimWorld.Ideology">1</Structure_Ideological>
    <Structure_Archist MayRequire="Ludeon.RimWorld.Ideology">1</Structure_Archist>
    <Structure_OriginChristian MayRequire="Ludeon.RimWorld.Ideology">1</Structure_OriginChristian>
    <Structure_OriginIslamic MayRequire="Ludeon.RimWorld.Ideology">1</Structure_OriginIslamic>
    <Structure_OriginHindu MayRequire="Ludeon.RimWorld.Ideology">1</Structure_OriginHindu>
    <Structure_OriginBuddhist MayRequire="Ludeon.RimWorld.Ideology">1</Structure_OriginBuddhist>
  </structureMemeWeights>
  <!-- many nodes omitted -->
</FactionDef>

MayRequire must be used for meme and precept references in FactionDef entries, as they only exist if Ideology is loaded.

<FactionDef ParentName="FactionBase" Name="OutlanderFactionBase" Abstract="True">
  <xenotypeSet>
    <xenotypeChances>
      <Hussar MayRequire="Ludeon.RimWorld.Biotech">0.05</Hussar>
      <Dirtmole MayRequire="Ludeon.RimWorld.Biotech">0.05</Dirtmole>
      <Genie MayRequire="Ludeon.RimWorld.Biotech">0.025</Genie>
      <Neanderthal MayRequire="Ludeon.RimWorld.Biotech">0.025</Neanderthal>
    </xenotypeChances>
  </xenotypeSet>
  <!-- many nodes omitted -->
</FactionDef>

MayRequire must be used for xenotype references in FactionDef entries, as they only exist if Biotech is loaded.

Optional Defs

MayRequire can be used to conditionally load entire Defs. This is far easier to use than the prior option of using PatchOperations to inject new Defs.

XML Description
<ThingDef MayRequire="Ludeon.RimWorld.Ideology" ParentName="Brazier">
  <defName>DarklightBrazier</defName>
  <label>darklight brazier</label>
  <description>A specially treated brazier that illuminates its surroundings with darklight and creates heat. These satisfy royal brazier requirements.</description>
  
  <!-- irrelevant content omitted -->

</ThingDef>

MayRequire is used to activate Royalty's darklight brazier stat if Ideology is also loaded.

<StatDef ParentName="MeditationFocusBase" MayRequireAnyOf="Ludeon.RimWorld.Royalty,Ludeon.RimWorld.Biotech">
  <defName>MeditationFocusGain</defName>
  <label>meditation psyfocus gain</label>
  
  <!-- irrelevant content omitted -->
  
</StatDef>

MayRequireAnyOf is used to activate the MeditationFocusGain stat if either Royalty or Biotech is loaded.

<ThingDef ParentName="ApparelMakeableBase" MayRequireAnyOf="Ludeon.RimWorld.Royalty,Ludeon.RimWorld.Biotech">
  <defName>Apparel_Cape</defName>
  <label>cape</label>
  
  <!-- irrelevant content omitted -->
  
</ThingDef>

MayRequireAnyOf is used to activate the Cape apparel if either Royalty or Biotech is loaded.

Miscellaneous

The following are specific, non-standard usages of MayRequire from official content XML:

XML Description
<PawnKindDef Abstract="True" Name="BasePlayerPawnKind">
  <race>Human</race>
  <apparelIgnorePollution MayRequire="Ludeon.RimWorld.Biotech">true</apparelIgnorePollution>
  <!-- many nodes omitted -->
</PawnKindDef>

MayRequire is used by PawnKindDef for the apparelIgnorePollution node.

NOTE: Analysis of game code seems to indicate that this particular use of MayRequire does not actually have any effect, and may have been put here merely as a flag.

<LifeStageDef ParentName="HumanlikeAdolescent">
  <defName>HumanlikeBaby</defName>
  <label>baby</label>
  <thinkTreeMainOverride MayRequire="Ludeon.RimWorld.Biotech">HumanlikeBaby</thinkTreeMainOverride>
  <thinkTreeConstantOverride MayRequire="Ludeon.RimWorld.Biotech">HumanlikeBabyConstant</thinkTreeConstantOverride>
  <!-- many nodes omitted -->
</LifeStageDef>
<LifeStageDef Name="LifeStageHumanlikeChild" ParentName="HumanlikeAdolescent">
  <defName>HumanlikeChild</defName>
  <label>child</label>
  <workerClass MayRequire="Ludeon.RimWorld.Biotech">LifeStageWorker_HumanlikeChild</workerClass>
  <!-- many nodes omitted -->
</LifeStageDef>

MayRequire is used for think tree override and worker classes in LifeStageDef entries for functionality related to children and growth in Biotech.

NOTE: Analysis of game code seems to indicate that the latter use of MayRequire does not actually have any effect, and may have been put here merely as a flag. The WorkerClass code is itself locked to Biotech.

Exceptions

  • MayRequire does NOT work on top-level XML nodes that are not Defs. This means that Operation tags cannot use MayRequire, though as previous indicated, PatchOperationSequence lists can. Again, this is not recommended because PatchOperationSequence can obfuscate errors.
  • As of the time of this writing (2023-07-17), there is a bug in RimWorld where MayRequire does not ignore the _steam suffix appended to Steam copies of a mod when you have both a local mod and a Steam mod with the exact same packageId. This means that if you have a local copy, then only the local copy will be recognized by MayRequire. You can get around this in the meantime by using MayRequireAnyOf with both packageIds, i.e. MayRequireAnyOf="MyName.MyMod,MyName.MyMod_steam".