Coder Social home page Coder Social logo

ludonarrative / storyassembler Goto Github PK

View Code? Open in Web Editor NEW
52.0 10.0 3.0 12.97 MB

StoryAssembler: a dynamic choice-based narrative generation engine

License: Other

HTML 8.75% JavaScript 88.57% CSS 2.67%
narrative-generation narrative narrative-game

storyassembler's Introduction

StoryAssembler

A narrative system for procedurally generating choice-based interactive narratives!

  • Powered jointly by an HTN (hierarchical task network) planner and forward-state planner.
  • Text and choices are dynamically assembled from pre-authored fragments that have effects.
  • Narratives are driven by lists of states which are reached by fragment effects.
  • Text templating is also available to make narratives dynamic on a word-by-word basis.

How to Run (Mac)

  1. Start up Terminal
  2. Navigate to the StoryAssembler root directory
  3. Type command to start up a Python webserver: python -m SimpleHTTPServer
  4. Start a web browser and go to http://localhost:8000

How to Run (Windows)

  1. If you don't have Python, download it (2 or 3 is fine)
  2. Start up Command
  3. Navigate to the StoryAssembler root directory
  4. Type command to start a webserver:
  • Python 3: python -m http.server
  • Python 2: python -m SimpleHttPServer
  1. Start a web browser and go to http://localhost:8000

Example Scene

A simple example scene's files are located in

  • js/StoryAssembler/data/scene-configs (the config file)
  • js/StoryAssembler/data/scene-content (the content file)

How Do I Start Writing?

All the tutorials, walkthroughs, and technical explanations live in the wiki

Why Would I Want To Write With This Thing?

StoryAssembler's good for creating quality-based narratives. In general it's good if you want to write a story where any of these are true:

  • most choices are gated by certain conditions
  • how the story progresses is dynamically determined via state
  • you want game variables from a concurrent game to influence the direction of the story or choice availability

The dynamic templating system means it's also good if:

  • you want to write stories where different characters take on story roles
  • individual qualities of characters influence choices or structure of the story

Who's Used This Thing?

A version of this system was used to drive the narrative component of Emma's Journey, a generative game about climate change created by UC Santa Cruz.

I Heard There's A Cool Narrative Viz For This Thing!

There's a viz for an older version of this library here, but because it still has some pernicious bugs, we're not including it in the library (for now).

storyassembler's People

Contributors

logodaedalus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

storyassembler's Issues

Re-architecture: get rid of require.js / streamline includes

The whole "mapping a json file to a string alias and then putting that alias in an array of strings at the top of Coordinator in the proper order" is a total nightmare.

We might be able to do away with this and re-factor it using ES6 classes (probably most hygienic way?)

Key requirements:

  1. Doesn't introduce weird loading bugs for long file parsing, etc (test with large scenes)
  2. Doesn't require you to type strings in multiple places in the correct order.

Formalize state cleanup between scenes

Currently there are lots of state variables from playthrough initialization (scenes, etc) that don't get cleared out or reset between scenes. We need to formalize our approach and add in that functionality (was custom-coded in Emma's Journey and then ripped out when we pulled the library out).

  1. We should add in syntax for state variables to persist between scenes or even between playthroughs
  2. What should be the default? What is the least likely to cause subtle bugs that are hard for authors to figure out?

refactor so non-determinism assembly is exposed as config

Currently StoryAssembler picks the first item in the list of valid items returned, which makes it 100% deterministic (which is good for debugging).

Factor it out though, so it can be exposed as a scene config. It's an easy way to make the system way more dynamic.

De-couple scene id needed for "begin" link from text entered on scene description

Currently, if you want to start a scene in timeline view, you have to make sure in the scene's config field "descriptionText" you have a link like this:

<a href='#' class='beginScene' id='begin-testScene2'>Begin Scene</a>

At the very least, we should only require that a link with 'beginScene' class is in it...setting the id of the scene to begin off the text value of the link's id (Display.js:606) is...horrible.

Wishlists can have longer length than actual length??

To reproduce:

  1. Play through a full scene (Example2) by clicking the scene link (not Begin)
  2. Return to main menu

In populateKnobs() when it checks length of wishlist for if (sceneSpec.wishlist[x].condition.includes("[")) the length is 19 but there's only 14 entries???

refactor dynamic scene selection to be more user-friendly

This currently lives in Coordinator.getNextScene()

Right now you'd have to manually insert your logic in there on a per-project basis. It'd be better if:

  1. We establish as a convention that State variables with the "sys_" prefix are for StoryAssembler (add to documentation)
  2. We automatically set "sys_nextScene" in StoryAssembler code to the scene id of the next one in the array before running any fragments (by default it goes to the next one in the list)
  3. In Coordinator.getNextScene(), change it so that it uses that value for next scene
  4. Put in docs that the way you conditionally control that is by setting "sys_nextScene"

textClickFuncs should be moved to its own file

Currently textClickFuncs lives ~line 295 in Display.js, and contains all the click functions bound to shimmer wishlist text. Instead it should live in its own file.

