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

Modifying a buff via modlet - think I have the path wrong

BFT2020

Hunter
So been working on some modding (just simple xml changes) that I can upload to new versions and not have to modify the individual xml config files every time the game is updated

So far, I got a customize traders file done and a progression file that are working as intended.

However, once I started working on a buffs file, I ran into the issue below that I can't figure out what I am doing wrong (though I believe it is a pathing issue on my part)

First, here is the error I get in the console

2021-09-07T20:48:20 748.531 WRN XML patch for "buffs.xml" from mod "Z-JeremyMod" did not apply: <set xpath="/buffs/buff[@name='buffDrugPainkillers']/effect_group/requirement/triggered_effect[@stat='Health']/@value" 


The previous times I got this error, it was a path issue (99.9% of the time, I either forgot something, inserted a space it didn't like, or mistyped the node path).

Here is the code

<set xpath="/buffs/buff[@name='buffDrugPainkillers']/effect_group/requirement[@cvar='$buffDrugPainkillersHealed']/triggered_effect[@stat='Health']/@value">0</set>


I am trying to change the painkiller health buff from 40 to 0.  I know this is the right location as making the change in the actual config file works.

Here is the specific part of the buffs file I am trying to modify

<buffs>

    <----SNIP----->

    <buff name="buffDrugPainkillers" name_key="buffDrugPainkillersName" description_key="buffDrugPainkillersDesc" icon="ui_game_symbol_pills">
         <----SNIP----->

        <effect_group>
            <requirement name="NotHasBuff" buff="buffInjuryConcussion"/>
            <requirement name="CVarCompare" cvar="$buffDrugPainkillersHealed" operation="Equals" value="0"/>
                <triggered_effect trigger="onSelfBuffUpdate" action="ModifyStats" stat="Health" operation="add" value="40"/>
                <triggered_effect trigger="onSelfBuffUpdate" action="ModifyCVar" cvar="$buffDrugPainkillersHealed" operation="set" value="1"/>
        </effect_group>

        <---SNOP----->
    </buff>
If someone could point out my mistake, that would be greatly appreciated.

 
<set xpath="/buffs/buff[@name='buffDrugPainkillers']/effect_group[3]/triggered_effect[@stat='Health']/@value">0</set>

That should work. You don't need to mention the requirement property since that's not a node that actually contains the triggered effects. I added the [3] cuz in your example it's just going to any effect group i think.  Either that or it's going to the first effect group and stopping there. It's been so long so I don't remember... Unfortunately most of vanilla's effect groups aren't labeled, so using the number works, but should they change anything about them or the order, you'll have to recheck the code.

Not able to test, but lemme know if this works for ya!

Also.. As I say for everyone using xpath, I highly recommend using notepad++, then pressing ALT 0. This will collapse each node. Write them down as you open it, and there's your xpath ... path.

 
Last edited by a moderator:
<set xpath="/buffs/buff[@name='buffDrugPainkillers']/effect_group[3]/triggered_effect[@stat='Health']/@value">0</set>

That should work. You don't need to mention the requirement property since that's not a node that actually contains the triggered effects. I added the [3] cuz in your example it's just going to any effect group i think.  Either that or it's going to the first effect group and stopping there. It's been so long so I don't remember... Unfortunately most of vanilla's effect groups aren't labeled, so using the number works, but should they change anything about them or the order, you'll have to recheck the code.

Not able to test, but lemme know if this works for ya!

Also.. As I say for everyone using xpath, I highly recommend using notepad++, then pressing ALT 0. This will collapse each node. Write them down as you open it, and there's your xpath ... path.
Thanks I will give it a try tonight if I have the time.

Yeah, I went the requirement route as I was trying to make sure it went to the correct effect group.  I did not realize that you can simply do the [3] to get it to go to the correct node within that property. 

And thanks for the reminder to recheck as newer updates get released.  That is something I already put in the back of my mind to do, but with TFP wanting to go Gold soon and release a final product, it shouldn't be too many times I would have to do that (plus, doing this coding myself means I am learning it and will be able to understand quickly if I need to make changes to my files after each update).  I read your thread in the Tutorials & Guide section, along with watching Max Fox's videos on xpath coding and downloading mods like Darkness Falls to see how others have done it; though nothing replaces just diving in and trying to write the code (and seeing how many red errors you get and yellow warning messages)  😁

I just started using notepad++ (it is what i am using to write these files as it allows me to quickly check to see if I mistyped something as I learn to use this program).  However, for some reason, I opened the original file in notepad and had to do the manual snips.  Now I am curious about the original file and will open it tonight using Notepad++.  I bet if I did it in the first place, that might have been easier to see the code structure than using regular old notepad. 

 
in your example it's just going to any effect group i think.  Either that or it's going to the first effect group and stopping there.


XPath selects a collection of XML elements. It doesn't stop when it finds a match. (It's why people use selectors in the first place.)

For example, you could do this:

<set xpath="//triggered_effect[@stat='Health']/@value">0</set>




That will select all triggered_effect nodes with the "stat" attribute value of "health", and set all their "value" attribute values to zero, for all XML nodes in the file.

