jschomay/elm-narrative-engine/2.0.0

NOTE: this package is not compatible with Elm 0.19.1

Story

Main engine API, with functions for you to load your story and build rules.

The framework takes care of managing all of the state, views, and interaction handling, allowing the client code to focus purely on the story.

Loading the story

Info
-> World item location character
-> StartingState item location character knowledge
-> Program Never

This is where you load all of your story details into the framework (from the client's Main.elm file). See https://github.com/jschomay/elm-interactive-story-starter.git for an example of how to define interactables and scenes.

main : Program Never
main =
    Story.load info world setup
type alias Info =
{ title : String
, byline : String
, prologue : String
}

Basic information about the story, which gets displayed on the "title page" before beginning the story.

info : Info
info =
    { title = "The Continuing Adventures of Bartholomew Barrymore"
    , byline = "B. Barrymore"
    , prologue = "Mr. Barrymore is at it again, with more shenanigans in this comedy-mystery..."
    }
type alias StartingState item location character knowledge =
{ startingScene : List (Rule item location character knowledge)
, startingLocation : location
, startingNarration : String
, setupCommands :
List (ChangeWorldCommand item location character knowledge)
}

Information for the starting state of your story. See the "Changing the story world" section for more information on the setupCommands.

setup : StartingState MyItem MyLocation MyCharacter MyKnowledge
setup =
    { startingScene = beginning
    , startingLocation = Home
    , startingNarration = "Home sweet home..."
    , setupCommands =
        [ addLocation Conservatory
        , moveCharacter John Conservatory
        , placeItem Umbrella Home
        ]
    }

Defining your story world

(item -> ItemInfo)
-> (location -> LocationInfo)
-> (character -> CharacterInfo)
-> World item location character

A means of looking up static information about your story interactables, which gets loaded into Story.load.

type alias World item location character =
World item location character

A collection of all of the interactable elements in your story, for loading into the engine.

type alias ItemInfo =
ItemInfo
type alias LocationInfo =
LocationInfo
type alias CharacterInfo =
CharacterInfo
itemInfo : String -> String -> ItemInfo

Display information for your items, including a name and description. The description allows markdown.

itemInfo "Umbrella" "My trusty umbrella, I take it everywhere."
locationInfo : String -> Color -> String -> LocationInfo

Display information for your locations, including a name, a highlight color, and a description. The description allows markdown.

locationInfo "Home" Color.Green "Home sweet home..."
characterInfo : String -> String -> CharacterInfo

Display information for your characters, including a name and description. The description allows markdown.

characterInfo "Harry" "My good friend Harry..."

Defining story rules

Rules are how you progress the story. They are made up of conditions to match against and commands to perform if the rule matches. Rules are grouped into "scenes" for better control and organization. The engine will run through the active scene from the beginning, looking for the first matching rule, then run it. If no rules match, the framework will perform a default command, which is usually just to narrate the description of what was interacted with.

A rule has four parts:

  1. A matcher against what interactable story element the user clicked on
  2. A list of conditions that all must match for the rule to match
  3. A list of changes to make if the rule matches
  4. Narration to add to the story line if the rule matches (note that you can use markdown)scene1 : List (Story.Rule MyItem MyLocation MyCharacter MyKnowledge) scene1 = [ { interaction = withCharacter Harry , conditions = [ inLocation Garden ] , changes = [ moveCharacter Harry Marsh, addInventory NoteFromHarry ] , narration = [ "He gives you a note, then runs off.", "I wonder what he wants?" ] } , { interaction = withInventory NoteFromHarry , conditions = [] , changes = [ addLocation Marsh ] , narration = [ "It says, "*Meet me in the marsh.*"" ] } ]

When a rule matches multiple times (a player clicks the same story element multiple times), it will run through the list of narrations in order, one per click, repeating the final one when it reaches the end.

type alias Rule item location character knowledge =
Rule item location character knowledge

A declarative rule, describing how to advance your story and under what conditions.

Interaction matchers

The following interaction matchers can be used in the interaction part of the rule record.

type alias InteractionMatcher item location character =
InteractionMatcher item location character
withItem : item -> InteractionMatcher item location character

Will only match the interaction part of a story rule if the player interacted with the specified item.

withLocation : location -> InteractionMatcher item location character

Will only match the interaction part of a story rule if the player interacted with the specified location.

withCharacter : character -> InteractionMatcher item location character

Will only match the interaction part of a story rule if the player interacted with the specified character.

withAnything : InteractionMatcher item location character

Will match the interaction part of a story rule every time (be careful about the the order and conditions of your rules since this matcher is so broad).

withAnyItem : InteractionMatcher item location character

Will match the interaction part of a story rule if the player interacted with any item (be careful about the the order and conditions of your rules since this matcher is so broad).

withAnyLocation : InteractionMatcher item location character

Will match the interaction part of a story rule if the player interacted with any location (be careful about the the order and conditions of your rules since this matcher is so broad).

withAnyCharacter : InteractionMatcher item location character

Will match the interaction part of a story rule if the player interacted with any character (be careful about the the order and conditions of your rules since this matcher is so broad).

Conditions

The following condition matchers can be used in the conditions part of the rule record.

type alias Condition item location character knowledge =
Condition item location character knowledge
withInventory : item -> Condition item location character knowledge

Will only match if the supplied item is in the inventory.

nearCharacter : character -> Condition item location character knowledge

Will only match if the supplied character in in the current location.

nearItem : item -> Condition item location character knowledge

Will only match if the supplied item is in the current location. Ignores inventory.

inLocation : location -> Condition item location character knowledge

Will only match when the supplied location is the current location.

withKnowledge : knowledge -> Condition item location character knowledge

Will only match if the specified knowledge has been acquired.

Condition item location character knowledge
-> Condition item location character knowledge

Will only match if the supplied condition does NOT match.

Changing the story world

You cannot change the story directly, but you can supply "commands" describing how the story state should change.

type alias ChangeWorldCommand item location character knowledge =
ChangeWorldCommand item location character knowledge
moveTo : location -> ChangeWorldCommand item location character knowledge

Changes the current location. The current location will be highlighted in the list of known locations, and will also be displayed at the top of the page, highlighted in the color defined for that location. Any items or characters that are in the current location will also be shown for the player to interact with.

addLocation : location -> ChangeWorldCommand item location character knowledge

Adds a location to your list of known locations. Any location on this list is available for the player to click on at any time. This avoids clunky spatial navigation mechanics, but does mean that you will need to make rules to prevent against going to locations that are inaccessible (with appropriate narration).

removeLocation : location -> ChangeWorldCommand item location character knowledge

Removes a location from your list of known locations. You probably don't need this since once you know about a location you would always know about it, and trying to go to a location that is inaccessible for some reason could just give some narration telling why. But maybe you will find a good reason to use it.

addInventory : item -> ChangeWorldCommand item location character knowledge

Adds an item to your inventory (if it was previously in a location, it will be removed from there, as items can only be in one place at once).

removeInventory : item -> ChangeWorldCommand item location character knowledge

Removes an item from your inventory. The item will not show up anywhere until you add it back to your inventory or to a location.

character
-> location
-> ChangeWorldCommand item location character knowledge

Adds a character to a location, or moves a character to a different location (characters can only be in one location at a time, or off-screen). (Use moveTo to move yourself between locations.)

removeCharacter : character -> ChangeWorldCommand item location character knowledge

Moves a character "off-screen". The character will not show up in any locations until you use moveCharacter again.

item
-> location
-> ChangeWorldCommand item location character knowledge

Move an item to a location. If it was in another location or your inventory before, it will remove it from there, as items can only be in one place at once.

removeItem : item -> ChangeWorldCommand item location character knowledge

Moves an item "off-screen" (either from a location or the inventory). The item will not show up in any locations or inventory until you use placeItem or addInventory again.

addKnowledge : knowledge -> ChangeWorldCommand item location character knowledge

Knowledge is an intangible "flag" that you can match against in your rules. For example if you add knowledge of learning about a suspect, then going back to people you have already interacted with can give you new information about the suspect when you interact with them again. You can also use this for acquiring skills or bonuses or anything intangible that would not be displayed in the story. You could track your actions, such as if you were kind or mean to an important character in an earlier scene.

However, before turning to this tool, consider if you can use a normal, story interactable instead. For example, perhaps you get a sketch of the suspect in your inventory, which you can "show" to people for more information. This keeps the story more concrete.

type MyKnowledge = LearnOfSuspect | WrongSuspect | Amnesia


addKnowledge LearnOfSuspect
List (Rule item location character knowledge)
-> ChangeWorldCommand item location character knowledge

Rules are grouped into "scenes" for better organization and control. This is how you switch between scenes when you want a different rule set. You may want to switch scenes at a "turning point" in your story to bring about new rules for the next objective.

scene1 = [...rules here...]
scene2 = [...rules here...]


-- in the `changes` part of a rule in a scene1:
loadScene scene2
endStory : ChangeWorldCommand item location character knowledge

Let the framework know the story has ended. Currently this has no effect, I'm trying to figure out what should happen when stories end. You may want to make the rule load a special "storyFinished" scene that only has one withAnything interaction matcher so the player can't do anything else (unless you want to let them "wander").