This module is technically outside of the scope of the Narrative Engine, as it deals with content that is handled within your application code, but narrative content is so common that a useful syntax and parser is included here.
Example syntax:
You shout at {$.name}{| again}.
"{What do you want?|Why do you keep bothering me?|Leave me alone!}"
{$.suspicious>3 & WARRANT.location=PLAYER?"Stop!---You're under arrest!"|"Never mind."}
The syntax is inspired by Ink (https://github.com/inkle/ink/blob/master/Documentation/WritingWithInk.md#6-variable-text).
The door creaks open, and you see... --- Nothing at all!
Breaks up your narrative into multiple parts (at ---
) for you to render as desired.
You have tried this {one|two|three|too many} times.
Chooses the option separated by |
corresponding to the cycleIndex
(zero-indexed) in the Config
. It repeats the final option after exhausting segments by default, or loops repeatedly with {~a|b|c}
syntax or chooses a random index each time with {?a|b|c}
syntax.
The random index is based on the cycleIndex
and the length of the trigger word and is only quasi random.
Empty segments are allowed - {|a||b|}
has three empty segments, beginning, middle and end.
You feel{PLAYER.happy_level>5? happy| sad}.
You can use any query syntax from NarrativeEngine.Syntax.RuleParser.parseMatcher
. If it succeeds, it will show the text to the left of |
, if it fails, it will show the text to the right of the |
.
$
will be replaced with the trigger
in the Config
.
You can have multiple queries separated by &
before the ?
. Any white space after the ?
and around the |
will be considered part of the text to display.
You can leave out the |
and right side text if you don't want to show any alternative text if the query doesn't pass.
The stranger's name is {STRANGER.get_name}.
Uses propKeywords
in the Config
to attempt to run a function (in this case the function keyed with get_name
) with the entity ID. You can define any kind of function you want, from a simple property lookup (especially useful when using the Entity Component System pattern) to generating a list of items in the supplied location.
You can also use $.get_name
which will replace $
with the trigger
in Config
.
List String
A fully parsed string, split at the "continue" marks (---
).
{ cycleIndex : Basics.Int
, propKeywords : Dict String (String -> Result String String)
, worldModel : NarrativeEngine.Core.WorldModel.WorldModel a
, trigger : NarrativeEngine.Core.WorldModel.ID
}
Provides a context for the parser to process narrative correctly. By managing this in the client, this module can stay stateless and generic.
Includes the following keys:
cycleIndex
- an integer starting at 0 indicating which index of cycle text should be used (used for all cycles in the narrative string).
propKeywords
- a dictionary of valid keywords to match against, and the corresponding functions that will take an entity ID and return a string as a Result. If it returns an Err
, the match will fail.
worldModel
- the full world model. Used to query against for conditional text.
trigger
- an entity ID used to replace "$" in conditional text.
parse : Config a -> String -> Narrative
Parses a single string of narrative content, then splits for continues. Trims and filters out empty strings.
Unlike when parsing all rules or entities ahead of time, you should call this per each individual content each time you have a new narrative. This is because the parsing depends on the Config
's state at the time it is parsed and cannot be done ahead of time.
parseMany
is provided so that you can still check for any parsing errors upon your app initialization.
Note that this will always return a string. In the case of a parsing error, it will say "Error". If you have run parseMany
initially, then you will not run into this case.
parseMany : Dict String String -> Result NarrativeEngine.Syntax.Helpers.ParseErrors ()
Call this as soon as possible and deal with errors appropriately to ensure you will have no parsing errors later. You can display the errors with NarrativeEngine.Syntax.Helpers.parseErrorsView
.
The provided dictionary should have keys to identify the correlating narrative content values.
If everything parses correctly, you can disregard the resulting empty value, and run parse
on individual content when needed.