jschomay/elm-narrative-engine/1.0.1

NOTE: this package is not compatible with Elm 0.19.1

Story

Entry point to the framework, where the client passes off all the story information.

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
-> Elements a b c
-> (d -> Scene a b c d e)
-> Setup a b c d e
-> 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 elements and scenes.

main : Program Never
main =
    Story.load info elements scenes 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 Setup a b c d e =
{ startingScene : d
, startingLocation : b
, startingNarration : String
, setupCommands : ChangeWorldCommands a b c d e
}

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

setup : Setup MyItem MyLocation MyCharacter MyScene MyKnowledge
setup =
    { startingScene = Beginning
    , startingLocation = Home
    , startingNarration = "Home sweet home..."
    , setupCommands =
        [ addLocation Conservatory
        , addCharacter John Conservatory
        , addProp Umbrella Home
        ]
    }

Defining your story world

type alias Elements a b c =
Elements a b c

The items, locations, and characters in your story that will be displayed and interacted with.

(a -> ItemInfo)
-> (b -> LocationInfo)
-> (c -> CharacterInfo)
-> Elements a b c

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

type alias ItemInfo =
ItemInfo

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

type alias LocationInfo =
LocationInfo

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

type alias CharacterInfo =
CharacterInfo

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

itemInfo : String -> String -> ItemInfo
itemInfo "Umbrella" "My trusty umbrella, I take it everywhere."
locationInfo : String -> Color -> String -> LocationInfo
locationInfo "Home" Color.Green "Home sweet home..."
characterInfo : String -> String -> CharacterInfo
characterInfo "Harry" "My good friend Harry..."
item : a -> Element a b c

Wrap your item type in an Element type

location : b -> Element a b c

Wrap your location type in an Element type

character : c -> Element a b c

Wrap your character type in an Element type

Defining story rules

Rules are declarative pairings of a "matcher" and a set of "commands" to perform if the rule matches. Rules are grouped into "scenes" for better control and organization. If no rules match, the framework will perform a default rule, which is usually just to narrate the description of what was interacted with.

type alias Scene a b c d e =
Scene a b c d e

A list of declarative rules, describing how to advance your story.

You build up rules via a declarative DSL like this:

storyRules =
    [ interactingWith (item Umbrella)
        `when` (unless (withItem Umbrella))
        `changesWorld` [ addInventory Umbrella ]
        `narrates` "I always take my umbrella with me."
    , interactingWith (item Umbrella)
        `when` (inLocation Swamp)
        `changesWorld` [ ]
        `narrates` "Good thing I brought my umbrella!"
    ...
    ]
Element a b c
-> Condition a b c e
-> AdvanceStory a b c d e
-> Rule a b c d e

Part of the rule building DSL. See the example above. firstInteractionWith is interchangeable with interactingWith, and will only match on the first interaction with the give story element.

Element a b c
-> Condition a b c e
-> AdvanceStory a b c d e
-> Rule a b c d e

Part of the rule building DSL. See the example above.

(Condition a b c e -> AdvanceStory a b c d e -> Rule a b c d e)
-> Condition a b c e
-> AdvanceStory a b c d e
-> Rule a b c d e

Part of the rule building DSL. See the example above.

(AdvanceStory a b c d e -> Rule a b c d e)
-> ChangeWorldCommands a b c d e
-> Narration
-> Rule a b c d e

Part of the rule building DSL. See the example above.

narrates : (Narration -> Rule a b c d e) -> String -> Rule a b c d e

Part of the rule building DSL. See the example above.

Matching rules

The following condition matchers can be used in the when part of the DSL. Note that you can nest and combine these matchers with unless, all, and any.

everyTime : Condition a b c e

Will match every time. Useful in combination with FirstInteractionWith (though you may want to restrict that more). Also useful when not supplying any commands to change the world, but you do want a custom narration.

withItem : a -> Condition a b c e

Will match if the supplied item is in the inventory.

nearCharacter : c -> Condition a b c e

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

nearProp : a -> Condition a b c e

Will match if the supplied item in in the current location.

inLocation : b -> Condition a b c e

Will match when the supplied location is the current location.

withKnowledge : e -> Condition a b c e

Will match if the specified knowledge has been acquired.

all : List (Condition a b c e) -> Condition a b c e

Will match if all of the supplied conditions match.

any : List (Condition a b c e) -> Condition a b c e

Will match if any of the supplied conditions match.

unless : Condition a b c e -> Condition a b c e

Will 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, as well as "narration" to accompany these changes.

moveTo : b -> ChangeWorldCommand a b c d e

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.

moveTo Conservatory
addLocation : b -> ChangeWorldCommand a b c d e

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).

addLocation Conservatory
removeLocation : b -> ChangeWorldCommand a b c d e

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.

removeLocation Home
addInventory : a -> ChangeWorldCommand a b c d e

Adds an item to your inventory.

addInventory Umbrella
removeInventory : a -> ChangeWorldCommand a b c d e

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.

removeInventory Umbrella
addCharacter : c -> b -> ChangeWorldCommand a b c d e

Adds a character to a location. (Use moveTo to move yourself between locations.)

addCharacter John Conservatory
removeCharacter : c -> b -> ChangeWorldCommand a b c d e

Removes a character from a location. The character will not show up anywhere else until you add it to another location. (Use moveTo to move yourself between locations.)

removeCharacter John Conservatory
addProp : a -> b -> ChangeWorldCommand a b c d e

"Props" are just items in a location instead of in your inventory. They could be something fixed to the location, such as a painting on a wall that you can look at but not take, or they can be something that you can take into your inventory.

This command adds an item to a location.

addProp Umbrella Home
removeProp : a -> b -> ChangeWorldCommand a b c d e

Removes an item from a location.

removeProp Umbrella Home
addKnowledge : e -> ChangeWorldCommand a b c d e

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, displayable story element 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
loadScene : d -> ChangeWorldCommand a b c d e

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.

type MyScene = CallToAction | SearchForClues | CatchSuspect | etc...


loadScene SearchForClues
endStory : ChangeWorldCommand a b c d e

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.