The double-slash means "any descendent of the currently selected element which matches, no matter how deep." (In this case it's the root node of the XML document.) It's useful for matching nodes that might or might not be a direct child of a node that you know how to target.

For example:

<set xpath="//buff[@name='buffDrugPainkillers']//triggered_effect[@stat='Health']/@value">0</set>




You might not know if the triggered effect is a descendent of a requirement node, or of an effect_group node, but it doesn't matter.

It's something to keep in mind for forwards compatibility. It obviously doesn't guarantee it will still work if TFP update the XML, but it helps.

 
Last edited by a moderator:
I will keep that in mind.  I have some changes I will be making in the loot file that a mass change would be easier to do than changing everything one by one.

I got the buff nerf to work and it tested out fine.  Nothing like lighting yourself on fire and not being able to instant recover by swallowing a white pill 😁

 
So to expand on my modding skills, I am now modifying the loot xml file.  The first thing I am doing is changing the weapons and armor to basic resources (simulating that all you are finding are broken armor and weapons).  I think I am doing it right, but wanted to confirm.  I don't get any errors when I load up my test world, but not sure yet if the game is doing what I think it is doing:

First, what I think I am doing:

-At each loot group with cloth armor, I am replacing the armor with resourceCloth and setting up a count range of this resource.

Now my code.  Instead of individually changing each loot group with cloth armor in it, I just want to find each instance with the cloth armor and change it.

<config>
<setattribute xpath="lootcontainers/lootgroup/item[@name='armorClothJacket']" name="count">3,6</setattribute>
<setattribute xpath="lootcontainers/lootgroup/item[@name='armorClothPants']" name="count">3,6</setattribute>
<setattribute xpath="lootcontainers/lootgroup/item[@name='armorClothBoots']" name="count">3,6</setattribute>
<setattribute xpath="lootcontainers/lootgroup/item[@name='armorClothGloves']" name="count">3,6</setattribute>
<setattribute xpath="lootcontainers/lootgroup/item[@name='armorClothHat']" name="count">3,6</setattribute>
<set xpath="/lootcontainers/lootgroup/item[@name='armorClothJacket']/@name">resourceCloth</set>
<set xpath="/lootcontainers/lootgroup/item[@name='armorClothPants']/@name">resourceCloth</set>
<set xpath="/lootcontainers/lootgroup/item[@name='armorClothBoots']/@name">resourceCloth</set>
<set xpath="/lootcontainers/lootgroup/item[@name='armorClothGloves']/@name">resourceCloth</set>
<set xpath="/lootcontainers/lootgroup/item[@name='armorClothHat']/@name">resourceCloth</set>

</config>


In my mind, the game is updating all instances with Cloth Armor by:

-Attaching a new attribute count=3,6

-Replacing all instances with Cloth Armor with resourceCloth

 
So to expand on my modding skills, I am now modifying the loot xml file.  The first thing I am doing is changing the weapons and armor to basic resources (simulating that all you are finding are broken armor and weapons).  I think I am doing it right, but wanted to confirm.  I don't get any errors when I load up my test world, but not sure yet if the game is doing what I think it is doing:

First, what I think I am doing:

-At each loot group with cloth armor, I am replacing the armor with resourceCloth and setting up a count range of this resource.

Now my code.  Instead of individually changing each loot group with cloth armor in it, I just want to find each instance with the cloth armor and change it.

<config>
<setattribute xpath="lootcontainers/lootgroup/item[@name='armorClothJacket']" name="count">3,6</setattribute>
<setattribute xpath="lootcontainers/lootgroup/item[@name='armorClothPants']" name="count">3,6</setattribute>
<setattribute xpath="lootcontainers/lootgroup/item[@name='armorClothBoots']" name="count">3,6</setattribute>
<setattribute xpath="lootcontainers/lootgroup/item[@name='armorClothGloves']" name="count">3,6</setattribute>
<setattribute xpath="lootcontainers/lootgroup/item[@name='armorClothHat']" name="count">3,6</setattribute>
<set xpath="/lootcontainers/lootgroup/item[@name='armorClothJacket']/@name">resourceCloth</set>
<set xpath="/lootcontainers/lootgroup/item[@name='armorClothPants']/@name">resourceCloth</set>
<set xpath="/lootcontainers/lootgroup/item[@name='armorClothBoots']/@name">resourceCloth</set>
<set xpath="/lootcontainers/lootgroup/item[@name='armorClothGloves']/@name">resourceCloth</set>
<set xpath="/lootcontainers/lootgroup/item[@name='armorClothHat']/@name">resourceCloth</set>

</config>


In my mind, the game is updating all instances with Cloth Armor by:

-Attaching a new attribute count=3,6

-Replacing all instances with Cloth Armor with resourceCloth


That looks correct, but there's an easier way to do it.

XPath comes with a "starts-with" function that you can use in your selectors. So that code could be rewritten like this:

Code:
<config>
	<setattribute xpath="lootcontainers/lootgroup/item[starts-with(@name, 'armorCloth')]" name="count">3,6</setattribute>
	<set xpath="/lootcontainers/lootgroup/item[starts-with(@name, 'armorCloth')]/@name">resourceCloth</set>
</config>
 
Back
Top