• If you have a mod, tool or prefab, please use the Resources section. Click Mods at the top of the forums.

XPath Modding Explanation Thread

This is a large amount of information in these posts. I'll be working on a full, properly formed tutorial as we get access to A17 and we can really make the xpath system shine. I've listed some examples here, and we'll be posting a ton more xpath samples over the coming weeks, as we port the SDX modlets over to use the native hooks.

By all means, begin posting your comments, questions, or requests for clarity.

Since we now know that xpath support will be built-into the vanilla base game, and discussions earlier were getting a bit confusing, I've decided to make a new thread to try to demystify xpath and what it'll mean for mods and modders going forward in A17. The information in this thread has been pieced together from forum and discord discussions. They may not be 100% accurate, but I will update this thread when we know the full implementation.

XPath is a way of adding, changing, or removing XML lines and nodes, without directly editing the XML files. It allows you to apply patches to the XML files without manually editing the vanilla XML files.

SDX has had partial support for this since its initial release, and a lot of the SDX Modlets are already written this way. We will be using some of the SDX's xpath as examples in this thread, but SDX is not required to do this in A17 and onwards.

I believe in using examples to explain concept, so in the next few posts, you'll see a mixture of explanation, along with sample xpath code.

The following Mod structure is expected for A17 and beyond:

Code:
Mods/
   <ModName>/
       Config/
       UIAtlases/ItemIconAtlas/
       ModInfo.xml

   <ModName2>/
       Config/
       ModInfo.xml
You will be able to have multiple mods loaded, and loading will occur as long as the ModInfo.xml exists. This is unchanged from what we've been doing doing with other alphas, with the exception of the Config folder.

This Config folder will support loading XML files, written using xpath, in the following folder structure:

Code:
Config/entityclasses.xml
Config/gamestages.xml
Config/items.xml
The files in the Config folder will not be a full copy of the vanilla file with your changes. Rather, it'll contain the changes you want done to the vanilla files. The files under the Config must match the vanilla file name. You cannot create an entityclasses2.xml file, and expect it to work. Any changes in the entityclasses.xml will have to be done in the same file. However, each mod can have its own entityclasses.xml.

During the game's initialization, it will perform the xpath merge in-memory only; no files will be actually be modified. This would allow us to remove mods we no longer want, without re-validating against steam, or previous copies of the xml. That's a big one. No more half merging of a mod, and not having it work, then trying to pull it back out.

What this means for us is that we'll be able to make a variety of smaller mods, which I've been calling modlets, which can add, remove and change smaller pieces of the game. They can be used together, or they could be added to an overhaul mod, in order to style your game play easier.

These modlets would also exists outside of the Data/Config folder, so if you have made direct XML changes in your Alpha 17.1 Data/Config files, and Steam updated the game to 17.2, you would have lost your changes, or would have to re-merge them in. We've all been there before. But if they existed as modlets, under the Mods folder, they would be safe. And as long as your xpath is still valid to the new XML, it should load up with no additional work on your part.

If we could use xyth's JunkItems modlet, which adds random, scrappable junk items to loot containers, and add them to Valmod Overhaul. Likewise, if Valmod Overhaul did not have the No Ammo modlet (which gives you the ability to unload a gun and get its bullets back without disassembling it), you could drop the NoAmmo modlet into your Mods folder. Headshots only? Want to increase stack sizes? Same deal.

With a properly constructed modlet, we'll be able to piece together new play styles for people to enjoy and share. A modder working on a large overhaul won't have to duplicate work. If they wanted to include the No Ammo mod, they wouldn't have to code it themselves, letting them focus on the bits that make their mod really unique.

Let's get started on your journey...

 
Last edited by a moderator:
Why didn't any of those lines work?


Pathing is incorrect.

For example, your first line should be 

<set xpath="/spawning/biome/spawn/@respawndelay">999</set>




the @respawndelay at the end tells the code which values in the spawning/biome/spawn nodes to change.

 
Can anyone help me?
Totally noob here (at least in 7dtd modding)
I believe my mod did load properly, but I get the yellow message that the line didn't do anything
The log warning is this
 

2023-10-16T18:34:14 74.700 WRN XML patch for "spawning.xml" from mod "ProjectGyropilot" did not apply: <set xpath="/spawning/biome/spawn/respawndelay"  (line 3 at pos 6)
2023-10-16T18:34:14 74.700 WRN XML patch for "spawning.xml" from mod "ProjectGyropilot" did not apply: <set xpath="/spawning/biome/spawn/maxcount"  (line 5 at pos 6)
2023-10-16T18:34:14 74.700 WRN XML patch for "spawning.xml" from mod "ProjectGyropilot" did not apply: <set xpath="/spawning/biome/spawn/time"  (line 7 at pos 6)




