-
Notifications
You must be signed in to change notification settings - Fork 27
Introduction to Plot Management
Any mod that aims at adding or modifying things that depend on other things that happened somewhere else will at some point have to deal with plot variables. We will go over Plot variables, Conditionals and Transitions, and have a look at some ways the games use to access or modify plot variables.
- Plot variables are global variables that are always accessible by the game.
- Plot variables are systematically stored in all saved games, although some of the vanilla ones may be stored in the player profile.
- Plot variables are typically of type
Bool
orInt
, but can also beFloat
. - Plot variables are referred to via their index (a positive integer). Plot variables of different types can use the same index, e.g.
Plot Bool 1234
andPlot Int 1234
are distinct. - Plot variables are not declared anywhere, but start to "exist" once they are assigned a value different from the default. By default, any plot
Bool
is FALSE, and any PlotInt
orFloat
is equal to 0. - In the game files, plot variables of type
Bool
are often referred to asState
.
Plot variables are used for a large number of different things in the games. Notably to record the advancement of missions and assignments, and all story-related choices made by the player. But also for many other things, like deciding which items are available in a shop, which codex entries are available, the state of ongoing or past dialogs, etc. Some plot variables are read in your save file when importing into the next game.
Of course, figuring out which plot variable corresponds to what is generally quite useful, but not always trivial. One of the utility tools of ME3Explorer is the Plot Database. This is a manually created list of known plot variables in all 3 games. In the figure below, we can see that Plot Bool 757
will be TRUE if Legion's loyalty mission was concluded with the destruction of the Heretics, while Plot Bool 759
will be TRUE instead if the Heretics were rewritten. We can also see that, for the vast majority of players, Plot Int 485-487
end up taking the value of 3 at some point or another. The Plot Database is a good starting point, but it should be clear that this list is very far from being exhaustive. One should also be aware that sometimes multiple plot variables are used for the same thing.
Since plot variables are used for many different things in the games, there are many types of objects that include a built-in system to check and/or modify plot variables. How it works precisely depends on the type of object. Very often, these make use of Conditionals and Transitions.
- Conditionals are functions that return TRUE or FALSE, depending on a sometimes complex combination of plot variables.
- They can sometimes take an additional input parameter (or argument), of integer type, for example to check whether
Plot Int 1234
is larger than a certain value. - In some cases, conditional functions may also call other variables than the typical Plot Bools or Ints.
- Conditionals are referred to via their index (a positive integer), which should not be confused with the index of the related plot variable(s).
All conditional functions for the main game (excluding those added by DLCs) can be found in the following files.
Mass Effect 1: PlotManager.u
Mass Effect 2: PlotManager.pcc
Mass Effect 3: Conditionals.cnd
In ME1 and ME2, those files can be opened with Package Editor. Each conditional is named with the letter F
followed by its index, and will be found under the group name bioautoconditionals
. In ME3, the file should be opened with Conditionals Editor.
Examples
In the figure below, we see a part of the definition of a planet where a small N7 assignment takes place in ME2. This object has a property PlanetLandCondition
with the value of 1237
. Hence, Shepard will be able to land on that planet if the Conditional 1237 returns TRUE.
Looking at the corresponding conditional function F1237
in Package Editor, we see that it will return TRUE if Plot Bool 4419
is TRUE (the assignment is available), AND Plot Bool 2738
is FALSE (the assignment has not yet been completed).
In this other example, the conditional function F63
does take an input parameter (or argument). If the Conditional is called with an argument of 0
, it will return TRUE if Plot Bool 21
is TRUE, but if the Conditional is called with an argument of 1
, it will return TRUE if Plot Bool 22
is TRUE, etc.
Things looks a bit different in the Conditionals Editor for ME3, but the principle is the same. The conditional function 1397
will return TRUE if Plot Int 10178
is not equal to 0 AND Plot Bool 21118
is FALSE.
- Transitions are sets of rules that define modifications of the values of plot variables.
- A transition can affect one or several distinct plot variables.
- They can sometimes take an additional input parameter (or argument), of integer type, for example to increment
Plot Int 1234
by a certain amount. - Transitions are referred to via their index (a positive integer), which should not be confused with the index of the related plot variable(s) or conditionals.
All transitions for the main game (excluding those added by DLCs) can be found in the following files. The transitions and other plot-related elements contained in those files can be looked at with the Plot Editor. Transitions are found under "State Event Map" tab.
Mass Effect 1: Packages\PlotManagerAuto.upk
Mass Effect 2: Startup_INT.pcc
Mass Effect 3: SFXGameInfoSP_SF.pcc
Note that these files contain other interesting data that can be viewed with the Plot Editor. In particular, you will find the definition of all Quests, with their various Goals and Tasks and associated plot variables. But this is quite an intricate system, so it will have to fall outside the scope of this tutorial.
Most transitions use pretty basic rules.
- For a
Plot Bool
, the transition can either set it to a value defined in the transition (TRUE or FALSE), or accept an input parameter (argument) so that the correponding plot bool is set to TRUE or FALSE depending on the value of the parameter (note the parameter is an integer, so 0 will set the plot Bool to FALSE, and 1 to TRUE). - For a
Plot Int
, the transition can either set it to a certain value or increment it by a certain value. Either way, the (increment) value can be set directly in the transition itself, or the transition can accept an input parameter (argument) which will define the output or increment value.
There are some more advanced uses of Transitions, but those are very rare in the games, except for Substates:
- A
Substate
transition is identical to a regular transition on aPlot Bool
, except that the affectedPlot Bool
is given a disfunctional family: one overprotective parent bool, and one or several degenerate siblings. The state of the Parent depends on the states of the children plot variables. We will see an example later.
Examples
In ME1, some stores sell special items like Grenade/Medigel upgrades, or Manufacturer licenses. These items are considered 'Plot Items' and are defined in a specific table. In the figure below, we see that one of the properties of the "Medigel Upgrade V" is c_transition
and has a value of 5622
.
Looking at the transition (aka State Event) 5622
in Plot Editor, we see that it affects two plot variables. It increments the Plot Int 82
by 1 (Increment
is checked, Use Param
is not, so New Value
defines the increment amount), increasing the max number of Medigel doses Shepard can carry. And it sets Plot Bool 6987
to TRUE, indicating that this upgrade has been bought and should no longer be available in store.
For another example, let's look at the plot variables that store the conclusion of Legion's loyalty mission in ME2. We saw previously that either Plot Bool 757
or Plot Bool 759
is set to TRUE if the heretics are destroyed or rewritten, respectively. Using the search function under the "Find Usages" tab in Plot Editor, we see that Plot Bool 757
is affected by only one transition (793):
The transition 793 is a Substate
transition. It works exactly the same as a regular Plot Bool
transition: Plot Bool 757
is set to TRUE. However, the form contains 3 additional fields: Parent Index
, Parent Type Or
, and Sibling Indices
. The state of the Parent Bool will be updated depending on the new state of the target bool (757) and its siblings (here: 759). Parent Type Or
being checked means that at least one of the children bools should be TRUE for the parent to be TRUE (otherwise, all children must be TRUE for the parent to be TRUE). In this case, since Plot Bools 757 and 759
correspond to two mutually exclusive ways of concluding a mission, we can deduce that Plot Bool 756
means the mission has been completed, no matter the outcome.
Importantly, we don't talk about Plot Bool 758
. We never talk about Plot Bool 758
!
There are many aspects of the games that require accessing/modifying plot variables, and thus the games have many ways to do so. We saw in the previous examples that several classes/objects have built-in properties to check plot variables (e.g. the PlanetLandCondition
) or to modify them (e.g. the c_transition
property in the PlotItems
table). I will not try to be exhaustive, but it should now be more easy figure it out, knowing what things like State
, Conditional
, or Transition
actually mean. Sometimes, classes include functions that directly access plot variables in their own custom ways, like in the example below. In consequence, tracking all things in the game that access or modify a given plot variable can be very difficult.
I will go in some more detail about how two important systems in the games (dialogs and sequences) can work with plot variables.
In Dialog Editor, a dialog is visualized as a series of boxes (or nodes) representing consecutive replies, alternatively from the player and one or more NPCs. Given the branching nature of dialogs in the games, thoses boxes can often have multiple output links, corresponding to different replies available to Shepard, or different possible reactions of the NPCs. To make these options depend on previous events, dialogs require checking plot variables. And to influence future events (e.g. unlocking a quest) dialogs require modifying plot variables.
The example below corresponds to a part of the dialog during the conflict between Legion and Tali on the Normandy in ME2. The bickering between the two squadmates ends on node E8
with a line from Legion, and Shepard has then 4 different reply options. The global view of the dialog on the left side panel already shows whether a node is assigned a Conditional and/or a Transition. The options to Charm (R8) or Intimidate (R11) come with both a Conditional (i.e. these options will only be available under certain plot-related conditions) and a Transition (i.e. choosing these options will affect some plot variables). Note how these options also automatically trigger other Transitions (4367 or 4371) via the next dialog node. The options to support Legion (R17), or to support Tali (R21) come with a Transition but no Conditional, meaning that they will always be available.
After clicking on a dialog node (here: R11), the right side panel of the figure above shows the definition of the Plot Check and Plot Transition, which is mostly straigthforward. Both the Conditional and the Transition are called by their respective index, and can be given an optional input parameter. If the Conditional/Transition does not take an input parameter, this value will usually be set to -1
here (although, to be sure, it is better to actually have a look at what the Conditional/Transition does). Note that the Plot Check can also be done directly on a Plot Bool
instead of a Conditional. In that case, the index of the Plot Bool
itself should be given, and the Conditional Parameter must be set to 0
or 1
, if the dialog option should be available when the Plot Bool
is FALSE or TRUE, respectively (thanks b.).
We can look at Conditional 408
, to see what is the requirement for the Intimidate option to be available. The function does not depend on an Argument, and will return true if the IntimidateSkill (related to the Renegade score) is larger than, or equal to, Plot Int 592
, which was defined previously somewhere else and corresponds to the threshold value to pass a very difficult Renegade check. Note that this example also shows how Conditionals can sometimes call other variables than the typical Global/Plot ones.
I will let the reader try figure out what the Transitions 1471 (support Legion), 1472 (support Tali), and 1473 (Charm/Intimidate), actually do and why/how they differ.
Sequences control a lot of the dynamic behavior of the various game objects, and therefore frequently interact with plot variables. Among the building blocks from which Sequences are created, several are directly related to plot management. We can find all these building blocks in the left sidebar of Sequence Editor:
The names of the available actions are self-explanatory:
-
BioSeqAct_PMCheckConditional
will follow the output "True" if the conditional function (referred to via its index in the propertym_nIndex
) returns TRUE, and follow the output "False" otherwise. -
BioSeqAct_PMCheckState
will follow the output "True" if thePlot Bool
(referred to via its index in the propertym_nIndex
) is TRUE, and follow the output "False" otherwise. -
BioSeqAct_PMExecuteConsequence
: I didn't mention Consequences in this tutorial as they usually don't interact with plot variables. The Consequence system is mainly used in ME1 to award XP, credits, etc (for finishing a quest or doing certain actions), but afaik not in the other games. -
BioSeqAct_PMExecuteTransition
: Executes the transition (referred to via its index in the propertym_nIndex
).
Note that all these plot-related sequence objects have the property m_nIndex
, which is used to define the index of the plot variable, or conditional, or transition, depending on the type of object.
In the example below, the Transition 789
will be triggered (with a parameter/argument of 2), right before finishing the sequence. But only if Plot Bool 123
is TRUE, and Conditional 456
returns TRUE as well.
Sequences also give the possibility to use plot variables directly, via the sequence variables BioSeqVar_StoryManagerBool
(and -Int and -Float). The sequence variables also refer to the proper Plot variables via their m_nIndex
property. But other than that, they behave as regular Bool, Int, etc variables in the sequence, and can be linked as variable input/output to any appropriate sequence object.
For example, in the figure below, the sequence checks that Plot Int 12345
is at least equal to 1, and otherwise sets that Plot Int to 1. Then it checks that the same Plot Int 12345
is smaller or equal to 5, and otherwise sets that Plot Int to 5. Finally the Plot Int 12345
is given as Input to a SeqAct_Switch
, so as to continue the sequence in 5 different possible ways, depending on the value of the Plot Int (which is now necessarily between 1 and 5).