-
Notifications
You must be signed in to change notification settings - Fork 27
Let's Make a Mod: Sequence Editing Examples
Sequences tell the game what to do and in what order. We’re going to make a simple mod that adds two Points of Interest (visual markers that Shepard can interact with, seen below in Figure 5), and make them give a small Paragon or Renegade boost. We will then iterate and expand on this, exploring how to sequence in more complex behaviour, some different ways to achieve the same results, and what other applications some of these techniques may have.
This tutorial is not intended as an exhaustive guide to sequencing or the Sequence Editor. Instead this is a collection of options intended to give you a feel for sequence editing, going over a series of exercises that will cover some useful functions. These include:
- Triggering in game events using points of interest, level load or remote events
- Setting and unsetting targetable items
- Setting and incrementing bools and integers
- Using bools and integers to direct sequence outcomes
- Using switches to direct sequence outcomes
- Bools to check whether DLC is installed to assist with compatibility concerns
This tutorial will be made in LE2, and there are differences between the games, but the broad principles should be consistent across the series. This tutorial assumes you already know how to set up a DLC mod and find the highest-mounted version of the file you want to edit, etc.
We will add these to the Normandy’s Comm Room. Open Biod_Nor_104Comm.pcc
. Search for “poi” in the search bar at the top and you will find the Point of Interest that already exists in the Comm Room. This is used to interact with EDI, and sometimes to call The Illusive Man. You will see it is part of the Persistent Level tree – this includes all objects in the level.
Figure 1
As shown in Figure 1, right click on the Point of Interest and choose “clone tree”. Do this twice, because we want to add two new points of interest.
Figure 2
Go to your new Point of Interest, which will be highlighted in yellow until you have saved your file. In the right-hand pane, change the tag to something unique but meaningful. We will use poi_paragon
and poi_renegade
because we want the points of interest to grant paragon and renegade points.
We will not be making use of tags in this tutorial, instead we will be using direct Export numbers. But tags are another way to target objects in sequences, and can be used across different files. It is important to change these tags, even if we will not use them, because they might be used for the object we have cloned, and if our new objects also use them, they could be called by mistake.
Select the Tag
line (red), then type the name into the box at the top of the pane (green). Apply, and you will be asked whether you wish to add a new name to the file. Select Yes (blue).
Repeat this process for the renegade POI.
Make a note of the Export number for your new objects – you will need this later. In Figure 2 you can see that the cloned POI (highlighted in yellow) is Export 321
.
Figure 3
Here is a close-up of the renegade POI properties. If you expand the Location property, you will see where the POI will appear in the level in XYZ coordinates which can be edited.
Kinkojiro’s tutorial on adding a datapad details how to place objects with precision. For the purposes of this tutorial, adding and subtracting 40 from the X value will work to move the two new POIs to each side of the original POI we have cloned.
Next we must change how they are labelled.
Figure 4
Expand the tree for one of your new Points of Interest. There should be one child – the SFXSimpleModule
. See the blue box in Figure 4. This controls the text strings the POI displays.
In the right-hand pane, in green, you will see that I have changed a number. This number refers to a text string in the game’s TLK file. 154268
is the string number for “Paragon”. This is in the base game, so we do not need to add a custom line to the DLC mod’s TLK file (though we will detail this later in the tutorial).
154269
is the vanilla string for “Renegade”.
Figure 5
Figure 5 shows the POIs in the game. However, pressing them currently does nothing. For that, we need a sequence.
Open the file in the Sequence Editor as shown below in Figure 6.
Figure 6
It will look like this:
Figure 7
So what are we looking at? Well, the central area is a visual representation of your sequence(s). On the bottom left, in the blue box, you can see that there is a main sequence which can contain subsequences. You must select one of these sequences to do any work (the main sequence is selected in Figure 7).
In the main sequence those subsequences will appear as a single “Sequence Object”. You can then go to those sequence objects in the tree view to see what they actually contain. It’s useful for keeping complex sequences coherent, or when you have modular sequence elements you want to clone and re-use, but it’s beyond the scope of this tutorial. We will just be adding things to the main sequence. You can move sequence objects about by clicking and dragging to get a better layout. You can also check the “Layout” menu for further options.
Towards the left hand side, there is a vertical yellow bar - this is the Sequence Object Toolbox. The red box at the top highlights where you can expand the toolbox. We will use this a lot, it’s where you generate new sequence objects. To the right, boxed out in green, is a list of all the objects in the sequence we are looking at. And at the bottom, also in green, are the specific properties of the selected sequence object.
Sequences more or less read like a flowchart. There’ll be a start point, and the game follows the links between sequences until it reaches an end point. Many start and end points can exist within a single sequence. For instance, in the Main Sequence in Figure 7, not all items are interconnected.
So. On to the sequence we want to make. We want our sequence to start when we interact with the POI, and to then give us morality points. In Mass Effect, morality points are awarded via a transition that increments the plot integer for Paragon or Renegade points. To understand more about the difference between transitions, conditionals, bools, integers and floats, I recommend 55tumbl’s tutorial on Plot Management. We will not go into these concepts in detail here.
Figure 8
Expand the Sequence Object Toolbox, go to the Events tab, and find SeqEvent_Used
. Double-click on it and it will appear in your sequence space, and in the list of sequence objects on the right. This is shown in Figure 8 outlined in magenta.
Select your new object and its properties will appear in the bottom pane.
Variable links are aligned horizontally at the bottom of the sequence object. As the name suggests, they control variables, either via a connected sequence object, or directly via properties. Input links accept incoming links and are at the left of the object. We have none here because we are starting the series of events. Output links are on the right and link to the next object in the sequence. Here we have two output links – “Used” and “Unused”. We will be using “Used” to indicate that the link should fire when the attached POI is used.
I have expanded the OutputLinks
tree in the properties pane to show you what it looks like, but we do not need to change anything here. The default settings are fine.
We do need to add a new property. Hit the “Add Property” button (outlined in green). This will open a new dialogue box. Double-click the Originator
property and it will appear towards the bottom of the properties pane.
Then we will add another property – a comment. This allows you to add a note to the sequence object that will be displayed above. It has no technical impact but it is very useful for keeping track of things. Again, open the Add Property dialogue box, and this time, add m_aObjComment
(see Figure 9).
Figure 9
See Figure 10 for a screenshot of your SeqEvent_Used
object’s properties, including the two new ones that were added.
Figure 10
Firstly, the Originator
property, outlined in blue. Enter the Export number of the Point of Interest that you want to attach to it here. Remember we noted down that this was 321
? Enter it into this field.
Next, the red box shows the Object Comment. Yours will not have that blue Item 0
line. To get it, highlight the property, then hit “Add Array Element” (the red box at the top). This will add an Item to the Object Comment array, and you can type your comment into it (in the box in outlined in green). Set it, and your comment will now appear above the sequence object. You can see this outlined in green in Figure 11, below.
Figure 11
Okay, so now we have attached the Used event to our POI, but what happens when it is used? Nothing! Because the Used output link goes nowhere!
Go back to the Toolbox. This time you want to choose the Actions tab. Find BioSeqAct_PMExecuteTransition
. Double-click and drag your new item to a sensible place in your workspace, a little to the right of your Used object. Then, drag and drop the black square of the Used output link, and connect it to the In input link on the Transition object.
Figure 12
We need to tell the Transition object which transition to execute. Select the object, then add a property (as we did to the Used object). You want to add the m_nIndex
property. This is a property you will be using a lot. It’s frequently used when checking or setting bools, integers, transitions or conditionals.
The transition to increase paragon points by a small amount is 4365
. See Figure 13 below. You can confirm this transition number and find others in the Asset Database. You must enable Compile Plot Usages (in the Tools menu) when generating your Asset Database to use this feature.
Figure 13
And you’ve done it! You have completed a simple sequence. You use the POI, which then triggers the transition and adds 2 paragon points to the paragon points integer in your save. Pre-existing code in the game will trigger the Paragon or Renegade notification in the lower right corner whenever these integers increase. No additional action is needed for that feature.
However, we’ve only done one of the POIs. We must repeat the process for the Renegade POI. Since we will be copying the sequence almost exactly, instead of adding fresh objects from the toolbox, it is better to save time and clone existing objects.
Figure 14
Right click on the sequence object and choose “Clone Object”. Do this for both the Used and Transition objects. Connect them in the same way. Amend the comments (if you are using them). Change the Originator
in the Used object so that it points to the Renegade POI. Change the m_nIndex
in the Transition object so that it grants a small renegade boost.
Boot up your game to see it in action. A video can be seen here.
How can we develop this further? What if something else happened after the transition. Perhaps the Points of Interest should toggle each other on and off, so that you can only use them to grant yourself a balanced morality boost?
We will show and hide the Points of Interest by using an object called SFXSeqAct_SetTargetable
.
SFXSeqAct_SetTargetable
is specifically for items that are, well, targetable. That you can interact with. If you want to hide a mesh object you would need to useSeqAct_ToggleHidden
instead, which functions differently.
Add four SetTargetable objects to your sequence (see Figure 15).
Figure 15
We need one to show and one to hide each of the two POIs. We then use the variable links to tell the object whether it’s allowing or blocking targeting and under which circumstances. We will connect the variable links to bools to feed a TRUE
or FALSE
value into the variable.
Figure 16
Double-click the SeqVar_Bool
object (blue, left-hand side) on the Variables tab to add bool objects. It will add a bool with a FALSE
value. Attach this to the CombatTargetable
and the CombatTargetableOverride
variables. Drag and drop the square icons beneath the variable onto the bool objects.
TargetableOverride
should be set to TRUE
. To make a bool TRUE
instead of FALSE
, select it, and in the properties pane, you should change bValue
from 0
to 1
. This is seen highlighted in blue in the lower pane.
As you can see, you can connect more than one variable to the same bool object, as long as you are comfortable that they will always be sharing the same value.
Finally, the Targetable
variable link. This is what either allows targeting (the POI is visible) or does not allow targeting (the POI is hidden). So connect this to a TRUE
bool for one object and a FALSE
for the other.
SeqVar_StoryManagerBool
(see Figure 16 outlined in magenta) should not be used here. The StoryManager objects refer to specific bools, integers, floats, etc., that are kept in your save file. We will use them later in the tutorial. The regular bool/int/float objects are temporary values used within the sequence itself. They have no index number, just a value.
Figure 17
Next, we need to tell the objects what they are setting as targetable. Add a SeqVar_Object
(visible in green in Figures 16 and 17). As shown in the properties tab, the export for the correct POI should be set.
Repeat this process for the other POI as well.
Now, we need to link the targetable objects to the rest of the sequence. By default, one POI should be visible and one should be hidden. Currently our sequence is triggered by using the POI. But if we want to hide one of the POIs by default, we will need a different trigger – such as the level itself loading.
Figure 18
Add a SeqEvent_LevelLoaded
object from the Events tab (green). As you can see, from the output link labelled “Loaded and Visible”, we have created two links. One shows the Renegade POI, the other hides the Paragon POI.
We also need to link up the transitions, so that after using a visible POI, we are granted morality points (via the transition) and then change the visibility of the POIs. The blue and red arrows show these links. The Paragon POI will hide the Paragon POI and show the Renegade POI. The Renegade POI will hide the Renegade POI and show the Paragon POI.
Here is a video showing the sequence in action.
So far we have triggered in-game events via using POIs, or at level load time. We have also used direct links between events, so that when one object completes its function, it leads directly to another.
An alternative to this is to use Remove Events. A remote event in one sequence can begin another sequence elsewhere. This is most often useful because it can be used to across different subsequences or even different files. BioD_Nor.pcc
can fire off a remote event that causes a sequence in BioD_Nor_103CIC.pcc
, for instance, provided both files are loaded into memory at the time.
In this tutorial, we will use remote events as a direct replacement for the links between the transitions and the SetTargetable objects. The end result will be identical in terms of player experience, but instead of drawing a links between them, the transition will trigger a remote event. That remote event will them either show or hide the POI.
Figure 19
This is a good opportunity to learn a little more about how links are displayed in the properties pane. The green square shows the transition object which we have selected. Its properties are displayed at the bottom. It has output links, and you must expand several layers to see that the first is linked to Export 330
(blue). You can directly change this number if you want to connect it to another object instead. If you want to remove the link, do not change this number to zero. The output link will disappear in the sequence editor, but it will cause a crash if it’s triggered in the game.
Instead, to remove a link, highlight the link and select “Remove Array Element” (both boxed out in red).
Remove both output links from both Transition objects so that there is no connection between them and the SetTargetable objects.
Figure 20
Open the Actions tab in the toolbox, and add two SeqAct_ActivateRemoteEvent
objects. Attach these to the transitions. See the green boxes in Figure 20. Add an EventName
property to each ActivateRemoteEvent object and give it a unique name. We will be using Show_Renegade
and Show_Paragon
. See the blue boxes in Figure 20.
Figure 21
Now we need to add the Remote Events themselves. From the Events in the toolbox, add two SeqEvent_RemoteEvents
(green, Figure 21). Add the EventName
properties and use the same unique names chosen in the previous step (red, Figure 21).
You can see the unique names in the title bars of the objects (blue/red overlay in Figure 21). So to visually follow the sequence, the transition towards the top left leads into an object that activates the Show_Paragon
remote event (a rectangle). This causes the actual Show_Paragon
remote event (a diamond between two bars) to fire off its links. Those links are connected the same way we previously connected the transitions – i.e. they show/hide alternate POIs.
Obviously in this specific circumstance, there’s no reason not to directly connect the transitions to the SetTargetable objects. Within the same sequence there’s not usually a reason to use this technique, we’re just doing it here to show how it works. It’s one more useful way to start a chain of events.
Okay, so now we want to ship our mod to other people, so that they can also enjoy the wonders of increasing their morality at will, in tiny equal boosts. But as considerate modders, we want to think about how to approach compatibility. Let’s look at a way to gate sequences based on whether your DLC is installed at the time the sequence runs.
That might sound counter intuitive – if the sequence is part of the DLC, surely the DLC itself would be installed? Well, yes, unless someone else wants to make a patch for your mod and needs this file to do it. If all the sequences checked whether your mod was installed and only ran if so, then anyone else could make their own changes on top of yours, and ship that file in their mod. If your mod was not installed, the sequences wouldn’t run. If it was, they would. The mods would be compatible without a patch! It is not always practical to gate sequences like this, and it will not solve all problems, but it is often worth considering.
First, to create this magical “is the mod installed right now at this very moment?” bool, we need to add a BioGame.ini file to the DLC mod. For LE2, this is simple, and you can simply create a text document, name it BioGame.ini, and enter the text shown in Figure 22, below. I’ve highlighted the bool in yellow. This needs to be your own unique choice, not in use by the vanilla game or any other mod. We are using 8742
.
Figure 22
The process is nearly identical for LE3, but you will need to decompile your mod’s coalesced, add the text to the correct file, then recompile it. You should also check the syntax. All official DLC mods have a bool like this, so if you decompile one of their coalesced files and search for “TimedPlotUnlock” you will find what you need to add to your mod’s coalesced file. The option does not exist for LE1.
Figure 23
Our sequence hides the Paragon POI by default, so we only need to decided whether or not to hide the Renegade POI. Add a BioSeqAct_PMCheckState
object from the Actions tab in the toolbox. This needs to intercept the link that currently goes between the LevelLoaded object and the SetTargetable that shows the Renegade POI. See the green box in Figure 23. It has been dragged to a sensible location for visual clarity, but the links are not yet in place.
As discussed previously, we can select the LevelLoaded object, expand the output links, and change the LinkedOp
. It currently points to 331
(red overlay). We need to change this to 350
(in blue – the object we want it to point to).
The
InputLinkIdx
refers to which input link it connects to. Many objects have only a single input link, but this is not always the case. If an object had more than one, the top input link would be ID 0, the second ID 1, etc.
We then need to add the m_nIndex
property to the PMCheckState object and set this to the bool you defined in BioGame.ini. See green in Figure 24.
Figure 24
Next, hook up the output links. If the bool returns TRUE
, then the DLC mod is installed and the link should lead to the object that shows the Renegade POI. If the bool returns FALSE
, the DLC is not installed and it should go to the object that hides the Renegade POI. The Paragon POI is hidden by default and with both hidden, neither can be triggered to appear. The file would function as a vanilla file unless bool 8742
started returning TRUE
.
Let’s try one final experiment. What if instead of granting either paragon or renegade points, we let the Paragon POI grant different boost levels (there are four standard transitions in the base game, granting small, medium, large and huge boosts). What if we repurposed the other POI so that it toggled between these options?
First, we’d need to re-label the POI. Change it so that it says “Toggle Paragon Boost” for instance, instead of “Renegade”. That text string doesn’t exist in the vanilla game, so we need to add it as a custom string to our DLC mod’s TLK file. This will have been generated automatically by the Mod Manager's DLC Starter Kit generation tool.
Best practice is adding the TLK to all localisations (languages), even if you simply add the English text to them. This means that users with different localisations will not get broken text strings, even if they are provided with English language strings instead. We’re just going to look at the English (or INT) TLK. The process for the others is identical.
Figure 25
Open the the TLK in the TLK editor, and Add String (green). Enter the String ID, which should be one less than the lowest number (which will be your mod’s name).
Figure 26
Enter your new text string, and then hit the save button in the lower right corner. You must also go to the File menu and choose to save the entire file. Do both things.
Figure 27
You can now return to the Package Editor, and – where previously we set the string to display “Renegade” (way back in Figure 4) – you should now enter your new custom TLK string. This may show No Data
if the package editor doesn’t have your DLC’s custom TLK loaded correctly, but don’t worry, it will show correctly in game.
Now we can start building our new sequence. Again, we start with a Used object, with the Originator
property set to the POI for granting paragon points.
Figure 28
Attach the Used output link to a SeqAct_Switch
object (accessible under the Actions tab in the toolbox).
Add a BioSeqVar_StoryManagerInt
, and attach this to the Index
variable link. As we discussed previously, a StoryManagerInt is an integer that will be saved in a save file. It means the choice will persist beyond the boundaries of this particular sequence – you can exit and re-enter the game and the choice will be remembered. Add an m_nIndex
property and enter a unique, unused number. We will use 8744
. This is shown boxed out in blue, in Figure 28.
By default the Switch object will have only one output link titled “Link 1”. But we need 4 output links, because we want to cycle between the four standard boost levels. To do this, in the properties tab, select the OutputLinks
and then use the Add Array Element option. These are outlined in green in Figure 28. Each time you add an array element it will clone the last output link. Do this until you have four output links (which will all read Link 1 – shown in magenta).
Figure 29
Next you will need to rename the output links so that they all have unique numbers – Rename them to Links 1 – 4, as shown in Figure 29. Expand each OutputLink
in the properties pane individually and type the correct name into the LinkDesc
line.
This setup means that the Switch object will use the integer as an index to decide which output link to use. If the integer returns 2
, Link 2 will be used. If it returns 4
then Link 4, etc.
Figure 30
By default, a Switch object will increment the attached integer by 1
when it is used, meaning it would cycle through the links. We do not want it to do this, so we need to add the IncrementAmount
property, and set this to 0
(which is the default when you add it). This is shown in green in Figure 30.
Figure 31
Here is what your sequence should look like. The Small Paragon Boost transition is set up exactly as the one in Figure 12. The rest were cloned, and had the object comments and m_nIndex
properties updated to reference the other vanilla transitions used to grant paragon points.
Figure 32
When you use a new bool or integer, it doesn’t need to be added to any master list of usable bools or integers. If it doesn’t already exist in your save file, the act of setting it will create the entry in the save. If you check a bool that has never been set at any point, it will return FALSE
. Unused integers return as 0
. However, using unset integers can lead to unstable results. Certainly this is a bad idea with Switch objects. It is best to “initialize” – or set – the integer first. There are several ways and places this can be done, but Figure 32 presents a possible solution.
You have some experience with sequencing now, so hopefully it will be easy for you to follow the items added between the Used object and the Switch object. Now, the first thing that happens after using the POI is a SeqCond_CompareInt
object (from the Conditionals tab in the toolbox – displayed in magenta). As it sounds, this will compare two different integers (attached via the variable links) and then provides a number of output links which will fire depending on Int A’s relationship to Int B.
We are comparing Int 8744
(a StoryManagerInt which is kept in a save file), with 0
(in green – a simple SeqVar_Int
that exists to store a numerical value within this sequence).
If Int 8744
is greater than 0
that means it’s already been set by something, and the sequence proceeds directly to the Switch object. If its equal or less than 0
, then it needs to be initialised, and we should set it to 1
. This is done with a SeqAct_SetInt
object (from the Actions tab). The Target
variable link should point at the Integer we want to set (in this case 8744
) and the Value
variable should contain the value we wish to set it to (in this case 1
).
Essentially this sequence checks whether Int 8744
is 1
or greater, and if it’s not, it sets it to 1
.
Great! But currently we have no way to set Integer 8744
to anything other than 1
. We can’t dynamically determine which transition to execute. We need a way to use the other POI (which we changed to display the text “Toggle Paragon Boost” in Figures 25 and 26) to change Integer 8744
.
Figure 33
Here is a sequence that will make the other POI increase the value of Integer 8744 by 1
each time you use it, until it hits 4
, at which point the next use will reset it to 1
and begin the loop again.
Once again, it starts with a Used object, with the correct POI set in the Originator
property. This feeds into a CompareInt object which will check whether the 8744
is equal to or greater than 4
. If so, it goes to a SetInt object which will set it to 1
.
If the value is 3
or less, it goes to a BioSeqAct_ScalarMathUnit
object (from the Actions tab). This lets you do basic math operations. By default it will add the values of the X
and Y
variable links, then write the value to variable Z
. You can add the Operation
property to change this to subtracting, multiplying, etc., but we are using the default. It adds X
(Integer 8744
) to Y
(1
) then writes that to Z
(also Integer 8744
, updating the value). Basically we’re adding 1
to Integer 8744
.
Another way to do this would be to create a custom conditional that returns true if
8744
is equal or greater than4
. We could check that conditional and ifTRUE
, we’d go to a custom transition which resets8744
to1
. IfFALSE
, we’d go to a different custom transition that would increment8744
by1
. This would be a simpler sequence, but setting up custom conditionals and transitions requires a Startup file for your DLC, and is beyond the scope of this tutorial.Here is a tutorial that covers LE2 startup files and creating custom conditionals.
LE3 startup files are created in a similar way but would only be used for the transitions. LE3 conditionals are stored in a separate file, and this tutorial – while designed for stores – covers creating and using custom conditionals.
LE1 also requires custom additional files, and no tutorials currently exist, although the LE1 Community Patch provides an example of implementation.
And now we’re done! We have one POI which cycles an integer on a loop from 1
to 4
, and another which uses that integer to trigger one of 4 transitions. We can now cycle through small, medium, large and huge paragon point boosts as shown in this video.