And my mod is like this

<config>

    <!--Sets zombies in all biomes to never respawn-->

    <set xpath="/spawning/biome/spawn/respawndelay">999</set>

    <!--Multiply biomes spawn numbers-->

    <set xpath="/spawning/biome/spawn/maxcount">3 * number(/spawning/biome/spawn/@maxcount)</set>

    <!--Sets zombies to spawn any time of the day-->

    <set xpath="/spawning/biome/spawn/time">any</set>

</config>



Why didn't any of those lines work?


Adding on to what @BFT2020 said, you can't do calculations in XML. Whatever is put in the text between the XML tags is inserted verbatim.

So, even if you fixed the path to the maxcount attribute, its value would still be wrong. If you used this XML:

<set xpath="/spawning/biome/spawn/@maxcount">3 * number(/spawning/biome/spawn/@maxcount)</set>


...the end result would be this:

<!-- Only doing one biome for brevity -->
<biome name="burnt_forest">
<spawn maxcount="3 * number(/spawning/biome/spawn/@maxcount)" respawndelay="2.8" time="Day" entitygroup="ZombiesBurntForest" />
<spawn maxcount="3 * number(/spawning/biome/spawn/@maxcount)" respawndelay="3.2" time="Night" entitygroup="ZombiesNight" />

<spawn maxcount="3 * number(/spawning/biome/spawn/@maxcount)" respawndelay="0.25" time="Day" entitygroup="ZombiesAll" tags="commercial,industrial" notags="downtown" />
<spawn maxcount="3 * number(/spawning/biome/spawn/@maxcount)" respawndelay="0.15" time="Night" entitygroup="ZombiesNight" tags="commercial,industrial" notags="downtown" />

<spawn maxcount="3 * number(/spawning/biome/spawn/@maxcount)" respawndelay="0.15" time="Day" entitygroup="ZombiesDowntown" tags="downtown" />
<spawn maxcount="3 * number(/spawning/biome/spawn/@maxcount)" respawndelay="0.1" time="Night" entitygroup="ZombiesDowntown" tags="downtown" />

<spawn maxcount="3 * number(/spawning/biome/spawn/@maxcount)" respawndelay="1" time="Any" entitygroup="WildGameForest" spawnDeadChance="0" />
<spawn maxcount="3 * number(/spawning/biome/spawn/@maxcount)" respawndelay="1.1" time="Night" entitygroup="EnemyAnimalsBurntForest" spawnDeadChance="0" />
</biome>




And, the game would now crash with red errors because the string "3 * number(/spawning/biome/spawn/@maxcount)" can't be parsed as a number.

Instead, what you have to do is use XPath to select each value of the "maxcount" attribute, and replace them with literal values:

Code:
<set xpath="/spawning/biome/spawn[@maxcount='1']/@maxcount">3</set>
<set xpath="/spawning/biome/spawn[@maxcount='2']/@maxcount">6</set>
<set xpath="/spawning/biome/spawn[@maxcount='3']/@maxcount">9</set>
<set xpath="/spawning/biome/spawn[@maxcount='4']/@maxcount">12</set>
 
Thanks! It works!
I mean, no warning or error popped up after I made those changes
I also had to remove the 3rd line because it complained about me using "any" for some reason. Capitalization perhaps?
And your suggestion is very clever and handy. I'll do just that

Edit: Definitively works! So many zombies
My idea is that zombies will permanently die and you will "conquer territory"

 
Last edited by a moderator:
Hello everyone
Since you guys were so helpful last time, I have another question

So what I want to do is, for the sake of organization and safety, to group a bunch of properties in a single one, because I will make a lot of entities, and they will have different set of pieces of code. Then I would still have a lot of entities, but each of them would carry much less cluttering info
For instance, for each mesh there's like dozens of lines or so of related properties like RigidBody, dismembering, scale and such
Then if I want to make, let's say a Feral variant of each of those characters, a lot more properties will add to the entity
I'm aware I can Extend, but it doesn't help enough. If each of those characters can have a female set of voices, then a different set of weapons... You see?
Is there a way to groups properties to distribute more neatly?

 
Last edited by a moderator:
I have an xpath question... the file I am trying to append into is ui_display.xml and in this xml is a subsection <item_display></item_display> with another section called character display or something like that. The issue is I need my append to jump into the <item_display></item_display> subsection and I cannot grasp from the tutorials here how exactly to do that. Most of this makes sense to me but either I missed something or there was no specific note to append into a specific subsection.

I have tried using <append xpath="/ui_display"><item_display><item_display_info display_type="MyType" displaygroup="MyGroup"><display_entry name="myStuff"/></item_display_info></item_display></append> which I am guessing worked to enter my data at the end of ui_display because no errors or warnings showed in the F1 load log. Unfortunately, it doesn't work in game. My item's stat sheet is blank.

