nitasema strives to be a versatile interactive fiction generator. it takes a script file and produces a self-contained html file that can be played in a web brower. nitasema has minimal dependencies and is fast. the name is Swahili and means "I will speak."
dependencies
- a C++11 compiler (default is
c++
) - libc
- libstdc++
building
to build, run make rel
, make dbg
, or ./make.sh
from the root directory. the only difference between the rel
and dbg
targets is that dbg
includes debugging symbols and doesn't strip the resulting binary. ./make.sh
is the same as running rel
, provided for systems without make installed.
file format
the nitasema *.n file format is based on simplified s-exps. it is designed primarily as an intermediate format for a forthcoming GUI editor but in the meantime it's perfectly intelligible to humans to and only slightly verbose.
lines can be commented out with the ;
character. there are currently no multiline comments.
whitespace is used to delimit values and is otherwise insignificant.
the data types are atoms, strings, and lists. strings are delimited by single-quotes ('
) and can span any number of lines. lists are delimited by [
and ]
(not the more usual parentheses, for ease of typing). atoms may contain any character in the ascii range '-' ~ 'z' | 'A' ~ 'Z' and any unicode character outside the ascii range. backslash (\\
) can escape single quotes in a string.
the first term in the *.n file must be a string naming the story. it may be followed by atoms to mark various flags.
flags
by
: sets the author. takes a string argument. currently has no effect on output. (by 'alexis hale'
)flags
: declares booleans. must be followed by a list of booleans. (flags [bloodsoaked screaming bleeding]
)vars
: declares variables. must be followed by a list of variables. (vars [health sanity terror]
)no-select
: disables text selection to make the story seem more app-like.start
: indicates the starting passage. takes an atom argument. required. (start the-beginning-of-the-end
)
passages
a [passage name (stylesheet) ...]
declaration creates a passage. a passage is a screenful of nodes; passages are rendered and displayed to the player once at a time and users can navigate from passage to passage with links. the first term must be the id of the passage as an atom. optionally a stylesheet name may follow. all following terms must be nodes.
[passage the-beginning-of-the-end doom-passage]
creates a passage with the id the-beginning-of-the-end
with stylesheet doom-passage
. ids are used only in the front end of the compiler and do not appear in the compiled html.
[passage the-beginning-of-the-end
'doom had at long last come to Las Vegas.'
]
creates a passage with the id the-beginning-of-the-end
and adds a text node. all macro nodes regardless of position are evaluated before the passage renders, though macros in conditionals will only be evaluated if the conditional evaluates correctly.
[passage the-beginning-of-the-end
[p 'doom had at long last come to Las Vegas.']
[p 'soon the mushrooms clouds of thermonuclear armageddon would envelop the globe.']
]
instead of creating text nodes, this example creates paragraph nodes. paragraph nodes are a better way to organize text.
to link to another passage, use the [link id 'title']
node.
[passage the-beginning-of-the-end
[p 'doom had at long last come to Las Vegas.']
[p 'soon the mushroom clouds of ' [link doooom 'thermonuclear armageddon'] ' would envelop the globe.']
]
to create a link to another passage that can only be clicked once (and will afterwards disappear forever), use action
[passage the-beginning-of-the-end
[p 'doom had at long last come to Las Vegas.']
[p 'soon the mushrooms clouds of thermonuclear armageddon would envelop the globe.']
[p [action make-tea 'make tea.']]
]
to set a flag, use the [set flag]
and [clear flag]
.
[passage doooom
[set world-destroyed]
[clear hankering-for-brunch]
[p 'welp looks like everyone\'s dead now. i hope you learned a'
'valuable lesson on the futility of existence.']
]
to test a flag, use the [flag? flag [#t-pred] ([#f-pred])]
node. #f-preds (else clauses) are optional.
[passage tea-with-aunt-holly
[flag? world-destroyed [
[p 'unfortunately, the world where both all tea in the universe and'
'aunt holly were located has been destroyed in thermonuclear'
'armageddon.']
[p 'better luck next time, sport!']
][
[clear hankering-for-brunch]
[p 'you enjoy a lovely evening sipping tea with aunt holly.']
[p 'you have been cured of <b>HANKERING FOR BRUNCH</b>!']
]]
]
a link can invoke a passage as a subroutine.
[passage bed
[p 'you are in bed.']
[sub inventory 'inventory']]
[passage bridge
[p 'you are on the bridge of the Unrelenting Fist of Endless Imperial Wrath.']
[p 'crew bustle about, trying to make a good impression for your inspection.']
[sub inventory 'inventory']]
[passage inventory
[p 'you have <b>HALF-EATEN FISH HEAD</b>, <b>HORRIFIC STENCH</b>, and <b>REGRETS</b>.']
[p [ret 'back']]]
an action can also invoke a subroutine, with the sub-action
node. its syntax is identical to action
.
to make nodes appear only once, and not on subsequent views, use once
[passage start
[once [[p 'you wake up and immediately wish you hadn\'t, wish you\'d never wake up again.']]]
[p 'you are in your office.']
[p 'the papers that need grading are still here.']
[once [
[p 'you pick one up, wondering if they could possibly be as bad as you remembered.']
[p 'they\'re not. they\'re worse.']
]]
]
to create an event that will interrupt the story only on the first click of a link, use gate
.
[passage home
[p 'you are home with a nice cup of tea.']
[p [gate fight-monster outside 'go outside.']]
]
[passage outside
[p 'you admire the splendour of the great outdoors.']
]
[passage fight-monster
[p 'a dreadful beast covered in the intestines of its victims bursts from the sewers!']
[p [ret 'kill monster with umbrella.']]
]
once
takes two lists of nodes, the second optional. the second will be displayed on any subsequent visits.
[passage offal-gardens
[once [
[p 'it is said the offal gardens of betelgeuse must be seen to be believed.']
[p 'well, they\'re not wrong.']
][
[p 'the offal gardens stretch away into the distance. you wish they didn\'t.']
]]
]
colors
colors may be set by the "colors" declaration. this overrides the default stylesheet.
colors [
bg '#121212'
]
the colors that may be changed are bg
, fg
, shadow
, link
, hover
, and shadow-hover
. any valid CSS color specification may be used. url()
can also be used to set a background image:
colors [
bg 'url(bg.png)'
]
stylesheets
the default stylesheet is set by the colors
declaration. different passages may have different stylesheets. stylesheets are declared as follows:
[style name
[global-rule 'expression']
[.link
[local-rule 'expression']
]
]
"rules" are simply css rules. [rule 'expression']
is translated to rule: expression;
at compile time.
anything in the global scope affects the entire passage. to set the background to slowly turn black on passages tagged "doom":
[style doom
[background 'black']
[transition '10s']
]
to style links, use the .link
selector. to style text, use the .text
selector.
to suppress the default stylesheet completely, use the no-style
declaration.
invocation
nitasema takes a filename on the commandline and prints its output to stdout
.
./nitasema input-file.n > output-file.html
the html file can be opened by any browser.
issues
- code needs major refactoring
- i wrote the entire thing in a 12-hour-long fugue and was up until 8am working on it, so there's bound to be lots wrong.
todo
- fully implement all node types
- implement other types of variable assignment besides =
- show line number & context on compile errors, not just parse errors
- add multiline comments
- change colors, stylesheet
- tags
- title screens
- gui??
- enable determining whether a passage was invoked as a subroutine
nitasema is developed by lexi hale and released under the AGPL license. (see LICENSE)