We can keep the syntax of {shimmer|shimmerFunctionName|initialValue} inline for scene descriptions...that's pretty good.

Click functions should be bound in timeline view for shimmer text.

var setUISceneContent in Display.js is the function I'm primarily looking at...

...essentially it looks like a bunch of the timeline functions got mutated into graphView, then when graphView got ripped out it ripped out the stuff that made timeline stuff work.

Use the canonical version or go back in history of Display.js to figure out best way to re-factor this to preserve the stuff we want (shimmerText, possibly config knobs???) but not get all the other graph stuff.

Add onClick capability to templates

Currently hacked into Display.js, but render should be changed from returning just a string to returning
{ text: "some text", onClicks: [{id:"span1", onClick:function deedle() } ] }

and then appending the onClick functions to a placeholder in processTemplate().

Then at the very end, it should by default replace the text with .text property, but should check for onClicks and if the array has stuff, cycle through searching and add them to the elements?

Add Template to allow "choices" as in-line hyperlinks

Template should be written as _link/1/, which should display in that spot the display text of choice 1. Choice 1 should then be hidden from being shown, and all clickEvents registered to clicking that choice should instead fire when the hyperlink is clicked.

Expose max_depth as a Coordinator parameter

All the code is in there to route it correctly, we just don't mess with it. Update docs and tell people that setting it high can result in choices taking a long time, etc etc.

Re-enable automatic speaker selection

Essentially deactivated for now (if not present in config, defaults to monologue, which doesn't really do anything currently) but there's a deactivated StoryAssembler to automatically pick fragments based on who the speaker is as well. This feature was implemented back in...2017? 2016? Then it was abandoned and drifted into "deactivation-by-code-never-executed" over time. It used to have three different modes (narration, monologue, dialogue) and would prioritize fragments that allowed the normal exchange to continue.

Currently you'd need to add

"mode" : {			//what mode dialogue exchange takes place in for the scene
   "type": "dialogue",		//can be dialogue or monologue
   "initiator": "friend1",
   "responder": "protagonist"
},

to your config file, and make sure each fragment has a speaker.

This could probably also come back integrated in with the avatar stuff, as that seems to be where a lot of character logic-y stuff is now.

Clean up localstorage usage

Right now there are a lot of straggling localstorage/State keys that aren't being used. Once we split out the repo, need to remove the chaff.

Refactor hooks for dynamically changing wishlist items before scenes start

Currently there are two approaches:

Display.createKnobs() : makes HTML elements (sliders)
Display.populateKnobs() : actually put in values and binder functions based on dynamic wishlist DSL
Display.processWishlistSettings() : process knob values and do string replacements on dynamic wishlist items

Display.createShimmers() : makes HTML elements (shimmerSpan) and sets values in localStorage
Display.processWishlistShimmers() : takes localStorage values and does string replacements on dynamic wishlist items

I propose that we should re-factor so that we use a #2 approach, and for #1 roll createKnobs() and populateKnobs() together into createKnobs(), and change their function so that they're writing to localStorage. Then we use processWishlistShimmers() approach for finalizing in both cases. This way we can demonstrate two UI approaches to hooking into dynamic wishlists.

Refactor / consolidate per-scene data into one scene JSON file

Where the data live now

(getStorySpec)

id
year
characters (dictionary with props "name", "nickname", "gender")
wishlist (array)
dataFiles (array)
startState (string array)
UIvars (obj array, props: varName, label, characters, affectedBy, range)
mode (obj, props: type, initiator, responder) -this might only be used when we say we want to label speakers and that's buggy???

(loadNoPathFallback)

id
text (fallback text to display if scene can't continue but we're in release mode)

(loadSceneIntro)

id
text (intro text for each scene)

(loadBackground)

id
src (location of background image for scene)

(loadAvatars)

sceneId
characters
-id
-graphics (which graphics pack to use)
-age (actually just used to delineate graphics pack)
-states (state conditions paired with avatars for those conditions)

we should probably get rid of graphics and age, and instead use an explicit "src" prop in states with the full filename

re-architecture: Coordinator.startScene(), StoryAssembler.continueScene() should be abstracted

We need to further separate (MVC-style) the module dependencies so that all the stuff specific to our displays is done independently of the data structures of Coordinator, etc.

Ideally, it should probably go like:

  1. Coordinator.init()
  2. do stuff with data properties now set in Coordinator for scene title screens
  3. Coordinator.startScene(_Coordinator, playGameScenes[0], true); is called
  4. Some kind of JS object is returned from that
  5. kick off stuff with that, keying off StoryAssembler.handleChoiceSelection() ????

Use cases we need to handle:

  1. Our two display templates retaining all functionality
  2. It being as painless as possible to create new templates (piping in choices, etc)
  3. It being as painless as possible to run StoryAssembler headless, to facilitate the testing harness, data viz, and any other shenanigans.

Is that all for the demo?

Was expecting to see some cool dynamic story but instead got a dialog with "go left" or "go right" the end. Did I miss something here?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.