I know I have to append ui_display.xml and I know it needs be in the <item_display></item_display> subsection. I just dont know how to write it in xpath.

I also tried a couple other ways but cause both yellow and red errors in the log so no point detailing those attempts. Any help would be appreciated!

 
Last edited by a moderator:
I tried making a vehicle mod that causes vultures to get shocked when getting too close.

Unfortunately, i am completely unable to make it trigger the buff and i really don't know what i'm doing wrong.

Please help, i don't know if it just refuses to work because the trigger is wrong (i've been trying various triggers, the one shown is just a test) or i'm just dumb, idk. Maybe even the requirement is giving trouble?

Code:
<configs>
	<append xpath="/item_modifiers">
	
		<item_modifier name="modVehicleAntiVulture" installable_tags="vehicle" modifier_tags="specialDamage" blocked_tags="noMods" type="attachment">
		<property name="Extends" value="modGeneralMaster"/>
		<property name="CreativeMode" value="Player"/>
		<property name="CustomIcon" value="modVehicleAntiVultureIcon"/>
		<property name="UnlockedBy" value="modVehicleAntiVultureSchematic"/>
		<property name="TraderStageTemplate" value="modsTier2"/>

		<effect_group>
		<triggered_effect trigger="onSelfEnteredGame" action="AddBuff" target="selfAOE" target_tags="vulture" range="35" buff="buffShocked">
		<requirement name="IsAttachedToEntity"/>
        </triggered_effect>
		</effect_group>
	</item_modifier>
	</append>
</configs>
 
Last edited by a moderator:
Good day everyone, 

In A20 I was able to use the following code. 

<append xpath="/entitygroups/entitygroup[descendant::entity[@name='zombieSteve']]">
    TestZombie1
    TestZombie2
    TestZombie3
 </append>

But in A21 when I try to use it. I get the following error. 

2024-02-01T17:02:31 313.250 WRN XML patch for "entitygroups.xml" from mod "AGG" did not apply: <append xpath="/entitygroups/entitygroup[descendant::entity[@name='zombieSteve']]"  (line 3066 at pos 3)

I also was able to run the following code before

<append xpath="/entitygroups/entitygroup[descendant::entity[@name='zombieBoe' and @prob='.3']]">
    <entity name="TestZombie1" prob=".3"/>
    <entity name="TestZombie2" prob=".3"/>    
    <entity name="TestZombie2" prob=".3"/>                
 </append>

But I get the following error. 

2024-02-01T17:02:31 313.250 WRN XML patch for "entitygroups.xml" from mod "AGG" did not apply: <append xpath="/entitygroups/entitygroup[descendant::entity[@name='zombieSteve']]"  (line 3064 at pos 2)

Does anyone have a clue! 

Thanks from

Lost and Confused

 
Good day everyone, 

In A20 I was able to use the following code. 

<append xpath="/entitygroups/entitygroup[descendant::entity[@name='zombieSteve']]">
    TestZombie1
    TestZombie2
    TestZombie3
 </append>

But in A21 when I try to use it. I get the following error. 

2024-02-01T17:02:31 313.250 WRN XML patch for "entitygroups.xml" from mod "AGG" did not apply: <append xpath="/entitygroups/entitygroup[descendant::entity[@name='zombieSteve']]"  (line 3066 at pos 3)

I also was able to run the following code before

<append xpath="/entitygroups/entitygroup[descendant::entity[@name='zombieBoe' and @prob='.3']]">
    <entity name="TestZombie1" prob=".3"/>
    <entity name="TestZombie2" prob=".3"/>    
    <entity name="TestZombie2" prob=".3"/>                
 </append>

But I get the following error. 

2024-02-01T17:02:31 313.250 WRN XML patch for "entitygroups.xml" from mod "AGG" did not apply: <append xpath="/entitygroups/entitygroup[descendant::entity[@name='zombieSteve']]"  (line 3064 at pos 2)

Does anyone have a clue! 

Thanks from

Lost and Confused


You need to use the csv structure and not append.  Entity groups structured changed from A20 to A21.  Go back to page 28 in this thread and the new method is explained in more detail.

 
Thanks for the help.

Have a great day

<csv xpath="/entitygroups/entitygroup[contains(text(), 'zombieBoe')]/text()" delim="\n" op="add">
    TestZombie
</csv>

 
EDIT: After sleeping on it I figured it out this morning. I had put the entity in the node path, not the entity group :)

I am trying to exclude any "fresh" zombies from DarksZombies from appearing in random spawns:

