nitasema  nitasema

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)