A Twine story format with power!
- Markdown support in passages
- Embedded JavaScript support in passages
- Easy-to-use helpers for getting story data, linking to passages, showing passages inline, and much more
- Light and dark theme
- Global header and footer support
- Save and load progress
-
Launch Twine 2
-
Click on the Formats link in the Twine 2 sidebar
-
In the dialog that opens, click on the tab Add a New Format
-
Paste this URL into the text box and click the +Add button:
https://cdn.rawgit.com/massivedanger/protagonist/master/dist/format.js
If you need help, please check out the Usage information below. If there's still a problem, please email [email protected] or file an issue on GitHub.
Markdown parsing is provided via marked. It supports GitHub Flavored Markdown and no non-default options are set.
One addition to the Markdown parsing is support for Twine-style passage links with the
format of [[Link Text|Passage Name]]
or [[Link Text->Passage Name]]
. The alternate
[[Passage Name<-Link Text]]
format is not supported.
Also, standard Markdown links can transition players to new passages if they follow the
format of [Link Title Here](#passage:Passage Name)
. The link must point to #passage:
followed by the name of the passage.
All passages support embedded JavaScript logic. That means you can do things like this in your passage:
<% number = 1 %>
Hello! Your lucky number is <%= number %>
Anything within <%
and %>
is executed, but not shown in the browser. Anything within
<%=
and %>
is executed and then the result is shown to the user. To learn more
about how this works under the hood, check out lodash's template function.
Protagonist has built-in progress saving. To allow the use of the forward and back buttons of the browser and to change the window's title upon reaching certain passages, just add the "checkpoint" tag to any passage. You can also use it as a helper like this:
<% story.checkpoint('Name of the checkpoint goes here') %>
If you'd like to save all the progress, you'll need to call story.save()
in your
passage. As a convenience, you can specify a saveLink
somewhere. I recommended
in a header or footer passage (see Meta Passages for more info). To learn more about
the saveLink
, see Helpers.
To assist with common Twine usage, a set of default helpers are supplied that you can use with the embedded JavaScript support.
Access to the current passage's data, including:
Properties
story
(Story): Parent Story object of this passageelement
(jQuery element): jQuery representation of the source HTML elementid
(number): Twine ID number of the passagename
(string): Name of the passagetags
(array[string]): All tags added to the passagesource
(string): Raw HTML source
Functions
render()
(string): HTML output of the passage's Markdown source
Properties
element
(jQuery element): jQuery representation of the source HTML elementname
(string): Name of the storystartPassageID
(number): Twine ID number of the first passageIFID
(string): IFID of the storycreator
(string): Name of the software that output the storycreatorVersion
(string): Version of the creator softwarehistory
(array[number]): All previous passages visited by the player, by IDstate
(object): Custom story data set during playcurrentCheckpoint
(string): Name of the last checkpoint visited by the playeratCheckpoint
(boolean): True if currently at a checkpoint passageconfig
(object): Custom data set at the start of play, but can changepassages
(array[Passage]): All passages within the storyserialized
(string): JSON representation of the player's current progresssaveData
(object): Progress retrieved that has been saved previouslypreviousPassage
(Passage): The passage visited before the current onenextPassage
(Passage): The passage visited after the current onehelpers
(object): All the helpers available inside of a passage's context
Functions
getPassage(query:string|number)
(Passage): Retrieves the Passage object for a passage by either its name or ID numbergoToPassage(query:string|number)
(void): Navigates the player to a passage by either its name or ID numbershowPassage(query:string|number)
(string): Get a passage's rendered output by either its name or ID numbercheckpoint(name:string)
(void): Manually trigger a checkpoint and change the window's title to match thename
providedsave()
(boolean): Save the player's current progress to the browser's local storagerestore()
(boolean): Restore the player's saved progressreset()
(boolean): Reset the player's progress by removing all saved data
Store any player data inside of state
. What player data means is up to you, but
state
is provided as a convenience. It's attached to the parent story
object.
By default, this will be an object with just a darkTheme
attribute. However,
any configuration set in your CONFIG passage will be parsed and added to this.
The properties below are ones specific to the story format that will be handled in a special way by default.
Properties
darkTheme
(boolean): Whether or not the dark theme is turned on. Defaults to false for new stories.stylesheets
(array[string]): an array of URLs to stylesheets to be injected into the page before play begins.
Show a link that, when clicked, navigates the player to the chosen passage.
text
: what will be shown to the player as clickable textpassage
: either the name of the passage or its Twine ID number
<%= link('Go outside', 'Outside') %>
<%= link('Go to the passage with ID #2', 2) %>
Show a link that, when clicked, inserts a passage's content inline.
text
: what will be shown to the player as clickable textpassage
: either the name of the passage or its Twine ID number
<%= showLink('Look outside', 'Outside') %>
<%= showLink('Look at passage ID #2', 2) %>
Show a passage's rendered content inline.
passage
: either the name of the passage or its Twine ID number
<%= show('Outside') %>
<%= show(2) %>
Navigate the player to a passage.
passage
: either the name of the passage or its Twine ID number
<%= goTo('Outside') %>
<%= goTo(2) %>
Show a random item from an array of items. For simple usage, a variable number of arguments can be supplied and a random item will be chosen from them.
choices
: array of items to choose from
<%= random(['thing 1', 'thing 2', 'thing 3']) %>
<%= random('thing 1', 'thing 2', 'thing 3') %>
Show a random number based on a minimum and a maximum. Optionally, it can be a floating point number.
min
: the minimum numbermax
: the maximum numberfloating
: whether or not the chosen number should be floating point
<%= randomNumber(0, 100) %>
<%= randomNumber(0, 1, true) %>
Toggles the HEADER meta passage's display
Toggles the FOOTER meta passage's display
Toggles whether the dark theme is on or off
Show a link that, when clicked, saves the player's progress.
text
: what will be shown to the player as clickable text
<%= saveLink('[save]') %>
Show a link that, when clicked, restores the player's progress.
text
: what will be shown to the player as clickable text
<%= restoreLink('[restore]') %>
Show a link that, when clicked, takes the player back a passage.
text
: what will be shown to the player as clickable text
<%= backLink('GO BACK!') %>
Show a link that, when clicked, takes the player forward a passage if they've gone back from it.
text
: what will be shown to the player as clickable text
<%= forwardLink('Go back to the future. Or forward. One of those.') %>
In addition to these custom helpers, a few third-party libraries are available within a passage's JavaScript.
A global header and footer passage can be shown at the top and bottom of your story.
All you have to do is create a passage named HEADER
or FOOTER
and they'll be
displayed (at the top and bottom respectively).
A passage named CONFIG
can contain any valid TOML
and it'll be parsed and stored within story.config
, which can be accessed from
any passage. This is recommended for any data that won't change or for anything
that all passages will need right from the start.
To aid with organization of JavaScript and CSS, any passage tagged with javascript
is
loaded when the story is played and any passages tagged with stylesheet
are loaded as
global styles when the story is played.
- Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
- Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
- Fork the project.
- Start a feature/bugfix branch.
- Commit and push until you are happy with your contribution.
- Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
- Try not to change the version number in your commits. If you want to have your own version, that's fine. Just have the version change be a separate commit I can cherry-pick around.
- Chris Klimas for Twine and Snowman
- Javy Gwaltney for showing me the power of Twine