<csv xpath="/entitygroups/entitygroup[@name='ZombiesAll']/text()" delim="\n" op="add" >
        zombieParasite
        zombieFatParasite
        zombieExperimentZ
        zombieArmy1
        zombieFreshFemale1
        zombieFreshFemale2
        zombieFreshFemale3
        zombieFreshFemale4
        zombieFreshFemale5
                zombieFreshMale1
                zombieFreshMale2    
                    zombieFreshFemale1Feral, .25
        zombieFreshFemale2Feral, .25
        zombieFreshFemale3Feral, .25
        zombieFreshFemale4Feral, .25
        zombieFreshFemale5Feral, .25
                zombieFreshMale1Feral, .25
        zombieFreshMale2Feral, .25
    </csv>


I've created a modlet, (which loads after DarkzZombiez) and tried the following lines of code:

<csv xpath="/entitygroup[contains(@name, 'zombie') or (contains(@name, 'Fresh'))]/text()" delim="\n" op="remove" >zombieFresh*</csv>
 

<csv xpath="//entitygroup[contains(@name, 'zombie') or (contains(@name, 'Fresh'))]/text()" delim="\n" op="remove" >zombieFresh*</csv>

<csv xpath="/entitygroups/entitygroup[contains(@name, 'zombie') or (contains(@name, 'Fresh'))]/text()" delim="\n" op="remove" >zombieFresh*</csv>

<csv xpath="//entitygroups/entitygroup[contains(@name, 'zombie') or (contains(@name, 'Fresh'))]/text()" delim="\n" op="remove" >zombieFresh*</csv>

...but I am still getting "fresh" zombie spawns in the wild.

If anyone sees the error in this I'd really appreciate it!

Had a sleep on it and discovered it in the morning. 

 
Last edited by a moderator:
Hi, I am really new to the modding. Is there any overview about valid tags, attributes, attribute names? Or do I have to go through all existing XML from TFP to check what can be used? Some kind of XSD?

Maybe sb here is able to help me.
I want to play around with action skills. E.g. using a pickaxe increases miner69 skill. In general it already works.

I took this as an example:
 

<effect_group>
<passive_effect name="HarvestCount" operation="perc_add" level="1,5" value=".2,1" tags="oreWoodHarvest"/>
<passive_effect name="PlayerExpGain" operation="perc_add" level="1,5" value="-.1,-.4" tags="Harvesting">
<requirement name="HoldingItemHasTags" tags="perkMiner69r"/>
</passive_effect>
...
</effect_group>


Increasing the XP for the action skill looks like this (just the trigger within the effect_group): 
 

<triggered_effect trigger="onSelfHarvestBlock" action="ModifyCVar" cvar="Miner69er_XP" operation="add" value="4">
<requirement for stone axe />
</triggered_effect>
<triggered_effect trigger="onSelfHarvestBlock" action="ModifyCVar" cvar="Miner69er_XP" operation="add" value="6">
<requirement for iron axe />
</triggered_effect>
<triggered_effect trigger="onSelfHarvestBlock" action="ModifyCVar" cvar="Miner69er_XP" operation="add" value="8">
<requirement for steel axe />
</triggered_effect>


I would like to check the 'TraderStageTemplate' property of an item  (baseTier0-3) to be able to give more XP for a steel axe than a stone axe.
How would I have to implement the <requirement tags to achieve this?
Is there any other requirement possible next to ItemHasTags and HoldingItemHasTags?

 
I would like to check the 'TraderStageTemplate' property of an item  (baseTier0-3) to be able to give more XP for a steel axe than a stone axe.
How would I have to implement the <requirement tags to achieve this?
Is there any other requirement possible next to ItemHasTags and HoldingItemHasTags?


You are on the right path, but I think you are looking at it from the wrong end.  Instead of looking to have different requirements, look into adding custom tags for the items.

Change

<requirement name="HoldingItemHasTags" tags="perkMiner69r"/>




to something like this

<requirement name="HoldingItemHasTags" tags="BFTIronPickaxe"/>




and then add the new tag to the item in the items.xml file

Code:
<item name="meleeToolPickT1IronPickaxe">
    <property name="Tags" value="melee,grunting,medium,tool,longShaft,attStrength,perkMiner69r,perkMotherLode,miningTool,canHaveCosmetic,harvestingSkill,BFTIronPickaxe"/>
 
This is what I also had in mind. But I wanted to avoid this as I would have to update all items for this (my idea was to sooner or later replace almost all skills by action skills). Going this way I also couldn't combine my mod with other mods which add new items/weapons.
But (thinking while typing) I could update all items at once using xpath - if I was able to add a new entry to the list of tags:

