Plot

From Dragon Age Toolset Wiki
Revision as of 19:11, 19 November 2010 by Proleric1 (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

A plot is a bundle of boolean (true or false) values that can be used to track the state of the game world. They are most commonly used to control the flow of conversations and to control the contents of the player's journal.

They can also be used as a convenient way to run scripts.

Bug: Duplicating a plot duplicates the uneditable GUID

Journal entries

Journal entries are created in the Plot Manager. When you open a Plot, you'll see a window called "Journal Text" at the bottom of the screen. Creating a journal entry is as simple as writing the text of the entry to that window while you have the appropriate flag selected.

For example, if you wanted to add a journal entry to the flag "JOURNAL_TEST" in the plot test_journal.plo, then you would check out test_journal, select "JOURNAL_TEST", and enter your journal text in the "Journal text" window.

For journal purposes, each plot file is a single journal topic, and so every flag that has an entry for a specific journal topic (say, "The Quest for the Golden Chicken") must be listed under the same plot file (say, "test_chicken"). You name the journal topic in the "Name" field in the Object Inspector when you have a Plot open.

For example, the Quest for the Golden Chicken has four parts:

  • QUEST_GIVEN: Research the last known location of the Golden Chicken.
  • RESEARCH_DONE: You know where the Chicken was last seen: the Museum of Precious Fowl. Talk to the curator of the museum to see if she heard anything about the whereabouts of the Golden Chicken.
  • CURATOR_MET: You spoke to the curator of the Museum of Precious Fowl. She told you many things, of which the current location of the Chicken is one. Go to the Dungeon of Infinite Sorrows and find the Golden Chicken.
  • CHICKEN_FOUND: You found the Golden Chicken. You are awesome.

As long as all relevant flags are in the same plot, each time a new flag is set, that flag's Journal Text will replace that of the previously set flag.

Only the latest Journal Text is displayed, but any previously set flags remain set (unless you explicitly switch them off). For example, when RESEARCH_DONE is set, QUEST_GIVEN will normally be set, too.

If you want to track steps within a subplot (say finding the Golden Chicken's last-known location involved several steps of researching places and visiting them in turn), you'll want to create multiple smaller plots and organize the flags within them, possibly naming them to indicate that they're subplots.

For example, if the Quest for the Golden Chicken had subplots of "Go to the dig site," "Speak with the Professor," and "Find the Tome of Finding Stuff," then you might want to create separate plots for each and begin each plot's name with "Golden Chicken: Go to the dig site" and so on. If you want to preserve the text of a journal entry when the next entry is given, you have to paste the original entry's text into the new entry or organize the new entry into a separate plot.

Currently there is a limit of 256 plot flags per plot. 128 of these flags are standard plot flags and the other 128 are "defined" plot flags whose state is determined by scripting each time they're read.

Tags for italics (<i> and </i>) can be used in the journal text field. Text can be emphasized with the <emp> and </emp> tags.

When a plot flag with "Final" set to "Yes" is set, the journal entry for the plot will be moved into the completed quests section of the journal.

Codex

A codex entry is a special type of plot resource which provides background information.

Simply create a new plot, setting the Entry Type to the codex section you wish to expand. This automatically creates a new blank codex slot in game.

It's not easy to remove the Official Campaign codex slots, because the associated plots are core resources. However, in a standalone module, those slots will remain blank throughout.

Creature codex entries appear when a creature of a given appearance is first killed. The codex text used is determined by the CodexPlot and CodexFlag entries in APR_base 2DA for the appearance in question. So you can make a new codex plot, either to give a different description to an existing appearance, or to add a new appearance.

An easy way to make other codex entries appear in game is to use a placeable which has an Examinable appearance. Set the PLC_CODEX_PLOT and PLC_CODEX_FLAG variables to the text you want to appear in the codex when the player examines the placeable.

Items can generate a codex entry on acquisition. The item's ITEM_SEND_ACQUIRED_EVENT variable must be set to 1. The item tag is set to the codex plot name (e.g. cod_bks_dalish_history). The ITEM_CODEX_FLAG variable identifies the main flag within that plot which corresponds to the codex text to be displayed. The default module_core script destroys the item after the codex entry is generated.

Story Plot

The Entry Type Story Plot is used to make the Story So Far text that appears when a saved game is loading.

Hints

The hints that appear during an area list transition are not plots at all, but strings chosen at random from the loadhints 2DA.

To suppress Ferelden-specific lore, remove the strings labelled as Flavor from that file.

Scripting

To interact with a plot's flags you'll need to include the plot in a script with a "plt_" prefix. For example, if you have a plot whose resource name is "kill_all_wolves", adding the following line at the top of your script would give it access to examine and change the flags in the "kill_all_wolves" plot:

#include "plt_kill_all_wolves"

You can then use the wrapper functions in wrappers_h (include "wrappers_h" to make them available) to get and set plot flags using the plot's name and flag's names as constants, like so:

int flag_value = WR_GetPlotFlag(PLT_KILL_ALL_WOLVES, FLAG_NAME);
WR_SetPlotFlag(PLT_KILL_ALL_WOLVES, FLAG_NAME, flag_value);

Plot event scripts

Many tasks in Dragon Age are accomplished by plot scripts triggered whenever a plot flag is read or set. A plot flag's ability to store a true/false value easily allows for "run once" situations without having to deal with local variables, but this is not strictly necessary and a plot's event script will run every time the plot flag is set regardless of whether it results in a change in the flag's value.

Unlike other event scripts, a plot script will have the function "int StartingConditional()" called when a plot flag is set or read. This allows the call to have a return value (an integer true/false result) that is most important when dealing with defined plot flags.

EVENT_TYPE_SET_PLOT is sent whenever a normal plot flag is set, with the event's integer 1 holding the identity of the plot flag that had been set. Note that the plot script is called before the plot flag itself is set; the plot flag's value doesn't change until after the script finishes running.

EVENT_TYPE_GET_PLOT is sent whenever a "defined" plot flag is read. The plot script's StartingConditional() function should return a result of TRUE or FALSE to indicate what the defined plot flag's value is.

To use a plot event script:

  • create a script e.g. "kill_all_wolves.ncs" by editing a copy of the default script (plot_core).
  • On your plot in the property inspector (bottom right of toolset) make sure to assign your script to your plot (instead of the default plot_core)
  • Set or get a plot flag, making sure to set the last parameter (nCallScript) to TRUE (see WR_GetPlotFlag and WR_SetPlotFlag
  • Remember that your script is executed each time a flag is set/get

If you want to trigger the plot (and thus the script) you can just call:

WR_SetPlotFlag(PLT_KILL_ALL_WOLVES, FLAG_NAME, flag_value, TRUE);

Important: Notice the last parameter TRUE. This parameter must be set to TRUE to have the function also call the plot script!

The default script plot_core illustrates the general structure that a plot script should have. Its comments document how various parameters are referenced. TO BE VERIFIED : The "conversation owner" passed to the script when not in conversation appears to be the object which created the event (this assertion has only been tested for module events).

Plot assist markers

Many quest givers in the single player campaign have a "!" symbol above their heads to indicate they can offer the player a quest. This will also appear on the area map. In order to have this present, you need to set the creature as a plot giver. This can be done in one of two ways, either within the creature properties, or via script using the following function:

SetPlotGiver(oObject, TRUE);

Once the player has received the quest, you need to disable this flag, unless you wish for the "!" symbol to remain over the character and on the world map. This can be done by using the plot's event handler script and adding the following line when handling the event that accepts the quest:

SetPlotGiver(oObject, FALSE);

In order to display intermediate plot markers (the downward pointing arrow symbol), you will need to modify the plot itself. For quest destinations you will want to add the tag of the creature or placeable to the Plot Assist list (bottom right) for the individual plot flags that are triggered when you want the destination marker to apear and disappear (Status set to Yes or No).

Plot flag ordering

Order doesn't matter for standard plot files, but does matter for codex plots.

For standard plots, whichever journal flag was last set to true will be the one that shows up in the journal. (I don't remember for certain at the moment, but I believe if you set additional "final" flags true it won't update a completed quest.)

For codex plots all active flags are displayed in order from least to greatest. This is used in cases like character entries where several equivalent entries are plot-dependent.

For example:

0: Cookie Monster is a devourer of cookies.
1: Cookie Monster was a devourer of cookies.
2: He threatened to destroy the kingdom.
3: <FirstName/> dispatched him handily.
4: But <FirstName/> bribed him with a cookie.
5: But Big Bird made him play nice.

0 would be true initially and set to false (with 1 set to true) if Cookie monster died. 2 would always be true. Only one of 3,4, or 5 would be true, depending on the player's actions (killing, bribing, or ignoring Cookie Monster). If the player had not yet encountered the monster and chosen an action, flags 3, 4, and 5 would all be false.

Granting rewards via plots

Each plot flag can have a reward associated with it, picked from a list defined by the rewards.xls 2DA. This reward is given to the player when the plot flag is set.

Once your rewards.GDA is in your export folder, it becomes available to plots when you next start the toolset.

This is a simple and powerful mechanism for giving the player XP, money, items or a combination thereof.

If you are making a new campaign, you may prefer to override rewards.xls entirely, since the default contents refer to the official campaign plots. Alternatively, you can make an M2DA if you just want to add to the official rewards.

Parent-Child Plots

Sometimes, plots have independent sub-plots which can happen in any order.

For example, "find a lemon and a duck, then make soup in the kitchen".

We might want to update the journal when the lemon or the duck is found.

This is simply achieved by defining three plots: the parent plot ("soup") and two child plots ("lemon" and "duck").

The relationship is established simply by setting the Parent Plot property of the child plot.

It's only a matter of presentation in the journal - you set the parent and child plot flags independently.

You might not need to synchronise them, either - in our example, it may be acceptable that the parent journal still says "I need to find a lemon and a duck, then make soup" after the child journals both say "I found x", until the soup is actually made.

In that case, it's sufficient to have a Defined Flag on the parent which checks whether both child plots are complete when we try to make soup in the kitchen. The plot script will include this snippet in the Defined Flags section:

    event eParms  = GetCurrentEvent();
    int   nFlag   = GetEventInteger(eParms, 1); // Plot flag
    int   nValue  = GetEventInteger(eParms, 2); // 0=Clear 1=Set
    int   nResult = FALSE;
 
    /* in script section for defined flags */
 
    switch(nFlag)
        {
          case DEFINED_SOUP_INGREDIENTS_FOUND:
            {
              nResult = (WR_GetPlotFlag(PLT_LEMON, LEMON_FOUND) && WR_GetPlotFlag(PLT_DUCK, DUCK_FOUND))
 
              break;
            }
        }
 
    plot_OutputDefinedFlag(eParms, nResult);
    return nResult;


However, if you need to update the parent journal as soon as all child plots are complete (so that it reads "I found a lemon and a duck, now go to the kitchen and make soup"), there is an important nuance - when a plot flag is set, its script is called BEFORE the flag receives its new value.

This complicates the logic. Also, if you close the parent journal before making the last child entry, the latter won't show up. To work around both issues, you can force the child flag to be set first (taking great care not to invoke the plot script in an endless loop).

So, in the plot script for "lemon", you need something like

    event eParms = GetCurrentEvent();
    int   nFlag  = GetEventInteger(eParms, 1); // Plot flag
    int   nValue = GetEventInteger(eParms, 2); // 0=Clear 1=Set
 
      switch(nFlag) 
        {
          case LEMON_FOUND:
            {
              if (nValue)
                {
                   // Force the plot flag to be set first
 
                   WR_SetPlotFlag(PLT_LEMON, LEMON_FOUND);   
 
                   // The following could be duplicated in the "duck" script (or common code):
 
                   if (WR_GetPlotFlag(PLT_LEMON, LEMON_FOUND)
                   &&  WR_GetPlotFlag(PLT_DUCK,  DUCK_FOUND))
                     WR_SetPlotFlag(PLT_SOUP, SOUP_INGREDIENTS_FOUND, 1, TRUE);                  
                }
              break;
            }
        }

whereas in the plot script for "duck" you need the converse. In this instance, checking that both flags are set will fail, because one of them is not yet set at the time the script executes.

Of course, unless you make the items "plot" or "indestructable", you might need additional checks to confirm that the player still has the items when the time comes to use them. That's yet another alternative to synchronising the journals.

Properties

General
Allow Pausing
Entry Type
GUID Unique in-game identifier for a plot.
Journal Image An optional journal image that can appear in the journal for this plot.
Name Name of the plot as shown in the journal
NameRequiresReTranslation A true/false flag used during game development for localization work
Parent Plot Associates subplots with a main plot.
Priority Sorts the plots in the journal
Resource Name A unique string identifier the toolset and the game both use to refer to this resource
Script Event script assigned to the resource.


Language: English  • русский