Vorple for Parchment: How it works

March 1st, 2012

Parchment’s method of displaying (Z-machine) story text is relatively straightforward. There’s a HTML element where all story text is added to as the story progresses.

Program flow with Parchment: Take input, parse command, display output

What Vorple does is that it treats the element where Parchment outputs story text as a buffer. The #parchment container is hidden and a #vorple container takes its role on the screen. Parchment takes user input, processes it, puts the output in the container and triggers a jQuery event Vorple is listening to. Vorple reacts to the event and pulls the buffer’s contents for further processing. Finally it displays the buffer contents in the appropriate place and clears the buffer for the next turn. Thanks to this technique Parchment itself requires very little to no modifications as it’s blissfully unaware of its UI being overridden.

By using the buffer the story text can be displayed wherever needed, not necessarily just in the scrollback. Each turn and each command are wrapped in elements that contain loads of semantical information, including the type of the command that triggered the response (parser error or normal action), the order of the turns, whether or not the command was later undone…

Program flow with Vorple: Parchment output is put into a buffer and moved to the screen through Vorple

The story file tells Vorple of what type the current turn is by calling the vorple.parchment.setTurnType() method at any time during the turn. If the player’s command was invalid, the story file sends vorple.parchment.setTurnType('error') along with the parser error. Now Vorple will add a parserError class to the turn’s container so that it knows to fold it away after the next turn. The author can also extend the feature with additional turn types.

Eval and HTML streams

Z-machine uses two to four streams, depending on the version. Stream 1, for example, is the story text output. Dannii Willis’s Z-machine 1.2 spec adds stream 5, the JavaScript eval stream, which lets the story file give JavaScript instructions to the interpreter. Essentially whatever the interpreter finds coming from stream 5 it evaluates as JavaScript. (Big thanks to Dannii who has also made an implementation of the eval stream in Parchment. Since the feature was ready-made, it cut down Vorple’s development time by months, and makes the story files compatible with other interpreters which I initially thought was not possible or feasible.)

In addition to the JavaScript stream, there has to be a way to add HTML elements inside the story text so that text can be manipulated and styled as necessary. The preview adds stream 6 which accomplishes this, although it’s not an optimal solution by any means: by sending for example string div transient info to stream 6 the interpreter understands the first word as the element’s name and the rest of the string as its classes and closes the tag immediately. The example string would become element <div class="transient info"></div>. Content to the element can be added with the JavaScript stream.

The JavaScript can be any valid JavaScript (at least at this point) or Vorple functions. Authors can also make their own story-specific Vorple modules, as demonstrated with this module used by the preview story’s alarm clock.

As mentioned in the previous post’s comments, the Glk Stylesheets specification determines how things will look like in the future. The Z-machine implementation should match the Glk specification as closely as possible, or at least the Inform syntax should be identical. This also means that Vorple will not support Glulx until Parchment has native support for Glk stylesheets.

Here’s an example of everything that the story file sends to the interpreter when we type X THERMOMETER for the first time in the preview story. Black text is stream 1 (normal story text), red text is stream 5 (JavaScript code), and blue text is stream 6 (HTML elements).

The thermometer shows that the outside temperature is span id795559607242 temperature $(".id795559607242").text("16 degrees Celcius");. Being a highly sophisticated device it also shows the wind speed, span id297333836310 windspeed $(".id297333836310").text("4 m/s");.

div id180186239553 transient info thermo-info $(".id180186239553").text("Type IMPERIAL to switch to imperial units and METRIC to switch back to metric units."); $(".thermo-info").html( $(".thermo-info").html().replace( /(imperial)/, "<a href=\"imperial\" class=\"command\">imperial</a>") ); $(".thermo-info").html( $(".thermo-info").html().replace( /(metric)/, "<a href=\"metric\" class=\"command\">metric</a>") );

Here the units are wrapped inside <span> elements, or more accurately the text is inserted in the created elements. Later the command IMPERIAL can change the content of these elements to match the new units.

In the last paragraph, which is first put into an element that colors and italicizes the text, the commands IMPERIAL and METRIC are made into clickable links. The links are interpreted as commands just as if the player would have typed them in with the keyboard. (The way how the text is turned into links is rather clunky and specific to the preview, mostly because of the HTML stream implementation’s limitations.)

The resulting HTML code (ignoring Parchment’s default markup) is:

The thermometer shows that the outside temperature is <span class="id795559607242 temperature">16 degrees Celcius</span>. Being a highly sophisticated device it also shows the wind speed, <span class="id297333836310 windspeed">4 m/s</span>.

<div class="id180186239553 transient info thermo-info">Type <a href="imperial" class="command">IMPERIAL</a> to switch to imperial units and <a href="imperial" class="command">METRIC</a> to switch back to metric units.</div>

Undum authors might recognize the transient class which is lifted straight from Undum and behaves identically: the text fades out at the start of the next turn.

On screen it looks like this:

An image of text displayed in the interpreter

Backwards compatibility is achieved by the story file checking whether the interpreter supports the new streams. If not, the story can supply alternative text-only content or just not display anything.

Next we’ll take a peek at how all this rather technical stuff looks like in Inform 7 code.


§ 3 Responses to Vorple for Parchment: How it works"

  • Dannii says:

    Loving these blog posts. I think I need to start a Parchment blog too.

    Are you listening for the stream event on #parchment?

    I like the Glk Stylesheets plan a lot, so will probably switch ZVM to use it in the future. But I don’t like Glk’s window model, so I want to keep the StructIO idea around too. Maybe I’ll request some Glk selectors for StructIO.

    • Juhana says:

      A Parchment blog would be great!

      I’m only interested in knowing when the turn has ended, so I added a TurnComplete event trigger at the end of getLine().

  • Dannii says:

    I’m keen to add more hooks, but I don’t know what will be really useful, and I don’t want to add ones that won’t be used because they’ll just slow everything down.

    I’ve given you selector $31 and stream 6, so you’re good to go now!