Do you know if this is possible? something like <add xpath="" ...

 
Last edited by a moderator:
Another question (which probably also cannot be solved):
I would like to increase the amount of healing provided by a bandage/Medikit - bases on my current level of medic perk  and (more important) also relative to the base amount of healing the item gives.

This is happening within items.xml for medical bandage: 
 

<triggered_effect trigger="onSelfPrimaryActionEnd" action="ModifyCVar" cvar="medicalRegHealthAmount" operation="add" value="30"/>




If I now add the following to my medical perk, it works perfectly fine (add a value):

<effect_group>
<requirement name="ItemHasTags" tags="medical"/>
<triggered_effect trigger="onSelfPrimaryActionEnd" action="ModifyCVar" cvar="medicalRegHealthAmount" operation="add" value="@HealingMultiplier"/>
</effect_group>


But this adds the same amount of healing to all kind of medical stuff.
What I would like to do instead is this (multiply my facter * base value from the item):
 

<effect_group>
  <requirement name="ItemHasTags" tags="medical"/>
  <triggered_effect trigger="onSelfPrimaryActionEnd" action="ModifyCVar" cvar="medicalRegHealthAmount" operation="multiply" value="@HealingMultiplier"/>
</effect_group>


Unfortunately, this piece of code is being executed before the effect from the bandage has been applied. So, it is HealingFactor*0 (and afterwards + 30) = 30 instead of 30 * HealingFactor.

Is there any way to solve this other than editing all items manually by adding another tag again and checking this tag as a requirement?

 
Last edited by a moderator:
Unfortunately, this piece of code is being executed before the effect from the bandage has been applied. So, it is HealingFactor*0 (and afterwards + 30) = 30 instead of 30 * HealingFactor.

Is there any way to solve this other than editing all items manually by adding another tag again and checking this tag as a requirement?


I'm no expert (so take this with a grain of salt as I don't know if this is the solution without trying it myself), but should the operation be perc_add instead of mutliply?  That is typically how it is setup to do either a percentage increase or decrease in a attritubute when you level up a perk (for example 10% reduction in time would be perc_add and the value -0.1)

As for the bandages, try using the tag stopsBleeding instead of medical.  That would only apply to bandages, first aid bandages, first aid kits, and sewing kits.  Since regular bandages and sewing kits don't have the heal ability incorporated into them, it wouldn't give them a heal bonus based on your perk level.

That is what I would try first if I was doing what you are trying to do.

 
Unfortunately, perc_add operation cannot be used within <triggered_effect>.

But I found another solution which seems to work.

In my medic perk I set the healing factor like this:

<effect_group>
<triggered_effect trigger="onSelfPrimaryActionEnd" action="ModifyCVar" cvar="HealingFactor" operation="set" value="@PerkHealingFactor"/>
<triggered_effect trigger="onSelfSecondaryActionEnd" target="other" action="ModifyCVar" cvar="HealingFactor" operation="set" value="@PerkHealingFactor"/>
</effect_group>


And then I extend the buff which handles all healing stuff and apply my healing factor.

<append xpath="/buffs/buff[@name='buffHealHealth']">
<effect_group name="Apply Healing Factor from Perk">
<triggered_effect trigger="onSelfBuffStart" action="ModifyCVar" cvar="medicalRegHealthAmount" operation="multiply" value="@HealingFactor"/>
<triggered_effect trigger="onSelfBuffStart" action="ModifyCVar" cvar=".healHealthAmountDisplay" operation="set" value="@medicalRegHealthAmount"/>
<triggered_effect trigger="onSelfBuffRemove" action="ModifyCVar" cvar="HealingFactor" operation="set" value="1"/><!--not sure if this is needed-->
</effect_group>
</append>


At this point the medicalRegHealthAmount from the item was already set.
In Singleplayer it seems to work. But I need to check if this also works in multiplayer when healing others. 

 
Last edited by a moderator:
Btw... is there any Discord Channel (or similar) where I could ask for help? I have lots of questions like 

Is there any trigger for lockpicking/opening a chest? Obviously none of the "onSelfXY" trigger works. 
Or: Is there any trigger for taking items out of a forge/workbench?

Or: When using a wrench to disassable an object: Is it possible to find out which object it is?

And I and don't want to spam all of them within this thread.

 
Last edited by a moderator:
Hi, it's me again, struggeling.

Works (while harvesting a car):

<effect_group>
<triggered_effect trigger="onSelfHarvestBlock" action="ShowToolbeltMessage" message="Harvest vehicle!"/>
</effect_group>


Also works (while shooting a zombie - no crawler):

<effect_group>
<requirement name="EntityTagCompare" target="other" tags="walker"/>
<triggered_effect trigger="onSelfDamagedOther" action="ShowToolbeltMessage" message="Hit Walking Zombie!"/>
</effect_group>


