Think Tree Stuff - KonradHeinser/EBSGFramework GitHub Wiki
Think trees govern everything that a pawn may decide to do, from satisfying needs to punching things to using abilities. Normally, the last one is somewhat of a pain to handle with basic Rimworld stuff, so I set up this Thing Tree Stuff to hopefully streamline the process.
Most of this stuff will only help with setting up ability casting for both non-colonists and colonists, though a few of the things do have use outside of ability autocasting, the most important of which is the record passage ThinkNode.
WARNING : The EBSG think tree takes effect before the pawn satisfies needs, self tending, and most other things that a pawn will voluntarily do. This means that if you have something isn't designed for more emergency type situations, you should probably set up your own tree to handle it. This page will briefly discuss that topic later
For those who aren't very familiar with think trees, this section won't make too much sense until later. This section can still be of help though as ThinkNode_RecordPassage is something designed to help with debugging issues. Its most basic appearance looks like this:
<li Class="EBSGFramework.ThinkNode_RecordPassage" />
This will create a log message every time a pawn reaches that point in the tree with the default text of "Point A has been passed - thinking pawn's name: " followed by the pawn's name. This is designed to make it easy to know who is even reaching this point in your tree, and will work even in trees outside of ones branching from the one in this framework. You can change the "Point A has been passed" portion of the message like so:
<li Class="EBSGFramework.ThinkNode_RecordPassage">
<output>This is a new message</output>
</li>
That would change the message to be "This is a new message - thinking pawn's name: " followed by the pawn's name.
The node also has additional options that are disabled by default:
- requiredPawnKindDef : The pawn must have this PawnKindDef to give any output
- reportJob : Default (False) : Checks if the pawn has a current job def, and if it does it reports the defName it finds
- reportStance : Default (False) : Checks the pawn's current stance (i.e. stance_busy), and if they currently have one it reports it. Has more use for those working on their own C# stuff
- reportTarget : Default (False) : Reports the current target the pawn has
- autoSearchForTarget : Default (True) : When getting the current target, this allows it to search for a nearby hostile if one isn't actively selected
- reportLord : Default (False) : Checks the pawn's current lord, and reports it if they have one
- reportMindState : Default (False) : Reports various parts of the pawn's mind state, namely what it has recorded for the enemyTarget, meleeThreat, and duty
- reportVerb : Default (False) : Reports the pawn's current verb. If the pawn has a current target, or autoSearchForTarget is set to True, the code will try to use that as a reference for getting the verb
While I doubt that this is something that you'd be thinking about this early in the process, this is important for those who want to use the EBSG Think Tree because one of the things it checks for is that there is any mod other than the framework registered in the EBSG Recorder. It does this so that if there is no other mod registered, it doesn't keep making the game process everything in the tree.
Settings added to the EBSG Recorder allow you to set points in your tree that completely disable everything after it if the setting is set to false. For example, EBSG_BasicThinkTree has one set up that will completely disable the entire tree (which can disable yours if you're using one of the insert tags) so people with performance issues or bugs can stop them:
<ThinkTreeDef>
<defName>EBSG_BasicThinkTree</defName>
<insertTag>Humanlike_PostMentalState</insertTag>
<insertPriority>100</insertPriority>
<thinkRoot Class="ThinkNode_Priority">
<subNodes>
<li Class="EBSGFramework.ThinkNode_ConditionalEBSGSetting">
<uniqueID>EBSGFramework</uniqueID>
<settingID>EBSGThinkTree</settingID>
<subNodes>
</subNodes>
</li>
</subNodes>
</thinkRoot>
</ThinkTreeDef>
If you don't want settings, or just aren't sure yet, set up a patch operation like this so you can continue on. Think of the uniqueID as being similar to a defName, if someone else has the same one things may go downhill very fast:
<Operation Class="PatchOperationAdd">
<xpath>Defs/EBSGFramework.EBSGRecorder[defName="EBSG_Recorder"]/thinkTreeSettings</xpath>
<value>
<li>
<uniqueID>InsertYourUniqueIDHere</uniqueID>
</li>
</value>
</Operation>
If you only want settings that allow players to disable specific abilities, check out this page.
If you already know some of the settings you want (i.e. a complete disable setting for just your stuff), you can expand upon the patch to include all the stuff you're looking to add. Two important notes. First, while the description is optional, the label is required. Without the label, errors will pop up whenever the mod menu is opened. Second, the setting you make will always default to being active unless you set defaultSetting to be false for the individual setting. The below code gives all the stuff you need to make settings. If you're looking to have more than two settings, just copy and paste another li under individualSettings:
<Operation Class="PatchOperationAdd">
<xpath>Defs/EBSGFramework.EBSGRecorder[defName="EBSG_Recorder"]/thinkTreeSettings</xpath>
<value>
<li>
<uniqueID>InsertYourUniqueIDHere</uniqueID>
<label>label</label>
<description>optional description.</description>
<individualSettings>
<li>
<settingID>settingA</settingID>
<label>label</label>
<description>optional description.</description>
</li>
<li>
<settingID>settingB</settingID>
<label>label</label>
<description>optional description.</description>
<defaultSetting>False</defaultSetting>
</li>
</individualSettings>
</li>
</value>
</Operation>
Sample think node:
<li Class="EBSGFramework.ThinkNode_ConditionalEBSGSetting">
<uniqueID>InsertYourUniqueIDHere</uniqueID>
<settingID>settingA</settingID>
<subNodes>
</subNodes>
</li>
IMPORTANT NOTE : The EBSG Framework has two settings. The first is one sets the entire tree to be active or inactive, and the second is one that specifically enables/disables the colonist portion of the tree. While the tree is active by default, the colonist portion is inactive by default, so if you set up colonist auto-casting, you need to make a note for users that it will only be available if they enable it in the settings. Without activating it, only non-colonists will auto-cast. These settings only impact the EBSG_BasicThinkTree ThinkTreeDef and any tree that uses one of its insertTags
insertTags are something ThinkTreeDefs use to make it easier for other think trees to insert themselves in the middle of things. The EBSG Think Tree makes a bunch of these that check for various common conditions to minimize the chance that a bunch of mods will need to keep making these checks. As an important note, insertTags are not the only way that additional trees can be branch from an original one, they are just the method that EBSG_BasicThinkTree uses. Here's an example of how to use one to start a ThinkTreeDef:
<ThinkTreeDef>
<defName>NewDefName</defName>
<insertTag>EBSGAI_NonColonist</insertTag>
<insertPriority>100</insertPriority>
<thinkRoot Class="ThinkNode_Priority">
<subNodes>
<li Class="EBSGFramework.ThinkNode_RecordPassage" />
</subNodes>
</thinkRoot>
</ThinkTreeDef>
If you have a tree like this set up and spawn an enemy (I usually use ancient soldiers or drifters), you should now see the occasional message about that pawn passing point A. As the insertTag implies, this tree takes place for all non-colonists when the EBSG tree is reached. The vanilla humanlike tree has 4 insert tags:
- Humanlike_PostMentalState : Happens before most voluntary actions, like satisfying needs, self tending, and drafted orders. The EBSG Think tree inserts here, but includes a check to ensure that drafted pawns don't have a current order
- Humanlike_PostDuty : Happens after draft orders, npc self tending, and lord duty stuff, but before everything else
- Humanlike_PreMain : Happens after prisoner stuff (i.e. escaping), emergency work, and eating
- Humanlike_PostMain : Happens after pretty much everything. Most things from this point on are for idle pawns
As mentioned previously, the EBSG Think Tree is intended more for emergency stuff, like combat abilities. It's put before self tending to allow for healing abilities to be prioritized, and before general drafted stuff to allow for combat abilities to occasionally be used. In some cases, it may be better to use this more as a guide of making your own tree completely separate from the EBSG one. For example, if your ability is for something like satisfying needs, it may be better to make a tree that uses the insertTag Humanlike_PreMain instead.
The EBSG_BasicThinkTree has 19 insertTags that can be used, which are encountered in the below order. Indentation indicates that if the previous condition failed, the specified tag will not be reached:
- EBSGAI_Colonist : Just checks if the pawn is a colonist
- EBSGAI_UndraftedAndFreeColonist : Checks if the pawn can "do a constant think tree job now"
- EBSGAI_UndraftedColonist_NoEnemyOnMap : Checks that there aren't any active enemies in the map
- EBSGAI_UndraftedColonist_EnemyOnMap : Checks if there is an active enemy on the map
- EBSGAI_UndraftedColonist_HarmedRecently : Checks if the pawn has been harmed in the past 120 ticks
- EBSGAI_UndraftedColonist_ShotRecently : Checks if the pawn has been shot in the past 600 ticks
- EBSGAI_UndraftedColonist_MeleeThreat : Checks if there is an enemy pawn within a range of 5
- EBSGAI_UndraftedColonist_NearbyEnemy : Checks if there is any enemy pawn within a range of 40
- EBSGAI_UndraftedColonist_ManyNearbyThreats : Checks if there are 5 enemy pawns within a range of 20
- EBSGAI_DraftedAndFreePawn : Checks if the pawn is drafted and not doing something else
- EBSGAI_DraftedColonist_EnemyOnMap : Checks if there is an active enemy on the map
- EBSGAI_DraftedColonist_HarmedRecently : Checks if the pawn has been harmed in the past 120 ticks
- EBSGAI_DraftedColonist_ShotRecently : Checks if the pawn has been shot in the past 600 ticks
- EBSGAI_DraftedColonist_MeleeThreat : Checks if there is an enemy pawn within a range of 5
- EBSGAI_DraftedColonist_NearbyEnemy : Checks if there is any enemy pawn within a range of 40
- EBSGAI_DraftedColonist_ManyNearbyThreats : Checks if there are 5 enemy pawns within a range of 20
- EBSGAI_DraftedColonist_EnemyOnMap : Checks if there is an active enemy on the map
- EBSGAI_UndraftedAndFreeColonist : Checks if the pawn can "do a constant think tree job now"
- EBSGAI_NonColonist : Checks that the pawn is not a colonist
- EBSGAI_NonColonist_HarmedRecently : Checks if the pawn has been harmed in the past 120 ticks
- EBSGAI_NonColonist_ShotRecently : Checks if the pawn has been shot in the past 600 ticks
- EBSGAI_NonColonist_MeleeThreat : Checks if there is an enemy pawn within a range of 5
- EBSGAI_NonColonist_ManyNearbyThreats : Checks if there are 5 enemy pawns within a range of 20
- EBSGAI_NonColonist_NearbyEnemy : Checks if there is any enemy pawn within a range of 40
Think nodes are a topic so broad I wouldn't really be able to cover everything on this page if I wanted to, and even when I limit it to just conditionals, it's still too broad a category to talk about every one. Because of those issues, this section will actually be rather short because your best bet is to have a perusal through some vanilla think trees to find examples of some of them in use. I'm not planning on leaving you completely unprepared for this one though.
First, up in Handling Settings we actually use two think nodes, ThinkNode_Priority and EBSGFramework.ThinkNode_ConditionalEBSGSetting. Most think nodes (though not all), have subNodes which can link to other think nodes (like more conditions) and the final job driver stuff (topic for later). Conditionals also have a few more tags within them beyond the subNodes tag. true is the one you will encounter the most because this is makes it so a conditional will only allow things to go through when it is NOT true. There are usually other tags available that depend on the conditional in question that sets up how it works, like the uniqueID and settingID tags from EBSGFramework.ThinkNode_ConditionalEBSGSetting.
Second, this is a list of some of the vanilla conditionals that may be useful. This list is far from extensive, but may help with setting up auto-casts. It has both the class and all of the specific tags for that conditional
- ThinkNode_ConditionalHashIntervalTick : Sets how often it can be attempted. Allows you to stop the pawn from trying to cast the ability constantly when its not on cooldown
- interval : Default (0) : How many ticks you want?
- ThinkNode_ConditionalHasAbility : Checks to make sure the pawn has the ability you are trying to make them cast
- ability : The AbilityDef in question. Must be filled out with an AbilityDef because there is no default
- ThinkNode_ConditionalShotRecently : Checks if the pawn has been shot recently
- thresholdTicks : Default (2500) : How recently the pawn was shot in ticks
- ThinkNode_ConditionalHasHediff : Checks if the pawn has the hediff
- hediff : The HediffDef in question. Must be filled out with a HediffDef because there is no default
- severityRange : Default (0) : Range of minimum float values for severity. If not filled in, severity doesn't matter. If filled in, picks a random number in the range to see if the hediff severity is higher
- ThinkNode_ConditionalAbilityCastLastTick : Ticks since the ability was last cast. Mostly useful for stopping the checks the subNodes do when the ability is on cooldown, or for stopping an ability with charges from expending all of them in quick succession
- ability : The AbilityDef in question. Must be filled out with an AbilityDef because there is no default
- minTicksAgo : Default (0) : Does as it promises. Usually set to the cooldown of the ability, but can be any integer
- maxTicksAgo : Only really useful if the ability is intended to be used in quick succession with itself or another ability
- ThinkNode_ConditionalNeedPercentageAbove : Checks the level of a specific need to make sure it is above the threshold. Only included in case the ability you're trying to set up changes need levels, be it with this framework's comp or through any other comp
- need : The NeedDef to check for. Must be filled out with a NeedDef because there is no default
- threshold : Default (0.0) : The minimum percentage as a decimal value (i.e. 10% becomes 0.1)
- ThinkNode_ConditionalTotalDamage : Compares the total damage on all of the pawn's parts with the lethal damage threshold, and if higher than the threshold the condition is met (i.e. The default activates when the pawn has 75% health remaining or less)
- thresholdPercent : Default (0.25) : The percentage of the pawn's lethal damage threshold that they need to have taken to continue this branch. The float represents the percentage (i.e. 0.25 = 25%)
Keep in mind that the above list is not remotely close to being robust. It only contains conditionals that I have personally found useful for AI stuff, but you may find others that can help your particular case. Looking through various think trees that handle abilities may help you find some others, or if you're feeling particularly dangerous you can decompile the vanilla code to try to find some other options
And finally, this framework adds a bunch of new conditionals that you can use that were either missing from vanilla or not as robust as I like. While the think nodes included above are usable without this framework, these ones will require the framework be enabled. These ones are structured the same as the ones I mentioned above, but by this point I'm going to assume that you understand how to deal with things like thresholds and null defaults that obviously need to be filled:
- EBSGFramework.ThinkNode_ConditionalAboveMood : Checks if the pawn's mood is above the threshold
- minMood : Default (0.5) : The minimum threshold mood needs to be at to allow this condition to pass
- EBSGFramework.ThinkNode_ConditionalAnyActiveEnemyInMap : Passes if any active hostile is on the map
- EBSGFramework.ThinkNode_ConditionalColonistShouldFlee : Checks if the pawn should flee automatically. This is determined by checking things like invisibility (if invisible, the pawn assumes they are safe), forced jobs/drafting, and hostility response settings (if set to ignore or attack threats, then no fleeing abilities needed). This is intended for checking if a non-combatant should use an escape ability due to a nearby threat
- EBSGFramework. ThinkNode_ConditionalConcerningBleeding : Checks if the current bleed rate total is over the threshold
- bleedThreshold : Default (0.01) : The threshold
- EBSGFramework.ThinkNode_ConditionalHasAbilityUsable : More in-depth version of ThinkNode_ConditionalHasAbility that first checks make sure they have the ability (similar to the vanilla one), and afterwards it checks to make sure the ability is not on cooldown. If applicable, it also checks to make sure it has enough charges, has enough hemogen, has enough of a DRG resource, and is not disabled by the autocast toggle comp (discussed later)
- EBSGFramework.ThinkNode_ConditionalHasAnyOfHediff : Checks if the pawn has any of the listed hediffs at any severity level
- hediffs : The list of HediffDefs
- EBSGFramework.ThinkNode_ConditionalHasTargetNoFriendlyFire : Checks if the target the pawn has doesn't have any non-hostiles around. This will not check if the target themselves are a valid target
- ability : The AbilityDef in quesiton. If left blank, other fields need to be filled in to provide parameters. Note that this conditional will not accept abilities with a warmup time over 5 due to the lack of predictability
- minRadius : Default (-1) : The area to check. If ability is not filled in, this needs to be set, and if ability is set
- minEnemies : Default (1) : While checking for non-hostiles the code also keeps track of how many hostiles it finds. If that total is below this number, it will not pass even if there weren't non-hostiles
- noTarget : Default (False) : If a specific ability is not referenced, this will tell the code that the assumed target is the caster's position
- avoidSelfHit : Default (True) : If noTarget is false, then this will try to make sure the pawn is not going to accidentally hit themselves
- EBSGFramework.ThinkNode_ConditionalHighResourceLevels : Checks if a resource gene is above a certain value. Should work with any type of resource gene, including DRGs
- gene : The GeneDef in question
- useTargetValue : Default (True) : Uses whatever the player set as the target value for the threshold
- minLevel : Default (0.9) : If not using the target value, this is used as the threshold
- EBSGFramework.ThinkNode_ConditionalNearbyAllies : Checks if there is a certain number of allies close to the current position. Allies are humanlike pawns in the same faction, so this won't be randomly triggered by pets, though pawns that don't have a faction are unable to recognize pawns that came into the map with them
- radius : Default (5) : The radius around the pawn to check
- minCount : Default (1) : The number of allies required to pass. The caster is not included
- EBSGFramework.ThinkNode_ConditionalNearbyEnemyTarget : Checks if there is a hostile pawn nearby. This only checks the pawn's immediate target, so if they are targeting an ally with something, they will ignore the enemy
- searchRadius : Default (40) : The area around the pawn to check
- EBSGFramework.ThinkNode_ConditionalNeedTending : Checks if the pawn has a hediff that needs tending
- EBSGFramework.ThinkNode_ConditionalPawnDraftedAndFree : Checks if the pawn is drafted and doesn't have a forced job
- EBSGFramework.ThinkNode_ConditionalTargetIsMechanoid : Checks if the current target is a mechanoid, or if the pawn isn't currently targeting something it checks the nearest hostile
- EBSGFramework.ThinkNode_ConditionalTendableAlly : Checks if any pawn in the faction on the same map have tendable hediffs
- EBSGFramework.ThinkNode_ConditionalTooManyNearbyThreats : Checks if there are too many hostiles nearby
- dangerRadius : Default (5) : How far to search
- minCount : Default (1) : How many hostiles need to be found for the condition to be met. 1 means any hostile nearby will trigger this
- EBSGFramework.ThinkNode_ConditionalUsingRangedWeapon : Checks if the pawn is using a ranged
- onlyPassIfDraftedOrAttacker : Default (True) : Causes player pawns to only meet this condition if they are either drafted or set to auto-attack nearby enemies
- EBSGFramework.ThinkNode_ConditionalValidTargetForAbility : Checks if the target in question is valid for the target parameters and the ability's comps. If the pawn has an active target due to a job or similar effect, that target is always the checked target. This is mainly for abilities that have specific conditions that must be met that don't require think nodes because they are part of the ability itself
- ability : The AbilityDef in question
- onlyHostiles : Default (True) : Only allows pawns that are hostile to this pawn
- onlyInFaction : Default (False) : Only allows pawns that are not hostile to this pawn. Does not work with onlyHostiles
- autoSearch : Default (True) : If the pawn is not currently targeting something, this will cause the pawn to look at the closest viable pawn
- EBSGFramework.ThinkNode_ConditionalNearbyCover : Checks the cover around the pawn to see if the total cover score is equal to or greater than the value you set. If you aren't sure what value to set, I recommend adding a record passage inside of your condition to see when it does and does not succeed
- cover : Default (4) : The minimum score to pass
All the conditions are right, and now it's time for the final question. What do we do? Job Givers are even more varied than the think nodes, making it even more impossible to cover in any notable depth within this little guide. The short version, job givers in think trees are what tell the pawn to do something. They are added to the tree in subNodes similar to how a think node is added, but don't have subNodes themselves because they are the final product of what the introduction promises. "Think trees govern everything that a pawn may decide to do, from satisfying needs to punching things to using abilities".
The jobGivers I mention here usually only have one tag in common, ability, and this is because this guide isn't really intended to be a good example for anything beyond auto-casting abilities. Any of the jobGivers mentioned here that don't have "EBSGFramework." in front of them are vanilla jobGivers, and as such don't care if this framework is enabled. If a job giver can't be used by a pawn, then the tree will act the same as it would with a failed conditional, and continue to the next node it can find:
- JobGiver_AICastAbilityOnSelf : Cast the ability on the caster. This only works if the ability is either able to be applied on the caster through the targeting parameters, or has targetRequired set to false
- JobGiver_AICastAnimalWarcall : Attempts to cast the ability on the nearest animal with no faction that doesn't have the BerserkWarcall MentalStateDef. The utility of this one is very limited, but listed here just in case someone finds a use for it
- JobGiver_AIJumpEscapeEnemies : Targets a location as far away from hostiles as feasibly possible. Personally, I've only made this work with Verb_CastAbilityJump, but other ability verbs can likely work for this as well. As far as I know, escape abilities that rely on comps (like most teleportation abilities) generally will not work well for this, though there may be exceptions. Experimentation is your friend for this one
- EBSGFramework.JobGiver_AICastAbilityGoToTarget : Causes the pawn to use a movement ability to get to a target. This should work with both the vanilla jumps and with teleportation comps
- safeJumpsOnly : Default (True) : Causes pawns to only use half the range to avoid having them jump too far from their allies
- onlyWhileWieldingMeleeWeapon : Default (True) : Causes pawns to only use the ability if they are armed and not using a ranged weapon
- EBSGFramework.JobGiver_AICastAnyOfAbilityOnEnemyTarget : Casts any of the abilities on the nearest (or currently targeted) enemy. If the pawn doesn't have any of the abilities or they are all on cooldown, this will be skipped
- abilities : The list of abilities to check for. ability will NOT work for this one
- hashInterval : Default (10) : Places a hash interval limiter similar to ThinkNode_ConditionalHashIntervalTick
- EBSGFramework.JobGiver_AICastAbilityTargetToCaster : Casts the ability with destination with a close enemy as the target, and the caster's position as the target. This will only work with abilities that don't use RandomInRange or randomRange
- ability : The AbilityDef to use
- maxBodySize : Default (1) : Used to limit body size of the target to avoid situations where something like an angry muffalo is pulled to the caster
- EBSGFramework.JobGiver_AICastBerserkAbility : Casts the ability on a semi-random target in range. While it was originally designed for berserk type abilities, it should work with any ability that uses CompProperties_AbilityGiveMentalState
- EBSGFramework.JobGiver_AICastHealingAbility : Casts the ability on a pawn in the same faction as the caster that needs healing. If the target is too far, the pawn will move closer to try to use the ability. If it is a self-healing ability, JobGiver_AICastAbilityOnSelf is what you are looking for
- bleedThreshold : Default (0.1) : How much the pawn needs to be bleeding to use the ability. If allTendable is true, then this is used as the priority threshold. The closest target is always the prioritized one
- allTendable : Default (True) : Causes the ability to be used on anyone with a tendable bleeding hediff. Having this true allows for the creation of two levels of importance, with anyone over the bleedThreshold always being prioritized
- EBSGFramework.JobGiver_AIResurrectHumanoid : Automatically uses the ability on a humanlike target in the pawn's faction that can be resurrected
- expiryInterval : Default (500) : How long until the job expires automatically. Unless you have a long warmup time, this probably shouldn't be touched
- maxRegions : Default (50) : The max regions for the cast position request. Unless you are really confident this needs changed, this should probably remain at the default
- EBSGFramework.JobGiver_AITeleportEscapeEnemies : Variation of JobGiver_AIJumpEscapeEnemies that uses with the vanilla teleport comp instead of the jump verb
- EBSGFramework.JobGiver_AICastHostileAbilityBetween : Casts an ability between the caster and the target. The ability needs to be able to target locations for this giver to work, and may frequently fail in environments with more obstacles
- percentage : Default (0.5) : A value between 0 and 1 exclusive that determines where in the line between the caster and the target to try to cast the skill. 0.5 attempts to cast the ability exactly half way between
Partial example from one of the superhero genes. Blood Infusement is a hediff that applies benefits to the caster, and this portion of the think tree made the pawn automatically refresh the hediff with their ability (assuming they had it) whenever it dropped below a severity of 3.5 (normally checks for above, but invert causes it to check for below 3.5 severity)
<li Class="ThinkNode_ConditionalHasAbility">
<ability>SHG_Hemomancer_BloodInfusement</ability>
<subNodes>
<li Class="ThinkNode_ConditionalHasHediff">
<hediff>SHG_Hemomancer_BloodInfusement</hediff>
<severityRange>3.5</severityRange>
<invert>true</invert>
<subNodes>
<li Class="JobGiver_AICastAbilityOnSelf">
<ability>SHG_Hemomancer_BloodInfusement</ability>
</li>
</subNodes>
</li>
</subNodes>
</li>
If you did not add any settings earlier and your tree is branching off of the EBSG one, make sure to add the smaller patch operation to your mod, with InsertYourUniqueIDHere replaced with your own id. The EBSG tree is set up to remain inactive unless at least one other mod is listed in the recorder to avoid pointless performance costs:
<Operation Class="PatchOperationAdd">
<xpath>Defs/EBSGFramework.EBSGRecorder[defName="EBSG_Recorder"]/thinkTreeSettings</xpath>
<value>
<li>
<uniqueID>InsertYourUniqueIDHere</uniqueID>
</li>
</value>
</Operation>