Skip to content
CJ Kucera edited this page Aug 29, 2018 · 20 revisions

Almost everything in here is courtesy of LightChaosMan so go say thanks, that said don't annoy him about this stuff since its complicated.

Welcome to the BPD-classroom. Here we will be explaining how BPDs (Behavior Provider Definition) work, and how you can change them, without crashing your game, as well as the parts that are currently unknown.

Lesson Zero: Useful Tools

There are a couple of tools which may come in useful as you start diving into BPDs.

  • BLCMM is the main tool which you should already be quite familiar with. Its Object Explorer will be invaluable in taking a look at BPDs and navigating around to find out how things work. (And, of course, you'll be using it to make whatever changes you want to make.)
  • FT/BLCMM Explorer is a tree-based view into the same data available in BLCMM's Object Explorer, and is sometimes useful as a higher-level overview. BLCMM's OE should still be your first stop for data, but it may be useful to have this as well (it's useful for getting a nice visual list of all the Behaviors inside a BPD, for instance)
  • BPD/Kismet Grapher (sourcecode) is an online tool to generate nice visual graphs of BPDs (and Kismet sequences, though we won't be talking about those here). These graphs are just about invaluable to get a feel for how a BPD actually flows, without having to do a ton of legwork yourself.
  • Two's Compliment Calculator can be used to interpret some of the strange numbers found in BPDs, and talked about during Lesson Three.

Additionally, some older tools are still available:

  • Adudney's Behavior Tool can also be used to get a feel for the structure of BPDs, though it's been largely superceded by the BPD/Kismet Grapher.

Lesson One: What is a BPD?

BPDs, along with Kismets, govern some of the most interesting stuff in the game.

NPC's transfering during quests, the Butcher ammo gimmick, intersting skills like blood soaked shields, all action skills, amp shields, enemies spawning attached to each other, and 100s of other things in the game.

Borderlands has a bunch of Behavior_* objects which do very simple activities. A Behavior_SimpleAnimPlay behavior will start an animation, a Behavior_SpawnItems behavior will spawn some items int he world, and a Behavior_TriggerDialogEvent behavior will trigger some dialog, for instance.

BPDs are attached to specific objects in the game (like an enemy, a projectile, a container, etc) and collect a group of these individual Behavior_* objects together using a bunch of logic, so that more complex things can happen. If you need more than one Behavior to accomplish your goal, a BPD comes into play.

For example, for amp shields, the BPD more or less translates to:

  1. When a shot is fired
    1. If the current shield value is at maximum, then damage the shield
    2. Play a sound effect.

The graph for this, via the BPD/Kismet Grapher, looks like this:

Amp Shield BPD Graph

There are a total of four Behavior objects referenced in there, and the logic of the BPD is what ties them together and ensures that things happen in the proper order.

Using BPDs, you can create complicated if-then-else constructs, with random chance, delay and whatnot in between. In the end, it's about as close as you can get to actual programming inside Borderlands modding.

If you want an example of a full blown web of behaviors, have a look at the BPD of an action skill.

Lesson Two: What a BPD Looks Like

Alright, so we know it does cool stuff, but what does it actually look like?

We'll be using one main BPD during this tutorial -- the BPD for amp shields: GD_Shields.Skills.Impact_Shield_Skill:BehaviorProviderDefinition_0

Dumping that object name inside BLCMM's Object Explorer will yield:

BPD Dump inside BLCMM's Object Explorer

All BPDs contain an array BehaviorSequence. For simpler BPDs, this array will usually only contain a single entry, which is the actual BehaviorSequence we're interested in. More complicated BPDs may have many more.

When referring to a BPD in the rest of class, we will be referring to whatever is inside this BehaviorSequence.

Each BehaviorSequence contains a couple of fields of its own. The parts that are interesting to us are EventData2, BehaviorData2, ConsolidatedOutputLinkData, ConsolidatedVariableLinkData and ConsolidatedLinkedVariables.

As the name implies, a BehaviorSequence, is actually a sequence of behaviors. That is, it is a load of behaviors linked together, in some way.

The actual behaviors that are being linked are the ones listed in BehaviorData2.

How they are linked, is not clear at all, at least not at first glance. You'll notice throughout the entire BPD, there's all these wierd looking numbers, that seem completely random.

Well, they're not random. These numbers are actually the things that connect everything together, in a 'connect the dots' kind of fashion.

Lesson Three: How Does a BPD Work?

Alright, let's start with the cornerstone of BPDs, the magic that's used to connect everything: what do all those weird looking numbers mean?

These numbers, are actually two numbers merged together, for efficiency and stuff.

This is implied by the names of the fields that precede these weird numbers, like LinkIdAndLinkedBehavior or ArrayIndexAndLength.

Okay, so how are there two numbers in there?

The answer is binary numbers.

Borderlands uses 32 bit integers for most things in the game, and these weird numbers are integers.

So how do you put 2 numbers into a single 32 bit number?

Well, you give each number 16 bits, and then just push them together.

So, lets look at a specific example, the first weird number in the BPD, in the eventdata of our example: OutputLinks=(ArrayIndexAndLength=196609). If we convert 196609 to binary we find 110000000000000001.

Since this is just two 16 bit numbers pushed together, we can split them by taking the rightmost 16 bits and whatever is leftover (since leading zer0's aren't shown).

This would turn 11 0000000000000001 into 11 and 1, which are both binary numbers, representing 3 and 1 in base 10.

Combining this with the name ArrayIndexAndLength, it makes sense that it means that the number represents an array index of 3, and a length of 1, for whatever that may be used.

Now that we know how to read the weird numbers, lets see how a BPD uses them to 'connect the dots'.

Following ConsolidatedOutputLinkData

Every sequence needs a start, so let's start there, how do we find the first behavior in the sequence?

Naturally, the behavior in question is somewhere in BehaviorData2. The first weird number that we need is the OutputLinks of the event that triggers this sequence, which happens to be the number that we analyzed above. We know that this number, 196609, actually means 3 and 1.

You might think: "alright, so the first behavior is the one at index 3 of BehaviorData2", but, you'd be wrong.

All pointers towards behaviors are actually rerouted trough ConsolidatedOutputLinkData. This means that the thing we just found is whatever the things are at index 3 through 3 (length 1) of ConsolidatedOutputLinkData, which is the single element LinkIdAndLinkedBehavior=0,ActivateDelay=0.000000.

Now, this being a great example, LinkIdAndLinkedBehavior=0 is actually one of those compound numbers, except this time it represents a LinkId of 0 and a linkedBehavior of 0, which combines to a total number of 0.

For now, ignore LinkId, the thing we we are interested in the second number, the LinkedBehavior, which happens to also be 0.

This means that the first behavior in this sequence is the behavior at index 0 of the BehaviorData2 array. This happens to be

(
    Behavior = 
Behavior_SetShieldTriggeredState'GD_Shields.Skills.Impact_Shield_Skill:BehaviorProviderDefinition_0.Behavior_SetShieldTriggeredState_35',
    LinkedVariables = 
    (ArrayIndexAndLength=1),
    OutputLinks = 
    (ArrayIndexAndLength=2)
)

We now know the starting point of our sequence. All that's left is to figure out how the rest of the behaviors are linked together.

Luckily, if you were able to follow along this far, that part is relatively easy. Each entry in the BehaviorData2 array, contains a field OutputLinks.

This field tells us which behaviors follow this one. Again, this is one of these compound numbers, and since it is used to eventually point us to other behaviors, it will first point us to ConsolidatedOutputLinkData, just like with the initial link.

In this case, our compound number is 2, which translates to an ArrayIndex of 0 and a Length of 2, after doing the binary conversion.

This means we look at the two entries (length 2) starting at index 0, so concretely, we look at the elements at index 0 and index 1.

These are the following two elements: LinkIdAndLinkedBehavior=-16777214,ActivateDelay=0.000000 and LinkIdAndLinkedBehavior=-16777213,ActivateDelay=0.000000.

Starting with -16777214, which translates to 1111111100000000 0000000000000010, which translates to -256 and 2. Similarly, -16777213 translates to -256 and 3.

This would be the moment to tell that we're using so called 2's complement to convert from and to binary notation, which is what enables negative numbers.

To convert them yourself, you can use something like this.

Whenever converting to binary, always use 32 bit options, since that's what BL2 uses to generate these numbers in the first place.

When converting back, leave in the leading zer0s, they are important for 2's complement.

As before, we disregard the (negative) linkIDs, and look at just the LinkedBehaviors, for which we have found the indices 2 and 3.

So, the two behaviors following the initial behavior are those stored at index 2 and 3 of BehaviorData2, or the third and fourth element, since in computer science, we count starting from 0.

These are the Behavior_CompareFloat and Behavior_PostAkEvent behaviors. Following the same procedure, we find that the Behavior_PostAkEvent isn't followed by anything, and Behavior_CompareFloat is followed by the Behavior_SimpleMath, trough the third element of ConsolidatedOutputLinkData.

BPD Variables

So, we now know how the behaviors are linked together, but how do the behaviors know what to do?

Some behaviors work fine as a standalone behavior, like Behavior_Explode. Others, like Behavior_SimpleMath require some sort of input/output to work their magic.

For the amp shield example we're using, shield stats are of particular interest. Such data is conveyed trough variables, or properties if you will.

Each entry in the BehaviorData2 array has a field LinkedVariables. Using the same compound numbers as before, this points towards sub-arrays of ConsolidatedVariableLinkData.

So, for example, Behavior_SimpleMath_4 has a field LinkedVariables equal to ArrayIndexAndLength=196611, which translates to an Array index of 3 and a length of 3.

So, that means that Behavior_SimpleMath_4 uses the entries 3, 4 and 5 of ConsolidatedVariableLinkData, or

(PropertyName="A",VariableLinkType=BVARLINK_Input,ConnectionIndex=0,LinkedVariables=(ArrayIndexAndLength=131073),CachedProperty=None),
(PropertyName="B",VariableLinkType=BVARLINK_Input,ConnectionIndex=0,LinkedVariables=(ArrayIndexAndLength=262145),CachedProperty=None),
(PropertyName="Result",VariableLinkType=BVARLINK_Output,ConnectionIndex=0,LinkedVariables=(ArrayIndexAndLength=327681),CachedProperty=None)

Like earlier with behaviors, this is just an intermediate step.

From here we follow the next LinkedVariables field, which is another ArrayIndexAndLength. As far as we've yet to find an exception, the Length part of the numbers stored in these particular LinkedVariables is always equal to 1, since it points to a single entry in the ConsolidatedLinkedVariables array.

Let's take the first entry from above as an example, which has LinkedVariables=(ArrayIndexAndLength=131073), which translates to index 2 and length 1.

Similarly, the numbers corresponding with B are index 4 and length 1, and result maps to index 5 and length 1.

From these 3 indices (2,4 and 5) (and yes, they are actually just indices, since they are all of length 1), we go to ConsolidatedLinkedVariables, which is equal to (0,0,1,0,2,1,0,3,1,0,0) for our BPD.

Indices 2, 4 and 5 point towards the numbers 1, 2 and 1 respectively. This makes sense, as that behavior is the thing that actually causes amp drain, so 1 corresponds to current shield value, and 2 corresponds to the amp drain of the shield.

It then performs the calculation 1-2, and writes it to its BVARLINK_Output variable Result, which happens to be the same variable as A, or the current shield value.

Finally, these numbers in ConsolidatedLinkedVariables are actually another layer of indices. This time, they point towards VariableData of the BPD.

Concretely, this means that the first (Name=,Type=BVAR_Attribute) you see in there is the current shield value, the second one is the amp drain, and, as you would discover trough more digging, the third is the max shield value.

This was a humongous wall of text, but that is how the dots of a BPD are connected. Once you understand what's going on, this image should help you to keep the things organized:

Relationship between BPD variables

Oddities with LinkIdAndLinkedBehavior

The Two's Compliment numbers that was discussed above makes total sense when looking at OutputLinks.ArrayIndexAndLength or OutputVariables.ArrayIndexAndLength variables. The top sixteen bits is the array index to start from, and the bottom sixteen is how many to process. These variables come out looking very nice in the Two's Compliment Calculator which we've linked to.

The LinkIdAndLinkedBehavior variables inside ConsolidatedOutputLinkData, though, seem much more weird. To go back to the example above, we had a value of -16777214, which already looks strange since it's a negative number. Using the Two's Compliment Calculator, we get -256 and 2. The 2 makes sense, but why would an ID be negative? Computer data doesn't tend to use negative numbers as IDs, after all...

The binary value of -16777214 is: 11111111 00000000 00000000 00000010. That second group of eight bits (the second byte of the value) is all zeroes. Well, it turns out that across all of the BPDs in both Borderlands 2 and TPS, that's true for every single value inside all ConsolidatedOutputLinkData arrays. And in fact, if we decide to interpret the first eight bits as a single "unsigned" value all to itself, the "Link ID" referenced in LinkIdAndLinkedBehavior often makes a lot more sense. Instead of -256, the 11111111 would get interpreted as 255, and many of the other cases throughout the Borderlands data make much more sense as well: there will often be values which range from 0 to about 15 or so.

A full breakdown of the distribution of Link IDs across BL2 and TPS, if we consider the Link ID to be the "top" byte of that value (as opposed to the top two bytes, as the Two's Compliment Calculator does) can be found here.

Lesson Four: How Do We Edit a BPD?

So, you've managed to get this far without being intimidated by a wall of text? Good on ya! Grab a beer or something to celebrate.

Once you understand it till here, it will only get easier.

Next on the agenda is actually modifying a BPD.

Unlike most things in this game, behaviors and BPDs are NOT static. That is, changes you make to them can be overwritten, seemingly at random sometimes.

Remember those Behavior_Delay things that you tried to change once with no luck?

That is because Behavior_Delay uses input variables, and each time the BPD is executed, the value inside the Behavior_Delay is overwritten with this new input.

Things We Definitely Cannot Change

The main thing to keep in mind is that we cannot, with our current understanding of BPDs, modify anything about ConsolidatedVariableLinkData and VariableData.

  • ConsolidatedVariableLinkData is modified by the game each time the BPD is executed, its CachedProperty is updated.
  • We have no clue where the game is pulling the actual values from. By trial and error, and dumping in-game, one can usually piece together what a (Name=,Type=BVAR_Attribute) actually is, like max shield, for example.

Since we don't even know how the game is getting all these values, we can't hope to modify them with our current understanding.

It is probably related to the BVAR_Object and BVARLINK_Context parts in the BPD, or possible it's coming from somewhere outside of the BPD, like maybe LockedSkillDetailStr=<Locked> in GD_Shields.Skills.Impact_Shield_Skill, or who knows where.

What We Can Change

Fortunately, most of the rest of a BPD structure is fair game for editing, though juggling all the various variables, indexes, and two's-compliment numbers can make it a sometimes bewildering practice. Note that all BPD edits must be done via Hotfix. That's actually a boon, in the end, because it allows you to specify the exact value you want to edit rather than having to specify the whole BehaviorSequence at once.

As with regular Borderlands modding (at least in BL2), one limitation is that we can't add elements to an existing array without redefining the whole array. If you wanted to do something like add some new elements to ConsolidatedOutputLinkData, to link Behaviors differently than they already are, you may want to first set up a hotfix which just copies the "stock" array but adds a bunch of empty elements at the end (with their values set to 0), and then use targetted hotfixes to set the values when appropriate. Ideally you could do that alongside comments and folders inside your mod, so that it's clear what various parts of your edits are meant to do. Merely setting the whole array all at once would be a little less clear to anyone else looking at the code (or even to yourself, a day or two after you're done dealing with it).

Unlinking Variables

There are multiple ways we can change things. We'll start with unlinking variables.

A few paragraphs above, Behavior_Delay was used as an example of a Behavior whose value you can't directly alter. The Behavior tends to use input variables, so each time the Behavior is executed, any changes you tried to make earlier will get overwritten. This is controlled by the LinkedVariables.ArrayIndexAndLength property inside the BehaviorData2 array.

Well, we can't change ConsolidatedVariableLinkData or VariableData, but we can alter the LinkedVariables.ArrayIndexAndLength property inside the BehaviorData2 array. So if you wanted to set a static custom delay right on a Behavior_Delay object, just find its entry inside BehaviorData2 and use a hotfix like the following:

set GD_Whatever.Foo:BehaviorProviderDefinition_0 BehaviorSequences[X].BehaviorData2[Y].LinkedVariables.ArrayIndexAndLength 0

Substituting the proper object name, and X and Y for the proper indexes. Then the behavior won't have a linked variable anymore, and you can hardcode whatever you like.

Note that some behaviors may not work properly with their variables de-linked, but it can be useful in a variety of contexts.

Variable Swapping

To make amp shields drain 100% of shield on each amp shot, we could replace the variable that is now using the amp drain variable, by the one that uses the current shield value. The new shield value will then always evaulate to current shield - current shield = 0.

From above, we know that the second operand of the Behavior_SimpleMath uses the variable at index 4 of ConsolidatedLinkedVariables, and we know that the first operand is current shield, which is variable 1.

To realize completely draining amps we would wanna make something like

set GD_Shields.Skills.Impact_Shield_Skill:BehaviorProviderDefinition_0 BehaviorSequences[0].ConsolidatedLinkedVariables[4] 1

Similarly, one can plug in different variables into other behaviors, like in the Behavior_CompareFloat.

If Behavior_CompareFloat does not return true (it checks if current shield == max shield), the behavior sequence is halted. So, if you plug in two of the same variables into the Behavior_CompareFloat, it will always return true, and hence, the BPD will continue execution no matter the current shield value. Notice that, to make continously draining amp shields, more needs to be done outside of the BPD, which we won't go over here.

Modifying delay values in ConsolidatedOutputLinkData

We can alter Behavior_Delay objects by unlinking their variable bindings (or by changing the binding to some other variable in the BPD), but we can also specify our own delays on any behavior link in the BPD, by modifying ConsolidatedOutputLinkData elements.

Each entry of this array has two fields: LinkIdAndLinkedBehavior, which points towards a Behavior object, and ActivateDelay, which is a number of seconds the BPD waits before executing the linked behavior. Since this number must be positive, all we can do is make behaviors execute later.

One could use this to make amp shields drain 1 second after firing, allowing you to get off multiple amp shots, and probably getting multiple drains afterwards, which would look like this

set GD_Shields.Skills.Impact_Shield_Skill:BehaviorProviderDefinition_0 BehaviorSequences[0].ConsolidatedOutputLinkData[3].ActivateDelay 1

This method alone can't be used to remove delays completely, unless there is some variable in the BPD that seems to always be equal to 0. For instance, the Landscaper only has fixed variables at 0.2 seconds, so you can make the pellets explode quickly, but not instantly with this method.

Removing and Adding Behaviors

Next on the agenda is actually rewiring a BPD, removing and adding of behaviors.

We'll start with removing a behavior. Say you have a Behavior_CompareXXX or Behavior_Delay, Behavior_RandomBranch or whatever else is getting in the way of executing your BPD all the way trough.

You'll wanna remove that behavior.

The way we remove the behavior, is not by actually removing the behavior from BehaviorData2, but by redirecting every behavior that links towards this blocking behavior towards the behavior the blocking behavior points to.

For this example, we'll cut out the Behavior_CompareFloat from the sequence, which will effectively achieve the same result as we got by remapping the variables.

First off, we need to know what points to the Behavior_CompareFloat.

As we discovered above, Behavior_CompareFloat is one of the two behaviors following the Behavior_SetShieldTriggeredState, where Behavior_PostAkEvent is the other behavior following Behavior_SetShieldTriggeredState. The goal now becomes to make Behavior_SetShieldTriggeredState point directly to Behavior_PostAkEvent and Behavior_SimpleMath, which is the single behavior following Behavior_CompareFloat.

From above we know that the OutputLinks of the Behavior_SetShieldTriggeredState points towards the two (LinkIdAndLinkedBehavior=-16777214,ActivateDelay=0.000000) and (LinkIdAndLinkedBehavior=-16777213,ActivateDelay=0.000000) entries in ConsolidatedOutputLinkData, which in turn mean linkID -256 (or 255, depending on how you want to interpet Link IDs) with LinkedBehaviors 2 and 3.

We want the linkedBehaviors to now be 1 and 3, since 1 is Behavior_SimpleMath, and 2 is Behavior_CompareFloat. Converting a linkID of -256 and a LinkedBehavior to a single number (via the Two's Compliment Calculator) yields -16777215.

The final code for this rewiring thus becomes:

set GD_Shields.Skills.Impact_Shield_Skill:BehaviorProviderDefinition_0 BehaviorSequences[0].ConsolidatedOutputLinkData[0].LinkIdAndLinkedBehavior -16777215

The procedure for adding behaviors is similar, but, we have far less experience with that, and from what we know from @FromDarkHell and @the_Nocturni, it's much more .... volatile, from the attempts they had with it.

To add a new behavior to the BPD, the first step is to add it to the BehaviorData2 array. This is again done trough hotfix, but you'll need to copy-paste the entire old array, and append the new behaviors to it.

As mentioned above, when adding a new behavior, the cleanest way to do it is to leave LinkedVariables and OutputLinks both being equal to (ArrayIndexAndLength=0), and give them the proper values in a later hotfix. That way your edits can be as small as possible, and organized neatly in your mod, with comments and categories to make it clear what you're changing and where.

For this it is important to realize that, just like regular set commands, the order of hotfixes do matter. Hotfixes are executed in the order they are shown in BLCMM. (top -> down)

This means you can first create a big array, and in a later hotfix, modifify what you just created.

After you have the behavior plugged in, you're gonna need one or more entries in ConsolidatedOutputLinkData that point towards your new behavior. It is usually good practice to give these new links a unique LinkID, and naturally, have the LinkedBehavior be the index of your newly added behavior. Furthermore, if you have a single behavior linking to multiple next behaviors, you'll wanna have all those links have the same LinkID. Something we ignored up till now

After that it's a matter of having an existing behavior in the BPD link towards the new ConsolidatedOutputLinkData entry, and it shouldtm be done.

Lesson Five: Limitations of BPD Editing

As mentioned above near the beginning of Lesson Four, we can't modify anything about ConsolidatedVariableLinkData and VariableData, at least with our current understanding of how BPDs work.

Consequently, when adding behaviors to a BPD, you are inherently limited to behaviors that do not require inputs/outputs, or at least only those inputs/outputs that are already present in the BPD you are changing.

Having said that, there's probably also some cross-DLC mixing issues, like some of our gunsmiths have experienced (the reason why the OM, pimpernel and sandhawk are popular ones to replace).

All in all, with these 3 methods of changing BPD's, we're more or less free to modify a BPD any way we want in it's original sandbox, and if we're lucky we can bring in a new toy, but nothing too spectacular.

Lesson Six: Another Example

As a conclusion to this class, we'll consider a different example: custom delay spikers. We could use the "Unlinking Variables" method to disable the Delay behavior, but in this example we'll instead bypass the Delay behavior entirely, to give an example about how that would work. (This is the technique you'd be using to make more advanced BPD edits.)

You know those spiker pistols, which take 1.8 or so seconds to detonate, meaning that enemies don't die when you want them to?

We can change that with BPD edits.

First off, for those reading alone, the BPD in question is GD_Weap_Pistol.Projectiles.Projectile_Spiker:BehaviorProviderDefinition_0, and it looks like this:

BPD Dump for Spiker pistols

The relevant bit on a graph for the BPD looks like this:

BPD Graph for Spiker pistols

So, the problem in this BPD would be our friend Behavior_Delay. Step one will be to get rid of that one. Behavior_Delay is the 6th element, so it has index 5 in BehaviorData2.

It's output link has ArrayIndexAndLength=65537, which translates to an ArrayIndex of 1 and a length of 1 in ConsolidatedOutputLinkData. Element 1 of ConsolidatedOutputLinkData is (LinkIdAndLinkedBehavior=-16777214,ActivateDelay=0.000000), which is a LinkID of -256 and a LinkedBehavior of 2, which is the ProjectileBehavior_Detonate.

Next we see which of the other behaviors point towards Behavior_Delay, by inspecting all of the elements of ConsolidatedOutputLinkData, and see which have a LinkedBehavior of 5. The ones that have this are those on index 0 and index 2, which are -16777211 and -16777211, which both translate to -256 and 5.

Then, the next question we wanna solve is, which behaviors link to a subarray that contains index 0 or 2? Both of these are only used by a single behavior, which each only link to the delay behavior, namely ProjectileBehavior_Attach_6 and ProjectileBehavior_Attach_7.

These behaviors are on index 4 and 7 of BehaviorData2.

We're now ready to redirect these behaviors directly to ProjectileBehavior_Detonate, by using the OutputLink of Behavior_Delay, which is 65537.

This results in the following code:

set GD_Weap_Pistol.Projectiles.Projectile_Spiker:BehaviorProviderDefinition_0 BehaviorSequences[0].BehaviorData2[4].OutputLinks.ArrayIndexAndLength 65537
set GD_Weap_Pistol.Projectiles.Projectile_Spiker:BehaviorProviderDefinition_0 BehaviorSequences[0].BehaviorData2[7].OutputLinks.ArrayIndexAndLength 65537

Next, we need to add in our own delay, to replace the one we just removed. We do this by modifying the element with index 1 of ConsolidatedOutputLinkData, the one that points to ProjectileBehavior_Detonate and is used by both ProjectileBehavior_Attach now.

set GD_Weap_Pistol.Projectiles.Projectile_Spiker:BehaviorProviderDefinition_0 BehaviorSequences[0].ConsolidatedOutputLinkData[1].ActivateDelay 0.345

And with that, we have custom-delay spikers.

Concluding Notes

Even with all this knowledge & stuff, it's very easy to make a mistake, do something the game doesn't like, or otherwise crash your game.

BPD's are sensitive bastards, so if you're gonna attempt to edit them, don't get mad if your game decides to crash on you, multiple times. Especially, since a core part of it is inherently still just trail and error.

And, that's basically it folks. Nothing too complicated, right?

If you made it this far, good job. If anything is unclear, try reading that part again, or ask a question in the discord.

Clone this wiki locally