Doesn't work (while harvesting a car).

<effect_group>
<requirement name="EntityTagCompare" target="other" tags="vehicle"/>
<triggered_effect trigger="onSelfHarvestBlock" action="ShowToolbeltMessage" message="Harvest vehicle!"/>
</effect_group>




Any ideas on how to get the last one working?

 
 XML / XPath modding has been further expanded in A22. 
 
 Executive Summary:
 
    Two new features have been added to the game to support modding.

    Conditionals
    ------------
        The conditionals allow you to add if/else statements in your xml, adjusting how changes are applied based on a variety of conditions during xml loading.

        Example ( blocks.xml 😞
            If a modlet called "MyDependencyModlet" is loaded, then change the keystone block's model.
            

<configs>
<conditional>
<if cond="mod_loaded('MyDependencyModlet')">
<set xpath="/blocks/block[@name='keystoneBlock']/property[@name='Model']/@value">@:Entities/LootContainers/luggageBigClosedPrefab.prefab</set>
</if>
</conditional>
</configs>



    Include
    -------
        The game can now include references to other files from the same modlet, allowing you to structure your xmls a bit better.

        Conditonals and include can work together, allowing you to load custom xmls for various events.

        Example ( entityclasses.xml 😞
            Include the following three xmls as part of loading the entityclasses.xml.
      

<configs>
<include filename="myEntityclasses.xml" />
    <include filename="entityclasses/feral.xml" />
    <include filename="entityclasses/bosses.xml" />
</configs>


<include:
====================

The game now supports the <include syntax in XML, enabling users to divide larger files into smaller ones or load different XML files based on conditions.

<configs>
    <include filename="myEntityclasses.xml" />
</configs>

Key points:
    - You can use the <include statement in any of the game's XML files located in your mods folder to reference another file.
    - The 'filename' attribute specifies a relative path to the parent file, which, in this context, is your modified version of the XML file (e.g., Config/entityclasses.xml).
    - The 'filename' can reference files within the same folder or in sub-folders but not from different modlets.
    - There are no naming restrictions for the included XML files.
    - Each included XML file must adhere to the format and structure of the parent file, including having a top-level node.
    - Included XML files have the same xpath capabilities as the parent file.
    - Ensure the case of your reference files matches, as file references will fail on Linux and Mac if there is a case mismatch.
        <!-- This would fail on Linux and Mac if your actual filename is bosses.xml -->
        <include filename="Bosses.xml" />
    - Included XML files can only be of the same type as the file they are added to. 
        For example, entityclasses.xml can only include files that also patch entityclasses.xml.
        You cannot add an <include from entityclasses.xml to patch blocks.xml.
    - You can use conditionals to wrap <include statements (details documented below).

Here are some examples illustrating the usage of the <include syntax in XML files:

Example 1:
    <!-- MyMod/Config/entityclasses.xml -->
    <include filename="Conditionals/ConditionalExamples.xml" />

Example 2:
    <!-- MyMod/Config/entityclasses.xml -->
    <include filename="EntityChanges/ferals.xml" />
    <include filename="EntityChanges/bosses.xml" />

Example 3:
    <!-- MyMod/Config/entityclasses.xml -->
    <include filename="bosses.xml" />

These examples demonstrate how you can use the <include syntax to reference and include additional XML files within your main XML file.

Conditionals:
======================
Conditionals in XML provide a way to modify game files during the loading process, giving modders more control over their mods.

These conditions are evaluated when the player first loads the XML, whether in single-player mode or when connecting to a server.
    - Conditionals are not re-evaluated during gameplay and do not respond to dynamic changes in the game. They are only checked when the XML files are loaded.
    - For instance, a conditional included in a buff will not behave differently once the game is loaded, even if the buff is modified or updated.

The basic structure of a conditional statement is as follows:

    <conditional>
        <if cond="mod_loaded('MyDependencyModlet')">
            <!-- XML Code Snip -->
        </if>
        <if cond="mod_loaded('MyDependencyModlet2')">
            <!-- XML Code Snip -->
        </if>
        <!-- Otherwise, do this -->
        <else>
            <!-- XML Code Snip -->
        </else>
    </conditional>
 
 
 Key Points:
    - Each <if /> or <else> statement must be enclosed within a <conditional> block, and <else> statements are optional.
    - If multiple <if /> are in the same level, within the same conditional, they are treated as  "else-if".
        - If the first <if /> is true, then the other <if /> in the same conditionals will not be evaluated.
    - Each <if /> statement must have at least one cond that evaluates to true or false. 
    - All XML files that support XPath also support conditionals.
    - Conditionals can be used to wrap <include /> statements for including files.
        - <include /> can only be in top level (ie, patch level) conditionals.
    - By default, conditionals are evaluated on the host, then sent to the clients.
        - The evaluator attribute is optional and defaults to "host". 
        - If set to "client", conditionals are evaluated by the client and host. 
            - Each system will evaluate the conditional independently.
        - If a conditional is placed at the top level, the evaluator is ignored, and is evaluated on the host.
            <configs>
                <!-- evaluator is ignored! -->
                <conditional evaluator="client">
                    <if ...>
                </conditional>
            </configs>
            - This is done to properly evaluate the <include files.
        
    - Host evaluated conditionals are not included in `ConfigsDump`; only their final evaluated results are stored.
    - Conditionals can be placed at any level within the XML.
    - Conditionals cannot evaluate based on other XML properties from the game. 
        For example, you cannot use a block's property value as a condition.
    - Conditional checks are done by using NCalc.
        - https://github.com/ncalc/ncalc
    
<if /> statements contain a cond attribute that can be used to evaluate a conditional.
    - A cond needs to evaluate to true or false based on the "equation".
    - A cond can be read by non-programmers as:
        - if this mod is loaded, do this
        - if this mod is at version 2.2, do this
    - A cond may contain multiple equations, separated by "and" and "or".
    - Equations support the following operands:
        >=    :     Equal or Greater
        <=    :     Equal or Less Than
        >    :     Greater Than
        <    :     Less Than
        ==        :     Equal To
        !=        :     Not Equal To
        <>        :     Not Equal To
        
    - If the return type is a number or boolean, you do not need to use quotes after the operand.
    - If the return type is a string, you will need to use quotes for the comparison value.
        - <if cond="mod_loaded('mymod') == true" />
        - <if cond="gamepref('OptionsDynamicMusicEnabled') == 1" />
        - <if cond="serverinfo('ServerName') == 'Testing'" />
            - since ServerName is a string, you must wrap the word Testing in single quotes.
    - If the return type is a boolean, the operand can be omitted if you want to test for true.
            <if cond="mod_loaded('myModlet')" />  is equivalent to <if cond="mod_loaded('myModlet') == true" />  
            
        -If you want to reverse the condition,
            <if cond="mod_loaded('myModlet') == false" />      
    - If a conditional has multiple <if /> statements, only the first statement that is evaluated as true is used.
        - Other <if /> statements in the same conditional will not be evaluated, even if they are testing different things.
        
    - These are defined in XmlPatchConditionEvaluator class in the NCalcEvaluateFunction().

Additional custom classes were added to the game to extend basic ncalc functions:
    - The list below is formatted as such:
        - First part is the return type.
        - Second part is the 'method' being called
        - Third part is the parameter, if any, that is needed.

    - The return type "Version" is Microsoft's .NET Version.
        - https://learn.microsoft.com/en-us/dotnet/api/system.version?view=net-8.0

    bool mod_loaded( "modlet name")
        - Returns true if the modlet is loaded, otherwise returns false if it is not.
        - Uses the ModInfo.xml's name="" value.
        - All modlets are loaded by the time this is evaluated, regardless of load order.
        
    Version mod_version( "modlet name" )
        - Returns the Version of modlet if it is loaded.
        - If the modlet is not loaded, it returns a Version of 0.0.
        - The version() format below can be used to compare against it.
        
    Version game_version()
        - Returns the current game Version.
        - The version() format below can be used to compare against it.

    Version version( x,y,z)
        - Converts the numbers into a Version, which can be used to compare against mod_version and game_version.
        - y and z can be omitted if you do not need them.
            - For example, if you only wanted to compare "22" instead of "22.1.0".
        - You can ommit y and z, or z. 
        - You cannot ommit y if you want to evaluate against z.
        - Examples:
            -  <if cond="game_version() == version(22)" >
                True if Game Version was 22.x.x.
            -  <if cond="game_version() == version(22,1)" >
                True if Game Version was 22.1.x.
            -  <if cond="game_version() == version(21,1)" >
                False if Game Version was 22.x.x.
            -  <if cond="game_version() >= version(22)" >
                True if the Game Version was 22.x.x.
            -  <if cond="game_version() >= version(22)" >
                True if the Game Version was 22.1.2.
            -  <if cond="game_version() >= version(22)" >                
                False if the Game Version was 21.x.x.

    int/string/bool serverinfo( "server property" )
        - Returns the property value passed in, such as "GameName"
        - Returns an int if the value is a number, a string if it's a word, or a true/false if it's boolean.
        
    int/string/bool gamepref( "gamePref" )
        - Returns the preference value passed in, such as "OptionsDynamicMusicEnabled"
        - Returns an int if the value is a number, a string if it's a word, or a true/false if it's boolean.
        
    bool event("event name")
        - Returns true or false, depending on if the passed in event is active. 
        - Defined in events.xml. 
            Events.xml allow you to set up 'events' based on dates. 
            - This allows for adjusting the user experience without pushing a new set of files.
            - For example, during the Easter event time window, you can change birds' nests into more colourful versions.
            - Once the event is over, the next time the player loads, they'll get the regular birds nest.
            - See events.xml for more details.

    int time_minutes()
        - Returns the current minute at the time of parsing.

    bool game_loaded()
        - Returns true or false, depending on if the game is loaded.

    Examples:
    
        <!--
            This checks to see if there's a modlet that is already loaded.
        -->
        <conditional>
            <if cond="mod_loaded('MyDependencyModlet')">
                <include filename="NPCs/entityclasses.xml" />
            </if>
        </conditional>

        <!-- 
            This is the same check, but it's condition is flipped. We'll run the code in the <If if the modlet is not available.
        -->
        <conditional>
            <if cond="mod_loaded('MyDependencyModlet') == false">
            </if>
        </conditional>
    
    
        <!--
            is this mod loaded, and is greater than 22.0? 
        -->
        <conditional>
            <if cond="mod_version('MyDependencyModlet') >= version(22,0)">
        
            </if>
        </conditional>

        <!--
            Does the player have Dynamic Music Enabled?  We want this to be evaluated by the client, since the server doesn't know.
            If the player does change this option while in game, it will not take effect until the player goes back to the main menu, and reloads the world.
        -->
        <conditional evaluator="client" >
            <if cond="gamepref('OptionsDynamicMusicEnabled')">
        
            </if>
        </conditional>
        
        
        <!-- 
            Not all conditionals have a real-world use case, but have been exposed for testing and for creative people.
            If the time, at server start, is an even minute.  12:04, 1:16, but not 1:59 
        -->
        <conditional>
            <if cond="(time_minutes() % 2) == 0">
            </if>
        </conditional>

        <!-- 
            Multiple conditionals can be specified using the and keyword. 
            In this example, the condition is true if the time of start up is between 20 and 25 minutes.
        -->
        <conditional>
            <if cond="(time_minutes() <= 25) and (time_minutes() >= 20) ">
            </if>
        </conditional>

        <!-- 
            In this case, we are checking to make sure we are loading the game, before running the check 
            This is mostly affects settings that are only available when the game is loading, either connecting to a dedi or SP client.
            The menu itself may not have the information. 
        
            Since the game server is started so late in the process, most of the xml is already evaluated by the time it gets configured,
            which may mean that serverinfo has limited use atm.
        -->
        <conditional>
            <if cond="game_loaded() and serverinfo('GameName') == 'Testing'">
            </if>
        </conditional>
    
        <!-- 
            Conditionals can have a lot of nested if/elses.
            This probably should be used with caution as it could get pretty confusing.
        -->
        <conditional>
            <if cond="(time_minutes() <= 20)">
            </if>
            <else>
                <if cond="(time_minutes() <= 25)">
                </if>
                <else>
                    <if cond="(time_minutes() <= 30)">
                    </if>
                </else>
            </else>
        </conditional>
        
        <!--
            Another way to write if / else statements is as follows.
            As soon as an <if /> is evaluated as true, the checks are stopped for the conditional.
            That is, if the time_minutes() is less than 25, it will not evaluate the last <if /> or <else>
        -->
        <conditional>
            <if cond="(time_minutes() <= 20)">
                <!-- if the current minute is 20 or less -->
            </if>
            <if cond="(time_minutes() <= 25)">
                <!-- if the current minute is 25 or less, but also more than 20, since it passed the first check. -->
            </if>
            <if cond="(time_minutes() <= 30)">
                <!-- if the current minute is 30 or less, but also more than 25, since it passed the first two checks -->
            </if>
            <else>
            
            </else>
        </conditional>
        
        <!-- 
            We can also evaluate which game version we are loading against.
        -->
        <conditional>
            <!-- Check if the game version is above 22.0 or equal -->
            <!-- Game Version would return the full version, 22.1.233 -->
            <if cond="game_version() >= version(22,0)">
            </if>
        </conditional>

        <!--
            This checks to see if there the NPC Core modlet is loaded, 
            we can then load up custom entityclasses that rely on it.
        -->
        <conditional>
            <if cond="mod_loaded('0-XNPCCore') and mod_loaded('0-SCore_sphereii)">
                <include filename="NPCs/entityclasses.xml" />
            </if>
        </conditional>

        
    More examples syntax can be found in the A22-XML-Xamples/Config/Conditionals/ConditionalExamples.xml.

https://github.com/SphereII/7D2DConfig/releases/tag/XMLSamples

 
Back
Top