Dialog

Release notes for Dialog 0f/01, Library 0.23:

Version 0f of the language introduces a number of new features:

Closures

A closure is an anonymous bit of code that can be placed in a variable and invoked later. Closures are defined inside curly braces where a value is expected. For instance:

        ($X = { Hello, world! })

        %% Nothing is printed yet, but $X is bound to the code in braces.
        %% Use '(query $)' to invoke it:

        (query $X) %% This will print "Hello, world!"

Earlier, a typical story would contain something like this:

        (space 5) (bold) \*\*\* You win! \*\*\* (roman)
        (game over) %% Launch the game-over menu.

In a story with multiple endings, that boilerplate code could end up being repeated in several places. But from now on, the library contains an additional predicate, ‘(game over $)’, defined like this:

(game over $Message)
        (par)
        (space 5)
        (bold) \*\*\* (query $Message) \*\*\* (unstyle)
        (game over)

In the story code, all that remains to do is this:

(game over { You win! } )

A closure captures the environment surrounding its definition. This means that you have access to the same local variables both inside and outside of the brace-expression:

(perform [attack $Obj])
        The attack is unsuccessful.
        (game over { You've been eaten by (a $Obj) } )

Finally, closures may take a parameter, accessed through a variable called $_. To query a closure with a parameter, use the built-in predicate ‘(query $Closure $Parameter)’. Multiple parameters can be passed in the form of a list.

Recall that the standard library provides a family of predicates for parsing object names, such as:

(understand $Words as object $Obj preferably worn)

and

(understand $Words as object $Obj preferably takable)

The ‘preferably’ part primarily controls how the word ALL is interpreted. There is now a new variant of the predicate:

(understand $Words as object $Obj preferably $Closure)

The closure takes a candidate object as parameter. This allows you to specify your own arbitrary conditions, such as:

(understand $Words as object $Obj preferably {(item $_) ~(edible $_)})

Divisions and styles

A new ‘(div $) …’ syntax is introduced, to help separate content from presentation. Divisions are rectangular areas of text, usually spanning the full width of the interpreter window, to which style hints can be applied. At the moment, the only supported style hints in the main text window are bold, italics, and top and bottom margins. More hints are supported in the status bar area; see below.

(intro)
        (div @quote) {
                This could be displayed in italics, for instance, if
                the corresponding style hint is defined for the @quote
                class.
        }

The parameter of ‘(div $)’ is the name of a style class. These names don’t end up in the game dictionary (unless they’re also used elsewhere, of course).

Styling is specified using a small subset of CSS. All CSS attributes are regarded as optional styling hints, so a backend or interpreter can pick and choose among them, or ignore them altogether. This is what the syntax looks like:

(style class @quote)
        font-style: italic;
        margin-bottom: 2 em;

The purpose of divisions and style hints is to allow the same story to run on many different platforms, with varying support for advanced presentation techniques. This is a first step towards a future where Dialog stories can be played in a web browser with full CSS support. But, critically, I want Dialog stories written for the web to also be playable (with excellent performance) on vintage computer systems, and to work well will screen readers. That’s why the style attributes are defined separately from the text and logic of the story, and treated as optional hints.

This is not a total break with the past. It is still possible to set the style explicitly at any time, using e.g. ‘(bold)’ and ‘(italic)’. To get back to the default style of the surrounding div, use the new predicate ‘(unstyle)’ instead of hardcoding a switch to ‘(roman)’ type.

The old ‘(par $)’ built-in predicate has been removed. The same effect, if absolutely necessary, can be achieved with an empty div with the desired margin.

Major overhaul of the status bar functionality

The old low-level predicates for drawing into the status bar, by explicitly moving the cursor, have been removed from the language. Instead, the layout of the status bar is controlled via style attributes.

The status bar is now a special kind of div, and it may contain other divs in turn. These internal divs can have a fixed width (expressed in em units) or occupy a percentage of the width of the parent division. They can float towards the left or right end of the parent division. The height of the entire status bar is specified in ems. The height of a floating div is always 100% of the status bar, and its contents in turn can be arranged vertically using ordinary, non-floating divs.

This is an example of a simple status bar with a score display in the upper right corner:

(style class @status)
        height: 1 em;

(style class @score)
        width: 20 em;
        float: right;

(redraw status bar)
        (current score $S)
        (current room $R)
        (status bar @status) {
                (div @score) {
                        Score: $S
                }
                (room header $R)
        }

A new built-in predicate, ‘(progress bar $ of $)’, draws a progress bar scaled to fit the width of the current div. It is rendered with character graphics on the Z-machine backend.

Miscellaneous

The built-in predicate ‘(unbound $)’, which probably wasn’t used outside of the library, has been replaced by ‘(bound $)’ with opposite semantics. This has allowed an optimization to be implemented in a cleaner way inside the compiler.

Documentation

For more details of these new features, please check out the new Input and Output chapter in the manual, as well as the new section on Closures.

This release also includes bugfixes and performance improvements to the standard library and to the optimizing compiler.

5 Likes