Dialogue Modding – pt 6 – Plots

Hmmm. So. Plots are extremely important, not just in dialogue, but to anything that happens in the game. They’re the backbone of the game, the structure that connects everything together, and are used heavily in both entry level and advanced modding. And yet there’s a reason I’ve been putting off dealing with them until last.

This is not exactly entry level stuff.

The entry level stuff

Plots have been covered briefly in parts one and two, but only as far as necessary to understand the roles of actions and conditions in dialogue. It’s time to revisit our basic understanding of plots as their own entity.

Each plot, as an individual unit, is really just a container for plot flags along with a little extra information. This information will always include:

  • A GUID by which each plot is identified
  • A plot script, for all the unique behavior the plot may have to handle
  • A plot entry type
    • 1 = Plot
      • This is the default type when no special UI behavior is required
    • 2 = Story So Far
      • Story So Far plots update the text that appears when loading a save game
    • 3 = Codex: Creatures
    • 4 = Codex: Items
    • 5 = Codex: Places
    • 6 = Codex: Lore
    • 7 = Codex: Characters
    • 8 = Codex: Letters and Notes
    • 11 = Codex: Art of War
    • 12 = Letter
      • I believe these are letters delivered to Hawke’s desk, as opposed to the codex category
    • 14 = Plot Group
      • These appear as category headers in the journal, i.e. Main Plot, Companions, Rumors, etc.

Plots may also use the following fields:

  • The plot name, which is a string reference for the title of the in-game journal or codex category, journal or codex entry, job board posting, or letter
  • The parent plot. If the parent plot is a plot group, the journal entry for this plot will be found under that heading. If the parent plot is a quest, this plot can display as a subquest that must be checked off individually before the full quest can be completed

What makes editing plots difficult in DA2 compared to Origins is that in some cases the internal structure of plot files has been radically changed from Origins. Two notable difficulties:

  • The plain text name of each plot flag is exported to the GFF in Origins plots. In DA2, this is missing, which makes it impossible to guess the purpose of each plot flag by just looking at the file.
  • When DA2 plots were restructured, rather than adding new GFF fields (as they did in conversations), they reused existing fields. This means the labels when viewed in pyGFF are incorrect. They should be labeled as follows:
- 0                                     PLOT
    3           TEMPLATERESREF          ECString        seb221pt_repentance
    13000       PLOTGUID                ECString        2AA50907F15F47A094C9835F9A5195F3
    13001       PLOT_NAME               TlkString       6185016, 0
    13002       PLOT_SCRIPT             ECString        seb221pt_repentance
    13003                               INT32           1
    13004                               ECString        ?
    13005       PLOT_PARENT_PLOT        ECString        dae00pt_followers
    13006       PLOT_ENTRYTYPE          INT32           1
    13007                               UINT8           0
    13008       PLOT_PARENT_GUID        ECString        380459474ED24FFCB9A2BC1EB47E34AD
    13050                               UINT32          0
    13051                               UINT32          0
    13052                               UINT32          0
    13053                               UINT32          0

Types of plot flags

The truly important part of plots are the flags. Plot flags are written to the save game with the GUID identifying the plot, an integer identifying the flag, and a 1 if true. If the plot flag is saved with a 0 or is not in the save at all, it is false.

Standard plot flags (which start at 0 and potentially cap out at 128) can be actively set by the game to be true (or set back to false). Standard plot flags are often set within a conversation, particularly at the end of a conversation branch to indicate that stage of the conversation has been played and to trigger whatever behavior is required to progress the quest. These behaviors are coded in the plot script, and can cover a vast array of functions.

Defined plot flags (which start at 256 and count up from there) cannot be actively set. Instead, they are scripted if-statements that will return true or false depending on other factors. While standard flags can be used as either conditions or actions, defined flags can only be used as conditions.

To give an example, gen00pt_party has a number of standard flags that can be used in conversations to add or remove various followers to and from the party (using the SetFollowerState() script function), and a number of defined flags that check to see if each follower is in the active party or not (using the GetFollowerState() script function). These defined flags are checked whenever party members can interrupt with dialogue reacting to the current conversation, or whenever they can add or lose approval, so that these things only happen if that follower is actually actually present.

File-defined plot flags

Dragon Age 2 also added additional fields within the plot file that can define plot flags without the use of scripting. Both script-defined and file-defined plot flags can be used within the same plot, though not for the same flag. These new fields can only 1) check if other plot flags are true or false and 2) combine them with simple boolean logic.

Logic has never been my strongest suit so please bear with me. I’ll try to break this down as best I can.

File-defined plot flags consist of two parts: 1) a list of zero-indexed plot flags, and 2) a list of logical functions. The first number in a logical function defines what type of function it is.

  • 0 is a plot flag
  • 1 is the AND operator
  • 2 is the OR operator
  • 3 is the NOT operator

The second number in a logical function either defines which plot flag to check from the first list, or how many arguments that function accepts.

So if the logic list looks like the following:

- 13501                          [COMP]                              
  - 0                            COMP        
      13200    PLOT_FLAG_ID      UINT16      256
    - 13502                      [PFLG]      (2 items)
      + 0                        PFLG        478CEED95AD54937ACD9594206E57642, 261
      + 1                        PFLG        96F97AD3A07A4A3794B0089BF8D0C302, 14
    - 13520                      [CLAS]      (3 items)
      + 0                        CLAS        1, 2  (AND)
      + 1                        CLAS        0, 0  (PFLG)
      + 2                        CLAS        0, 1  (PFLG)

That AND function (1) accepts two arguments, so it will return true if the two plot flags on the preceding list both return true.

If the logic list instead looks like the following:

    - 13520                      [CLAS]      (4 items)
      + 0                        CLAS        2, 3  (OR)
      + 1                        CLAS        0, 0  (PFLG)
      + 2                        CLAS        0, 1  (PFLG)
      + 3                        CLAS        0, 2  (PFLG)

That OR function (2) accepts three arguments, so it will return true if any of the three plot flags return true.

These have been simple examples, but they can get far more complex when nesting is involved. Here’s an example of that:

- 13501                          [COMP]
  - 0                            COMP        
      13200    PLOT_FLAG_ID      261
    - 13502                      [PFLG]      (4 items)
      + 0                        PFLG        18854EB329424101AAD5D80BD648D445, 268
      + 1                        PFLG        96F97AD3A07A4A3794B0089BF8D0C302, 19
      + 2                        PFLG        96F97AD3A07A4A3794B0089BF8D0C302, 15
      + 3                        PFLG        96F97AD3A07A4A3794B0089BF8D0C302, 16
    - 13520                      [CLAS]      (8 items)
      + 0                        CLAS        1, 2  (AND)
      + 1                        CLAS        0, 0  (PFLG)
      + 2                        CLAS        2, 2  (OR)
      + 3                        CLAS        3, 1  (NOT)
      + 4                        CLAS        0, 1  (PFLG)
      + 5                        CLAS        1, 2  (AND)
      + 6                        CLAS        0, 2  (PFLG)
      + 7                        CLAS        0, 3  (PFLG)

The first AND function accepts two arguments, but the second argument is an OR function, which itself accepts two arguments. These are the NOT function (3, which takes the next argument) and the following AND function (which takes the last two)

Here’s one way to think of the nesting:

AND:
0
OR:
NOT:
1
AND:
2
3

And here’s how it would look if it were instead defined in dascript:

if( FLAG_0 == TRUE
    &&( FLAG_1 == FALSE 
        ||( FLAG_2 == TRUE && FLAG_3 == TRUE )))
    return TRUE;

if( FLAG_0 && ( !FLAG_1 || ( FLAG_2 && FLAG_3 )))
    return TRUE;

Plot assist

Standard flags can also use special fields used to automate some common behaviors. While plot assist technically refers only to the quest markers, I’m going to generalize the term to refer to the entire collection of custom fields available to standard flags.

- 13300                                           [SIMP]
  - 0                                             SIMP
      13009                                       UINT8       0
      13200       PLOT_FLAG_ID                    UINT8       5
      13301       PLOT_FLAG_ENDS_PLOT             UINT8       1
      13302       PLOT_FLAG_REWARD                INT32       800220
      13303       PLOT_FLAG_JOURNAL               TlkString   6185022, 0
      13304       PLOT_FLAG_AREA_LOCATION_TAG     ECString    dae221ar_chantry
      13305                                       ECString    ?
      13306                                       [PFLG]
      - 0                                         PFLG
          13100                                   ECString    E952278C059E408E88278E00F1B3C36E
          13101                                   UINT16      0
      13320       PLOTASSIST_LIST                 [ASIS]
      - 0                                         ASIS
          13321   PLOTASSIST_TAG                  ECString    seb221ip_at_hightown
          13322   PLOTASSIST_STATUS               UINT8       2
  • Plot flag ID is an obvious field
  • Ends plot: If set to 1, setting this plot flag will mark the quest or subquest complete and update the journal entry accordingly.
  • Reward: An ID for an entry in rewards.gda to be granted when this plot flag is set
  • Journal: A string ID for the description to be shown in the journal or codex. If this is a journal entry, the most recent flag set will replace any existing text in the  journal entry. If this is a codex entry, the most recent flag set will add a new paragraph at the end of the current codex entry if it exists. These paragraphs can be removed by setting the flag false.
  • Area location tag: If this is set, it will show the quest active at this location on the travel map.
  • Plot flag list: I’m 90% sure when this flag is set true, any flags in this list will also be set, but I don’t really feel like triple-checking it
  • Plot assist: The tag can be any type of object you want: a creature, a placeable (like a chest or a door), a waypoint, etc. The markers will appear in game and on the area map as follows:
    • 0: removes a previously set marker
    • 1: sets the “❗” plot giver marker
    • 2: sets the “⏬” plot assist marker

The next steps

In the interest of length and usability, this examination of plots has been split into an additional two parts. An intermediary tutorial will revisit information and resources published elsewhere about how to create modded scripts and plots for DA2 and expand it to cover custom plot scripting. The most recent information I have on adding custom plots is in my Automatic Item Delivery tutorial.

The following installment of the dialogue modding tutorial will go into detail on various methods that can be used to determine the actual purpose and use of existing vanilla plot flags, including custom debug scripts and plot script disassembly.