Story Format

From Post-Apocalyptic RPG wiki

Jump to: navigation, search

Deprecated code proposal.png This article features a deprecated code proposal.

Deprecated Proposals are no longer considered for implementation. This does not necessarily mean that they are completely rejected but for now have been set aside.

This article is a proposal for a file format for interactive story content in PARPG. It follows on from Story engine which describes the overall framework.

There is a very large amount of material here, and it is likely that there are errors. Please report any inconsistencies or problems!

Contents

Quick overview

An event has a set of preconditions (things that must have happened beforehand) and a set of postconditions (things that should be done afterwards). Each event can have a title, a set of people involved and a description. The description is used to inform the player (in words) about what is happening; it could be the dialog the player says, it could be a description of what is happening ("The box hums." or "Die traitor!"). The description is what appears in any dialog tree that is shown by the game.

For example:

"Save princess quest"
---------------------
"John": 'I will save you princess!'.
Beforehand:
- "princess kidnapped" and "health" of "john" greater than 5.  
Afterwards: 
- start combat between "John" and (guard).
- add journal entry for 'I said I''d rescue the princess.'.
- begin "princess rescue plot arc" with "princess guard" as (guard).

Each story event in the file is separated from the others by a blank line, which it might helpful to think of as marking a paragraph. Story events can be grouped into story arcs (and story arcs can contain other story arcs). Individual events in a story arc can share a cast of characters, and props (the explanation of why you'd want to do this will have to wait for later), and some pre- and post-conditions. When a story arc has pre-conditions, those pre-conditions must be met for every event in the arc. For example, if "princess is alive" is a precondition for the whole arc, then every event in the arc depends on the princess being alive - if she dies the whole arc becomes unavailable.

"Princess rescue plot arc"
==========================
Cast: A "princess guard" & "John".
Props: A "wooden sword".
Beforehand either:
- "princess kidnapped".
- "princess dead" and "queen kidnapped".

The most useful pre-conditions are in the form of pre-defined sentences supplied by the game not the story engine. For example, the game might say that it can answer a question like "%% is alive" where the story writer is invited to supply a name for %%; you might write:

"John" is alive.

These can be pairwise relations, so another possible sentence would be "%% has met %%"; the story writer might write:

"John" has met "Alice".

Post-conditions take the form of instructions, and the most useful are again are supplied by the game as pre-defined sentences. A possible sentence might be "Add to the journal %%" and the story writer would write:

Add to the journal 'Got a quest to rescue the Martian king'.

Story events

The main layout of a story event is as follows (parts in bold are keywords; all the punctuation is important):

*** "Event title" ("Arc title")
    -------------
Arcs:
- An arcs this event belongs to.
- Another arc this event belongs to.
Actors to targets: 'Description of event that takes place'.
Actors to targets: 'Another part of the event that takes place'.
 unless role is actor in which case actors to targets: 'An alternate description for this part'. 
Beforehand:
- A thing that must be true beforehand.
- Another thing that must be true beforehand.
Afterwards:
- Something that must be done if this event occurs.
- Another thing that must be done if this event occurs.
< Special flags for this event >

In choosing names, titles and descriptions, the writer has complete freedom. Names and titles are enclosed in double quotes, and descriptions are enclosed in single quotes. So for example if your character is called John, in the story file you will always write "John". Single quoted strings can span multiple lines, but double quoted strings cannot.

If you want to include quotes and apostrophes, you'll need to double them up (so "John''s car" rather than "John's car") - this is again because the parser isn't smart enough to know the difference between nested quotes, or between apostrophes and quotes.

Many parts of the above form are optional. The title is optional, but if there is a title, it must be underlined with dashes. A story event without a title can't be referenced elsewhere by its title. In the example we had "princess kidnapped" which might be a story event, so somewhere else there would be an event that begins

"Princess kidnapped"
--------------------
...

The title can have two extra bits we haven't seen in the examples. The stars are a shorthand way of saying "This story event follows the one before it." More precisely, when a title starts with stars, it follows the story event most closely before it in the file with a smaller number of stars. So if we take this example:

"Story event one"
-----------------

* "Story event two"
  -----------------

** "Story event three"
   -------------------

* "Story event four"
  ------------------

Then story event one is followed by story events two and four, and story event two is followed by story event three. Of course you could also write:

"Story event one"
-----------------

"Story event two"
-----------------
Beforehand, "story event one".

"Story event three"
-------------------
Beforehand, "story event two".

"Story event four"
------------------
Beforehand, "story event one".

These are two ways of saying the same thing, the first is just an easy shorthand for the second. Stars can be given even when the rest of the title is missing. So you could, if you wanted an unnamed event dependent on the previous event, say something like:

* "John": 'Watch this...'

Inevitably, there could be very many stars in some situations. To help with this, a star can be followed by a number indicating how many stars it should be read as, for example *5 means *****. With large numbers of stars, or with editing, keeping the star counts correct might become difficult. Its also possible to say *=, meaning same number of stars as the last event, *< for fewer stars than the last event, and *> for more. Of course these can be used in combinations *<< means two fewer stars, *>4 means four more stars. And so on.

Asterisks have a second side effect in that they continue the current arc. That is, if the event you indicate you depend on with asterisks was in a set of arcs, this event is also in all of those arcs.

The second optional part of the title is an arc name, giving a plot arc this story event is involved in. There is also the option to give multiple arc names underneath in the arcs section. We'll talk more about plot arcs later.

The actors, targets and description are all optional so you could have any of:

"John" : 'Hello!'.
"John" to "Jack": 'Hello!'.
to "Jack": 'You feel sleepy!'.
"John".
"John" to "Jack".
to "Jack".
'The box begins to play music!'.

You can have multiples of these, or you can also omit this part entirely.

The actors are the people performing the event. If there's no person "doing" the event, then there's no actor. The targets are the people on the receiving end of this event, be it speech or whatever. If there's no-one this event is to specifically, it doesn't have a target. The description is the text that will be shown to the player by the game. If there's no description, the player won't see any text.

The format is a little more powerful than this because it allows you have a list of actors and a list of targets (but each description has its own list of actors and targets). Lists have a particular form that might be hard to remember at first (again due to the limitations of the parser in understanding human language). Lists look like this:

"John"
"John" & "Jack"
"John", "Jack" & "Fred"
"John", "Jack", "Fred" & "Wilma"

and so on. So that a complicated story event might be described as:

"A greeting event!"
-------------------
"John", "Jack" & "Fred" to "Wilma" & "Betty": 'Hello (in unison)'.

Note that the comma goes outside the quotes.

Unless clauses are used with plot arcs that have cast roles. They indicate different dialog to be used in the event a particular individual was cast in a particular role. See the section on plot arcs for more information.

Preconditions

A set of preconditions consists of the keyword Beforehand followed by a rule which will decide whether the story event could occur at any given moment or not.

Event Names

The simplest kind of rule is just the name of a story event, for example we could have any of:

Beforehand, "princess has been saved".
Beforehand, "the tea party at the inn".
Beforehand, "John's dad comes to visit".

In this case, beforehand means right before. Unfortunately, to make it read a little more naturally, it's slightly more complicated to say what right before really means. It goes something like this: when a story event happens, any story event that depends on it happening right before becomes available. When any of those story events that followed it happens, it no longer happened right before and so they all become unavailable (because they depended on it happening right before!) Some examples should make this clearer.

"Step one"
----------
Beforehand, start of the story.

"Step two"
----------
Beforehand, "step one".

"Step three"
------------
Beforehand, "step one".

"Step four"
------------
Beforehand, "step two".

Imagine this is our story. Let's suppose step one just happened. Either step two or step three could happen next, because step one happened right before and both of them depend on that. If step two happens next, then step one no longer happened right before, which means neither step two or step three is available anymore. However, step two did happen right before so step four is now available. This basically means that it works out the way you expect naively in most cases.

Sometimes, we might want to depend only on a story event having ever happened; perhaps part of a previous conversation for example. In that case we can use the built in sentence has happened, for example we could say:

Beforehand "step three" has happened.

Of course if this was the only condition on an event in a real dialog, once step three had happened, it would always be available and the player might see it over and over again!

There are a few more built-in sentences like has happened. Start of the story happens only once, right at the beginning of the story. It can be used to make sure something only happens right at the start. Has begun can be applied to an arc name, saying whether that arc has started. Finally, start of with an arc name only happens once, right when that arc begins.

There are other names kept by the story called story knowledge which we shall talk about when we talk about remember and forget in post-conditions. These are true if they have been remembered, false if they have been forgotten, and also false if the story engine has never heard of them.

Predicates

To find out information from the game (rather than just the plot) the story writer must use predicates. Don't be put off by the name, these are just sentences provided by the game which have some missing words to fill as appropriate. For example, the game might supply a sentence "%% is alive", which allows the story to contain phrases like:

Beforehand "John" is alive.
Beforehand "Harry" is alive.
Beforehand "the great scaly dragon" is alive.

We mark phrases supplied by the game in blue, to show the writer does not have a choice about the wording (they're just simple patterns the parser is looking for, it doesn't understand them, except that they're a message to pass on to the game).

It depends on the game to define a set of predicates that make sense. These sentences can have any number of blanks, so for example we could have "%% can see %%", and then we could write:

Beforehand "John" can see "Fred".
Beforehand "the evil arch-villain" can see "the princess".
Beforehand "Toby" can see "Wilma".

Extra flexibility is provided by allowing the blanks to be substituted for lists, for example:

Beforehand "John" & "Toby" can see "the evil arch-villain".
Beforehand "Toby", "Wilma" & "the princess" can see "Tom", "Jerry" & "Larry".
Beforehand "John", "Toby" & "Wilma" are alive.

The last example is important: the story engine doesn't know about is and are (which is grammatically correct) it's just looking at sentences like "%% is alive" "%% are alive"; it's a good idea for the game to provide plural forms to allow the story to scan more naturally.

More complex logic

We've seen the basic sources of knowledge available to the story engine: it's own base of story events that have happened and knowledge it was told to remember, and also predicates for asking the game about its situation. We don't want to just rely on one of these per story event though; often we'll want a combination of several things to be true.

Story files allow information to be built up using basic logical phrases. For example, if "princess has been rescued" and "princess" is alive are two pieces of information, we can combine them simply:

"princess has been rescued" and "princess" is alive.
"princess has been rescued" or "princess" is alive.
not "princess has been rescued".
not "princess" is alive.

These correspond to the ordinary meanings of these words. The form for not might look a little alien, but it's like this because the parser isn't clever enough to know that the right forms should be "princess hasn't been rescued" and "princess" isn't alive. The final way of combining two pieces of information is xor which is probably unfamiliar to anyone without an engineering or mathematical background. xor means either one or the other is true, but not both. For example:

"princess has been rescued" xor "princess" is alive.

This means either the princess has been rescued, or the princess is alive, but both aren't true. So if the princess had been rescued and was alive, this wouldn't be true, only if one or other was true.

The next problem is that it's a little difficult to work out what we mean when we say:

"thing one is true" or "thing two is true" and "thing three is true"

Do we mean that thing three has to be true always, or do we mean that it doesn't matter if thing three is true so long as thing one is? In actual fact, the parser thinks of things connected together with or as closer together than things with and so sentence does mean that thing three always has to be true. But this is not very clear! The best way to make this clear is to split it over multiple lines, using bullet points.

The above example can be rewritten as:

 The following are true:
 -     Either:
 --       "thing one is true".
 --    or "thing two is true".
 - and "thing three is true".

The length of the bullet points lets us know how the things group together. The phrase the following are true starts a set of bullet points, all of which must turn out to be true. So for example:

 the following are true:
 -    "the princess is alive".
 -and the season is "winter".
 -and it is night time.

This would only be true when all of the bullets below it were true. The alternative is to start with either, for example:

  Either:
 -   "John"s "horse" is lame.
 -or "princess has been rescued".

Which is true when any of the bullets below it is true. These phrases can be nested as we saw in the first example, with each level adding another length onto the bullet point, just like normal bullet points.

As a special case, after beforehand you don't need the following are true; it's implied, so you can say:

Beforehand:
-    "thing one is true".
-and "thing two is true".

to mean both thing one and thing two must be true.

The first bullet point in a list has neither and nor or. Subsequent bullet points take either and or or (as in the examples above). This is to make it clearer which parts go with which other parts of the list. You can't use or in a group of bullet points headed by the following are true, and you can't use and in a group of bullet points headed by either.

It might occasionally be the case that you don't want to introduce more bullet points, perhaps what you have to say is short, or you think it would be clearer without adding an extra level of bullet points. In these cases there is also bracketing for logic, which isn't very much like English, but is very much like a logical system.

Our original example could have been written:

{"thing one is true" or "thing two is true"} and "thing three is true"

The brackets mean "do this bit first", so in the example, they say to work out whether thing one or thing two is true before working out whether thing three is true. If we wanted to say that "thing three doesn't matter if thing one is true" we could change the brackets:

"thing one is true" or {"thing two is true" and "thing three is true"}

Generally bullets are clearer, although more verbose, but there is the option to use brackets if you so desire. Note that these are curly braces not parentheses.

Postconditions

Postconditions consist of a keyword afterwards followed by a set of bullet points describing what actions should occur if this event comes to pass. For example:

Afterwards:
- start combat between "John" and "Fred".
- add journal entry for 'I said I''d rescue the princess.'.
- begin "princess rescue plot arc" with "princess guard" as "Fred".
- remember "Fred and John fought".
- forget "Fred and John are friends".

The major use of afterwards will probably be to cause something to happen in the game. The game offers sentences (in the imperative form; orders) that you can fill in to allow this event to change the game. As in the section on preconditions, we show these sentences in blue.

All the sentences in the afterwards section are performed in order every time the event happens. If you need to have only some of them happen, you need two different events so that the tests to decide which orders are carried out can be specified properly in a beforehand section.

There are three built in commands, remember, forget and begin. We'll talk about begin in the section on plot arcs. Remember and forget are used to store knowledge in a simple way. Essentially, each remembered piece of knowledge is just a name you give. For example if you say

Remember "John ate that candy".

Then you could write preconditions for other events like:

Beforehand, "John ate that candy".

This is used the same way as if there had been an event called "John ate that candy". In fact, you can use remember to pretend an event happened that didn't. For example if you have:

"John steals the candy"
-----------------------
"PC": '[steal the candy]'.
Beforehand, start of the story.

"John doesn't steal the candy"
------------------------------
"PC": 'I would never steal your candy!'.
Beforehand, start of the story.

* "PC": '[steal the candy anyway]'.
Afterwards:
- Remember "John steals the candy" has happened.

"Some time later"
-----------------
"Shopkeeper": 'You aren''t welcome here candy thief'.
Beforehand:
- "John steals the candy" has happened.

When we get to "Some time later" (after intervening events omitted for brevity) the shopkeeper's reaction is the same whether "John steals the candy" happened, or the unnamed event that followed "John doesn't steal the candy" happened. A neater way of doing this in this particular example might have been:

"John steals the candy"
-----------------------
"PC": '[steal the candy]'.
Beforehand, start of the story.
Afterwards: 
- Remember "shopkeeper's candy was stolen".

"John doesn't steal the candy"
------------------------------
"PC": 'I would never steal your candy!'.
Beforehand, start of the story.

* "PC": '[steal the candy anyway]'.
Afterwards:
- Remember "shopkeeper's candy was stolen".

"Some time later"
-----------------
"Shopkeeper": 'You aren''t welcome here candy thief'.
Beforehand:
- "Shopkeeper's candy was stolen".

One thing that might be puzzling is why we use has happened in the first example and not in the second. Events (like "John steals the candy") are slightly different from pure knowledge (like "shopkeeper's candy was stolen") because we need to know whether events happened right before (see earlier) or just at some point in the past. If we'd just said

Remember "John steals the candy".

it would be as if that event happened right before. You might want this to join the dialog tree up again. For example:

"John steals the candy"
-----------------------
"PC": '[steal the candy]'.
Beforehand, start of the story.

* "Shopkeeper": 'Miserable candy thief, die!'
Afterwards:
- "Shopkeeper" attacks "PC".

"John doesn't steal the candy"
------------------------------
"PC": 'I would never steal your candy!'.
Beforehand, start of the story.

* "PC": '[steal the candy anyway]'.
Afterwards:
- Remember "John steals the candy".

In this example, After the he steals the candy anyway, it goes straight back to the 'Miserable candy thief' line, because it is as if "John steals the candy" happened right before.

Forget is the opposite of remember and can be used in the same ways, both to store knowledge and to alter the course of events. We could extend the above example to allow a successful "late payment" option, for example:

"John steals the candy"
-----------------------
"PC": '[steal the candy]'.
Beforehand, start of the story.

* "Shopkeeper": 'Miserable candy thief, die!'
Afterwards:
- "Shopkeeper"  "PC".

** "PC": 'Take what I have... Please stop!'
Afterwards:
- "PC" gives "shopkeeper" all his money.
- Forget "John steals the candy" has happened. 

"Some time later"
-----------------
"Shopkeeper": 'Welcome back!'.
Beforehand:
- not "John steals the candy" has happened.

Processor instructions

In the section on postconditions (afterwards) we discussed some of the ways you can influence how the game decides which events are available using remember and forget. These are very powerful and almost any effect can be achieved with the right combinations. However, to make things a little simpler, certain processor instructions are available which automate some common types of choice. Processor instructions come last in an event.

Default behaviour

In order to explain how each processor instruction can be simulated with remember and forget it's necessary to understand what the default behaviour is without any processor instructions. You can safely skip over this bit on a first reading.

Let's take a sample event

"Sample event"
--------------
Beforehand, "earlier event 1".

This is taken as (by default)

"Sample event"
--------------
Beforehand:
- "earlier event 1".
Afterwards:
- forget "earlier event 1".
- remember "sample event".

This only happens to events that should have happened right before; so that when an event that depended on something that happened right before occurs, all other events depending on it are unavailable (including the event that just happened).

Branching

The instruction starts a branch means that this begins a parallel tree of conversation. This is best explained with an example.

"start event" 
-------------

* "branch event"
  --------------
  <starts a branch>

** "branch event 2"
   ----------------

* "normal event"
  --------------

** "normal event 2"
   ----------------

In this case first "start event" happens, then we have a choice of "branch event" or "normal event". If we select "normal event", things proceed as normal, "normal event 2", is the only choice. If we select "branch event" however, both "branch event 2" and "normal event" are available.

The explanation for this is that

"Earlier event 1"
---------------

"Sample event"
--------------
Beforehand, "earlier event 1".
<starts a branch>

is taken as

"Earlier event 1"
---------------
Afterwards: 
- forget "sample event marker". 

"Sample event"
--------------
Beforehand: 
- "earlier event 1".
- not "sample event marker".
Afterwards:
- remember "sample event marker".
- remember "earlier event 1".

Repeating events

The instruction repeats immediately means that this event doesn't stop any of the previously available events from being available again immediately. It's like branching, but even the branch's start point is still available. Another example

"start event" 
-------------

* "repeat event"
  --------------
  <repeats immediately>

** "repeat event 2"
   ----------------
 
* "normal event"
  --------------

** "normal event 2"
   ----------------

In this case first "start event" happens, then we have a choice of "repeat event" or "normal event". If we select "normal event", things proceed as normal, "normal event 2", is the only choice. If we select "repeat event" however, "repeat event" and "repeat event 2" and "normal event" are available.

The explanation for this is that

"Sample event"
--------------
Beforehand, "earlier event 1".
<repeats immediately>

is taken as

"Sample event"
--------------
Beforehand: 
- "earlier event 1".
Afterwards:
- remember "earlier event 1".

Sequence events

The instruction starts a sequence means that this event stops any alternative events that depended on the same events having happened right before from being available, like a normal event, but also that it will restore their availability when this dialog tree is done. An example

"start event" 
-------------

* "sequence event"
  --------------
  <starts a sequence>

** "sequence event 2"
   ----------------
 
* "normal event"
  --------------

** "normal event 2"
   ----------------

In this case first "start event" happens, then we have a choice of "sequence event" or "normal event". If we select "normal event", things proceed as normal, "normal event 2", is the only choice. If we select "sequence event" however, "sequence event 2" is the only choice, but after "sequence event 2", "normal event" will become available again.

The explanation for this is that

"Earlier event 1"
-----------------

"Sample event"
--------------
Beforehand, "earlier event 1".
<starts a sequence>

* "Sample event 2"
  ----------------

is taken as

"Earlier event 1"
-----------------
Afterwards:
- forget "sample event marker".  

"Sample event"
--------------
Beforehand: 
- "earlier event 1".
- not "sample event marker".
Afterwards:
- remember "sample event marker".

* "Sample event 2"
  ----------------
Afterwards:
- remember "earlier event 1".

Loop events

The instruction starts a loop means that this event stops any alternative events that depended on the same events having happened right before from being available, like a normal event, but also that it will restore their availability when this dialog tree is done. An example

"start event" 
-------------

* "loop event"
  --------------
  <starts a loop>

** "loop event 2"
   ----------------
 
* "normal event"
  --------------

** "normal event 2"
   ----------------

In this case first "start event" happens, then we have a choice of "loop event" or "normal event". If we select "normal event", things proceed as normal, "normal event 2", is the only choice. If we select "loop event" however, "loop event 2" is the only choice, but after "loop event 2", both "loop event" and "normal event" will become available again.

The explanation for this is that

"Sample event"
--------------
Beforehand, "earlier event 1".
<starts a loop>

* "Sample event 2"
  ----------------
Beforehand, "sample event".

is taken as

"Sample event"
--------------
Beforehand: 
- "earlier event 1".

* "Sample event 2"
  ----------------
Beforehand, "sample event".
Afterwards:
- remember "earlier event 1".

Singular events

The instruction happens once only means that this event never re-occurs unless the fact that it has happened is explicitly forgotten. An example

"start event" 
-------------

* "singular event"
  --------------
  <happens once only>

** "restart event"
   --------------
Afterwards:
-remember "start event".

In this case first "start event" happens, then "singular event". After "singular event", "restart event", taking us back to "start event", but "singular event" only ever happens once so there are no further events.

The explanation for this is that

"Sample event"
--------------
Beforehand, "earlier event 1".
<happens once only>

is taken as

"Sample event"
--------------
Beforehand: 
- "earlier event 1".
- not "sample event" has happened.

Advanced: Locking

Locking is a means for trying to control what other kinds of events can happen after yours. In the preceding examples, we missed out the lock for clarity. In the default case (if you don't say anything) the effect is as if you had said

"Event 1"
---------
Actors to targets.
<locks actors>

This means that if there is a choice of following events, events with the same actors will be avoided, and events with different actors given priority. Sometimes, this might not be what you want. You might want to force the PC to listen to a few separate dialog events from an NPC without being able to interrupt, or you might want to let the PC say several things in succession without letting any NPCs interrupt. To do this you need to lock the likely interrupters, for example

"Event 1"
---------
"PC" to "John": 'I remember...'.
<locks "John">

* "PC" to "John": 'nevermind...'.

"Event 2"
---------
 "John" to "PC": 'Lovely weather we're having?'.

After "event 1", the story engine is considering whether "event 2" or the unnamed event that follows "event 1" should occur. If we hadn't specified a lock in event 1, then it would have by default locked the PC, and John would speak next in event 2. However, since we did specify a lock, the PC wasn't locked (he isn't mentioned) and John was locked. So the unnamed event after event 1 happens, and John isn't allowed to interrupt.

You can in general lock anyone after any event, regardless of whether they were involved in it or not. You may also say:

<does not lock "John">

if you don't want to have John locked. For example, perhaps the event was

"Event 1"
---------
"PC" & "Ted" to "John": 'Shut up!'.
<does not lock "PC">

After this event, the PC is not locked and might speak again immediately, but Ted is locked, and won't be considered unless no-one else could do anything.

Including other files

The final processor instruction is used to include story events from other files into this one. The syntax is:

<insert 'filename.story' here>

It must be separated by newlines from any other paragraph (that is, don't put it in an event, or at the end of an event or arc). The file will be interpreted as if it had all been typed out at that point.

Plot Arcs

The layout of an arc description is:

"Arc title" ("A parent arc")
 =========
Arcs:
- An arc this one belongs to.
- Another arc this is nested in.
Cast: characters.
Props: objects.
Beforehand:
- A thing that must be true beforehand for every event in the arc.
- Another thing that must also be true beforehand for every event in the arc.
Afterwards:
- Something that must be done if any event in this arc occurs.
- Another thing that must be done if any event in this arc occurs.

An arc is a group of events with a title. There are a number of powerful things arcs enable. You can make an event part of an arc in three different ways, and an event can be a part of any number of arcs.

  1. Place the arc name in parentheses after the event title. Only one arc name can be placed here, and it can't be qualified.
  2. Use asterisks like you would to follow an event (note you can't use beforehand "arc name" to make an event part of an arc; that has a different meaning).
  3. Use the arcs section of the event. This allows maximum flexibility, you can list any number of arcs, and they can be qualified names, so you can join sub-arcs of other arcs.

Arcs for modularity

Story knowledge introduced in an arc is specific to that arc. So its perfectly legitimate to have two events of the same name in different arcs. For example

"Arc 1"
=======

* "Bob's bad day"
  ---------------

"Arc 2"
=======

* "Bob's bad day"
  ---------------

There's no conflict here. Inside arc 1, the first event is meant by "Bob's bad day" and inside arc 2 the second is. Outside these arcs, you'd need to qualify which you meant, for example:

"Arc 3"
=======

* "Joe's bad day"
  ---------------
Beforehand, "Bob's bad day" of "arc 1".

If you didn't qualify it, it would be looking for an event called "Bob's bad day" in one of its parent arcs, or in the main story. So in the following:

"Arc 2"
=======

* "Bob's bad day"
  ---------------
* "Arc 3"
  =======

** "Joe's bad day"
   ---------------
Beforehand, "Bob's bad day".

is fine, there's no need for any qualification, because arc 3 is inside arc 2.

Arcs for shared conditions

Suppose you have a group of events that happen one after another, but they depend on a key character being alive, and you're worried that character might die in between some of them. Without any use of arcs, you might end up writing:

Event 1
-------
Beforehand, "princess" is alive.

* Event 2
  -------
Beforehand, "princess" is alive.

** Event 3
   -------
Beforehand, "princess" is alive.

And so on. This is very verbose, especially if there were a few of them. To avoid this, you can make them an arc, and place the precondition on the arc itself; for any event in the arc to happen, it must match all the preconditions defined for the arc. So the above could be simplified to

Arc 1
=====
Beforehand, "princess" is alive.

Event 1
-------

* Event 2
  -------

** Event 3
   -------

This also works for postconditions, so if for example, you wanted a particular character that burped after everything he said, you could (as in, it would be possible, not this would be a good idea) use an arc like this to group his dialog events:

Burp arc
========
Afterwards:
- "John" burps.

Arcs for making generic characters "special"

One feature of RPGs is the hard separation between special and generic characters, where generic characters behave the same as an entire class of creatures, and special characters have personally written dialog. An arc can have a cast of characters passed in to it, and these become names within the arc. An example:

"Rescue princess"
-----------------
("guard") to "PC": 'I will help you get her majesty away from here!'
Beforehand:
-    ("guard") is nearby.
-and ("guard") is loyal.
Afterwards:
- begin "princess rescue arc" with ("guard") as "princess guard".

"Princess rescue arc"
=====================
Cast: a "princess guard".

* "princess guard": 'My name is {"princess guard"s "name"}'.

In this example, anyone that was nearby and loyal could be cast as the princess guard in the princess rescue arc. They become a special character with dialog written for them, despite the fact that they were just a generic NPC before the arc started.

Sometimes, you will want to account for certain standard options, for example an arc might only have a few people who could be cast in each role. Although the structure of the events is the same, you might want to personalise their dialog to increase replay value. This is the function of unless descriptions. If we have an event like:

"Complain event" ("Princess rescue arc")
----------------
"princess guard": 'This is hard work compared to guarding the castle.'.
unless "princess guard" is "Miserable Sam" in which case 
  "princess guard": 'We're doomed.'.
unless "princess guard" is "Fat Tom" in which case 
  "princess guard": 'I'm starving.'.

Then in the general case, the guard offers a generic complaint, but if we happen to know that it was Miserable Sam on duty when the princess escaped, and who came with us, we can personalise his dialog, and similarly for Fat Tom.

There is no distinction to the story engine between props and cast; but you may find it helpful to distinguish animate and inanimate cast this way (for example, if you want the PC to lie that any old sword is an heirloom, that could be passed in to an arc, and that particular sword can be treated as special by you thereafter).

Arcs in conditions

There are a few special preconditions that can be used with arcs. If we want to test if an arc has started, we can use has begun, for example

'Beforehand, "princess rescue arc" has begun

will be true when the princess rescue arc has been cast and events from it are ready to take place.

If we want to test whether an arc has just started (which means no events from that arc have occurred yet normally; you can think of the arc starting as an event, which in this case must have happened right before) then we can use:

Beforehand, start of "princess rescue arc".

We can also use is cast as to decide if a particular role has been filled by a particular individual, similar to the way we can do this using unless for descriptions.

In postconditions, we can forget an arc has begun which means it is uncast and all events from it cease to be available. It could conceivably be later re-cast if you allowed the event which originally cast it to occur again. You cannot remember an arc has begun; you can use begin to start an arc, passing in who you would like to cast the characters as. The full form of begin is:

begin arc name with "character" as "role"; "character" as "role"

and so forth; note that the with part can be separated with semicolons or using a conventional list (commas and ampersand).

It is possible to remember an arc name (rather than to remember it has begun), in a similar manner to the distinction for events. If you just remember the name, it is as if the arc starts again: the test start of "arc" will be true again.

The story title

At the very top of an input file it is possible to define an overarching arc for this file. This can only be done once, and it means every single event in the file is part of that arc. This is called a story title. The format is:

"My story"
==========
==========

Other than the double underline, it has all the parts of a normal arc paragraph.

Complex data

There are two ways of interacting with data non-trivially supported by the story engine. The first is what you might call properties or table look-up, the second is variables. These two combine in quite powerful ways.

Properties

In some of the above examples you will have seen phrases like

"John"s "horse".
"name" of "John"s "horse".

These are property phrases (equivalent to the genitive in English in some ways, hence the syntax). For programmers, you may recognise of as the member of operator '.' in common computer languages. In the story engine, these are ways of finding items that belong to people, or finding properties of people (the two aren't really the same thing in normal use, but for simplicity the story engine considers them the same thing).

You can introduce a property phrase in two ways, using of and the genitive s (called operator s in the grammar). The form for the first is

property of entity

so for example

"legs" of "chair"
"wife" of "John"

The form for the second is

entitys property

so for example

"chair"s "legs"
"John"s "wife"

The genitive s binds more tightly than of, so if you say

"weight" of "John"s "horse".

this means the weight of the horse of John, not the horse of the weight of John!

A property phrase like this can be used anywhere a name can. For example anywhere you can write "John" you could also write "John"s "wife". If you use a property an object doesn't have (only the game knows what property an object has - the story engine is just responsible for asking for the properties from the game for you) then one of two things could happen:

Firstly, in a beforehand section, the relevant condition involving that property will automatically be considered false. For example in

Beforehand, "John"s "wife" keeps horses.

If John doesn't have a wife, this condition can't be satisfied, so until John marries, this event will never occur.

Secondly, in a description or function, using a property which doesn't exist results in a warning, and the offending line being ignored. For example if you say:

Afterwards:
- Add "John"s "wife" to the "death list".

You'll see a warning about John not having a wife and the death list won't be changed.

Variables

Variables are one of the most powerful and complex features of the story engine. A variable is a name in parentheses in the story format, for example ("guard"). All the identifiers we've seen so far have some pre-defined meaning with the game - when you say "John" he corresponds directly with some character placed on the map in the map editor, and loaded into the game. In contrast, when the story engine is confronted with a variable, it is responsible for finding a possible thing which could go in this variable. This is both more powerful than it seems, and also sometimes less intuitive.

All of the finding out is done in the beforehand section of the event, in which you list the things you are looking for in the object that is to be identified with the variable. An example might help:

Beforehand, ("guard") is patrolling and ("guard") is near "PC".

This is very powerful: this rule will match whenever someone is patrolling near the PC, and you'll be able to refer to that character later on, for example,

Afterwards:
- ("guard") attacks "PC".

Suppose you want a primitive wildlife system. Consider:

"Hunting event"
---------------
Beforehand:
- ("hunter") can see ("hunted").
- ("hunter") is hungry.
- ("hunter") is a predator of ("hunted").
Afterwards:
- ("hunter") attacks ("hunted").

It is also less intuitive than you initially think. Imagine the story engine is very very helpful but understands no English. You send it away for beans, and it comes back with a chair. The rules governing a variable are there to reject unsuitable things the story engine brings back to you. The "hunting event" above has some loopholes that might be exploited for example.

What about humans, do they attack food on sight? Perhaps the game will have defined is a predator of for animals only (or should we make sure the story engine brought back an animal just in case). What if the PC is dragged away into fighting an animal automatically because the story engine brings back the PC for ("hunter"). With care and a little thought, a huge amount of power can be unleashed with variables, but it does require care.

The other feature you should be aware of with variables is that they are relatively expensive to maintain for the story engine. In particular it's can be expensive to search for top level variables in property matches. For example if you have:

Beforehand, ("husband")s "wife" is sick.

then the story engine actually goes away and checks everything on the level to see if it has a wife, and whether she's sick. With too many of these used in complicated combinations, eventually performance of the story engine will start to deteriorate (so if you do see performance problems, this might be why).

To ameliorate these effects, you can specify a tag inside the variable to help quickly narrow down the search. If you say

Beforehand, ("husband", a "male human")s "wife" is sick.

and assuming the game defines the tag "male human" specific to male humans on the level, then the search is done over just a tiny fraction of what would have been required before (because it only looks at male humans, rather than checking if every tree and bush has a wife, and if she's sick).

The problem doesn't manifest so obviously when variables are used in predicates, or as properties, because both of these can narrow down the search space automatically to some extent. You can still provide a tag if the game allows it to narrow things down even more. Two examples. Firstly if you say

("guard") was clicked on.

Then the story engine will ask the game for a list of things that might have been clicked on, and only test those, which offers a speed-up. Secondly if you say

"John"s ("item") is rusty.

Then the story engine will ask the game for a list of things that John owns, before it tests if any of them is rusty. Of course you could speed this up with

"John"s ("shovel", a "shovel") is rusty.

to find a rusty shovel that John has.

Strings

In everything that has happened so far, we've only talked about literal strings like 'Remember I saved the princess' as function arguments or in descriptions. It is possible to insert names and properties into strings using the interpolation brackets {}. An example will help

* "John": 'My name is { "John"s "name" } and I am from {"John"s "hometown"}'.

The brackets introduce a single name or property name which will be inserted into the text. This can be used in functions too, for example

Afterwards:
- Add to the journal 'I met { ("guard")s "name" } who promised to help me escape.'.

It's possible to use ~ as a quote character instead of ', for example, the previous could have been written:

Afterwards:
- Add to the journal ~I met { ("guard")s "name" } who promised to help me escape.~.

This is useful if you happen to need to use apostrophes and don't like doubling them up. Obviously you have to double ~ if you want one inside ~ quotes, for example

~His name's a secret, he's ~~50 years old~

FAQ

Why not just use XML? XML can do anything!

XML is a tree structured data format most naturally. Dialog "trees" may look like trees when you're clicking through them, but if you think about it they really aren't "trees" in the sense of building them. Certain elements reoccur, there are often loops, they may depend on external checks. The dialog graph is really directed and partially connected.

Of course, you can represent any graph structure you want in XML using some kind of reference system but you've lost any neat mapping with the "dialog tree". In fact, you could take the above proposals, wrap them up in lots and lots of angle brackets and have an XML input format. However, it must be remembered that making a dialog editor will take time, and making writers write XML is a sure way to get fewer writers and less progress. Hence the need for a more human friendly format.

Why not YAML? YAML can do anything!

YAML is a format for representing structured data. What will be your execution model for your YAML structure? Let's assume you can avoid verbosity and get something readable using YAML (it's just syntax - you'll still have to establish your conventions). Now what? Where will your predicates or postconditions be encoded, and how will they be encoded? Most likely you now hand roll a half-baked parser to deal with string values that encode expressions. You can put any kind of front end on the story engine; the parser is not the complicated bit. It just so happens this version is readable in combination with the powerful features we might want. If you can do better in YAML (you still have to represent all the same data, none of it is there for its sheer baroque glory) please do, and we'll use that.

Personal tools