Undum Documentation: API Documentation

Contents

High Level Overview

Undum games area based around three concepts: Situations, Actions and Qualities.

Situations

A situation is a chunk of code that is responsible for adding content to the screen and responding to user interaction. All user interaction is performed by clicking links in the content.

Often a link will change the current situation, in which case another situation is loaded, can write to the screen, and can begin responding to user interaction. When a situation changes, all links that were previously available are removed, so that the player can't unfairly go back and try alternative options after committing to one. It is possible to override this behavior, see the section on 'Sticky', below.

There is always exactly one active situation. These situations, and the links between them, form the structure of the game.

Actions

A situation may offer the player a series of actions to perform. These actions are internal to that situation and normally do not cause the situation to change. Actions may output more content, including new links for the user to select.

Qualities

Qualities represent the current state of the character. Internally they are all numeric values, able to take on any decimal value, positive or negative. They have no meaning to Undum - they are given meaning by your code as you perform calculations or make decisions based on their value.

Qualities display themselves to the user through a formatting function, which can turn the number into any kind of indicator: a progress bar, a symbol, a word, an integer, and so on. So as far as the user is concerned, qualities can represent any kind of value.

Other Concepts

There are a handful of other elements to an Undum game, but they are very much in a supporting role. Quality groups allow you to display a set of qualities under a common heading; and character text is a short chunk of HTML that you can use to summarize a character's qualities, or to give hints.

HTML

All of Undum's output is HTML. This allows you to style your content in any way you choose. It also allows you to include rich media into your output, such as images, video or audio.

Display Content

All HTML output should be in a format we call "Display Content" - this has particular rules:

CSS Styles

Undum also uses some of HTML's capabilities to control its own behavior. In particular, it uses a series of CSS classes to control how content behaves.

class="transient"

The CSS class transient can be used to mark HTML content that should be removed when the user changes their situation. When a situation changes, Undum will go back and remove any links from the text (leaving the text that was in the link). Any HTML content that has the CSS class transient will be completely removed at this time. Undum uses a fading animation to show the user this is happening, to avoid the user seeing an abrupt disappearance but being unable to work out what was removed.

Any HTML tag can be marked as transient. It is most commonly used on a paragraph of text that gives the user a set of options. Undum is designed to allow game developers to produce beautiful narratives - you don't want that narrative littered by "You could do X, or you could do Y." statements. Mark this content as transient, and it will not form part of the permanent record.

class="sticky"

When you change situations, links in previous situations will be removed. This prevents the user backtracking and picking options that no longer apply. Sometimes you want links to be available for a longer time, however. In this case mark them with the CSS class sticky. Sticky only applies to links, so should only be added to <a> tags.

class="raw"

Links can also have the raw CSS class. This class prevents Undum from interpreting the link as an instruction to the game. If you want to add a link to an external resource, you can add this class to it. Note that raw links are still removed when you change situations, so if you want a raw link permanently available, you should also make it sticky.

For some URLs, Undum can guess that the link is supposed to be an external link, and will automatically add the raw class for you. This isn't perfect, however, and you are better off not relying on it. If you have a link that you don't want Undum to capture, always use the raw class.

class="once"

Although links will be removed when the situation changes, often you want to remove them before that, as a result of an action within the current situation. There is an API tool available to do that in your code.

For convenience, there is also the once CSS class. Adding this to a link means that the link will be removed as soon as it is clicked. This is mostly useful for action links, because a link that changes the situation will automatically cause previous links to disappear.

Note that once is smart about this removal. It removes all links to the same action, not just the link that was clicked. So if you have the same action available in two links in your content, both will be removed.

class="options"

The options class only works on an unordered list. The default CSS also styles this differently. The list items will be presented as options within a table of choices. On devices with a mouse or pointer, the rows will change color when they are hovered over. The player can click anywhere on the row and the first link that it contains will be executed.

This class is intended for smarter presentation of standard option blocks, if you don't want your choices to be embedded into the hypertext.

Note that if you use this style, the unordered list will automatically disappear after being clicked, regardless of whether it is marked as "transient".

Headings

In the default CSS for Undum, the only heading level expected in the text is <h1>. You can use other headings, but you'll have to create your own CSS styles. One level of heading is almost always enough (if not too much) for narrative works.

Types of Link

Undum captures the links you put into your display content, and uses them to control the flow of the game. There are three formats of link that Undum understands, plus raw links, which it ignores.

Situation Links

Situation links are of the form situation-id. They must have no additional punctuation. As we'll see below, situation identifiers consist of lower case Latin letters, hyphens and the digits 0-9 only.

Clicking a situation link changes the current situation.

Action Links

Action links are of the form ./action-id. As for situations, action identifiers consist of lower case Latin letters, the digits 0-9 and hyphens only.

Clicking an action link carries out the specified action in the current situation.

Combined Links

Combined links are of the form situation-id/action-id. They combine both the previous steps: they change to the given situation, and then they carry out the given action once there. They are rarely used.

It is possible to use the combined form to refer to an action in the current situation. Undum is smart enough to understand that it doesn't need to change situation in that case, so it is entirely equivalent to the action link. It is rarely used (because it is so much more verbose), but can be useful. For example, we might want a sticky link to always take the character into their study and drink a potion, having this sticky link point to bedroom/drink-potion, achieves this. If they are already in the bedroom, then Undum notices this and doesn't try to make them enter it again.

External Links

As described above, you can add any other links to external content in your display content, as long as you mark it with the raw CSS class. It is also highly recommended that you mark all such links as opening in a new window or tab (add the target="_blank" attribute to the <a> tag). If you link the player to another page, and it replaces the Undum page, then for most browsers, all progress will be lost. Chrome appears to keep the progress, so that if you hit the back button you will be where you left off. Other browsers reset the game to the last saved location, or back to the beginning if the game hasn't been saved.

Link Data

This section describes a feature that is planned, or in development. It may not be functional in the current version of the code.

Undum links are allowed to add query data, e.g. ./action-id?foo=1&bar=2. This extra data is URL-encoded content which will be parsed and passed back to your code. For situation links the data will be passed into the old situation's exit handler, and the new situation's enter handler. For action links, the data will be passed into the situation's act handler. For combined links the data will be passed into both sets of handlers.

Raw links, as usual, do not have any processing performed. If they contain query data, it will be passed on to their target as it would for any link in any HTML document.

File Structure

An Undum game consists of a single HTML file. This imports the game engine and any supporting files. It also provides the structure needed for Undum to place content correctly on-screen.

The Undum engine consists of a single Javascript file, undum.js. In order to work, however, it needs supporting files. It requires the jQuery library (http://jquery.com) to be already imported, and it requires a game definition file to be imported after it.

The normal pattern of imports in the HTML file is therefore:

<script type="text/javascript" src="media/js/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="media/js/undum.js"></script>
<script type="text/javascript" src="media/js/mygame.game.js"></script>

By convention, the Javascript files are held in a media/js directory, but this is not enforced, you can arrange these files as you like, as long as you update the references to match.

In addition to the Javascript files, Undum expects the HTML file that imports it to have a particular structure. Although there is a good deal of flexibility, if you need it, you should start with the HTML file that is provided.

Finally, the HTML file will include a CSS file that controls the look and feel of the page. There are some elements in the CSS file which are used by Undum, and so, as for the HTML page, you should start with the CSS files provided. In most cases you will be able to leave the CSS file untouched, or else just tweak colors and image imports to match your game's style.

The HTML File

The sample HTML file provided shows the key points to edit. They are:

The Game Definition File

The game definition file where you create your game. To define a game you must provide the following data:

In addition it is very common to provide the following data:

And finally, there are a range of other data you can provide:

Identifiers

Identifiers are small snippets of text that allow you to refer to something in Undum. There are two types of identifier. The Game identifier represents your whole game, and has its own particular requirements (described below).

Lots of other things in Undum have identifiers (Situations, Actions, Qualities, Quality Groups), and they all have the same requirements. These identifiers must consist of Latin lower-case letters (i.e. a-z, no accents), hyphens, and the digits 0-9 only.

Game ID

The game identifier should be a string that is unlikely to be used in other games. You could use a UUID (my preference), or you could use a variation on your email mygame-myname@mydomain.com, or a URL you control http://mydomain.com/undum-games/mygame. If and when Undum games end up being re-distributable (as I hope they will), having a universally unique identifier will mean that saved games don't clash.

As stated previously, the game identifier doesn't have the same requirements as any other identifier in Undum, you can use any characters to make up your game ID.

Version Number

The version number is a string of text that indicates what version of the content is in the file. There is no set format for this version text, so you can use any scheme that suits you. I have used the "major.minor" approach.

Like the Game ID, this value is used to make sure that saved games don't clash. If you change you content, then previous saved games may not work correctly. By updating the version number, you allow the player to be notified of this directly, rather than suffering a silent crash. The effect of this is that you don't need to update the version number if you make a change to something that doesn't alter the structure of the game. If you just change some text, for example, or add an extra action that merely outputs a piece of description.

Situation List

Situations are defined in a Javascript object, placed in the undum.game.situations property. Situations in the object use their situation identifier as a property name, and a Situation object as its value. For example:

undum.game.situations = {
    doorway: new Situation(...),
    lobby: new SimpleSituation(...),
    lobby-upstairs-closed: new MyCustomSituation(...),
    ... etc ...
};

The situation objects are described more fully below.

Starting Situation

This is placed in the undum.game.start property. It should consist of the situation identifier as a string. It must be given.

Qualities

Qualities don't have to be displayed to the user. They can just function as internal properties for use by your code. Qualities that will never be displayed don't need to be declared, you can just set them when you need them (we'll look at the API for setting qualities below).

Often you want the quality to be displayed, however, and so you need to tell Undum to display the quality, and how it should be formatted. The QualityDefinition object does that. Any quality that doesn't have a corresponding quality definition will be invisible to the player.

Quality definitions are placed in an object in the undum.game.qualities property. Within this object, the property names are the quality identifiers, and the values they contain are QualityDefinition objects. For example:

undum.game.qualities = {
    investigation: new IntegerQuality("Investigation", ...),
    "found-mr-black": new OnOffQuality("Found Mr. Black", ...),
    ... etc ...
};

There are a number of QualityDefinition constructors defined which automatically format in common ways, you can also define your own and take complete control of the output. We'll return to discussion of the API, below.

Quality Groups

Qualities can be assigned to groups, and displayed under a common heading in the character panel. To define groups, place an object in the undum.game.qualityGroups property. This object should have properties which are the group identifiers, and values that are QualityGroup objects. For example:

undum.game.qualityGroups = {
    stats: new QualityGroup(...),
    clues: new QualityGroup(...),
    equipment: new QualityGroup(...)
}

A common source of puzzlement is that you don't use this data structure to define which qualities belong in which group. Instead, each quality that you want to assign to a group, should be given the identifier of the group it belongs to. So your undum.game.qualities property will look something like:

undum.game.qualities = {
    investigation: new IntegerQuality("Investigation", {group:'stats'}),
    "found-mr-black": new OnOffQuality("Found Mr. Black", {group:'clues'}),
    ... etc ...
};

Again, see the API documentation below for more details about what you can pass into these constructors.

Initialization Function

Your initialization function should be placed in the undum.game.init property. Normally its job is to configure the character at the start of the game. For example:

undum.game.init = function(character, system) {
    character.qualities.investigating = 0;
    character.qualities.money = 100;
    ... etc ...
    system.setCharacterText(...);
};

Initialization functions can, but normally doesn't, output text. Instead the starting situation is tasked with outputting the initial content.

Global Event Functions

Most of the time you want Situations to handle user interaction. Occasionally, however, you have to handle something that spans situations. It would be inconvenient to duplicate the same code in every situation. So Undum provides a set of hooks for you to respond globally to user interaction. There are five of these: enter, afterEnter, exit, beforeAction and afterAction. You can define functions in your game file using the properties: undum.game.enter, undum.game.afterEnter, undum.game.exit, undum.game.beforeAction, and undum.game.afterAction.

The enter, afterEnter, and exit functions look like this:

undum.game.enter = function(character, system, from, to) {
    ...
};

where from and to are the identifiers of the situations being left and entered, respectively. And the beforeAction and afterAction functions look like this:

undum.game.beforeAction = function(character, system, situation, action) {
    ...
};

where situation is the current situation identifier, and action is the identifier of the action being carried out.

These functions intentionally look like the enter, exit and act methods of the Situation object. These are described in more detail in the API reference, below.

API Reference

This section describes the types of object available to your game code.

Character

The character is created for you, but is passed into most of the functions that you define. It consists of an object with no methods and two properties:

qualities

The qualities object maps quality identifiers to their current value. Your code finds the current value associated with a quality by reading from this object, for example:

var gold = character.qualities.gold;

To set a quality, you have two choices. If you know the quality you want to set will not appear in the user interface, then you can set it directly:

character.qualities.gold += 1;

If it does appear on-screen, then this approach will mean that the character panel doesn't update, and the screen will be out of sync with the actual value. Instead it is better to use the System method setQuality, which also updates the UI:

system.setQuality('gold', character.qualities.gold+1);

It is fine to use setQuality if the quality isn't visible, making this the preferred option for all quality modifications.

sandbox

Not every bit of data you want to associate with a character fits nicely into a quality. The sandbox is a general storage space for any further data you want to store in the course of your game, for example:

character.sandbox.roomsSearched.push('bathroom');

Sandbox data is never visible to the user, so you can use any data structures you like here, to any depth.

System

The system represents the interface between your code and the user-interface. You don't create your own System object, it is passed into your code.

write(content, elementSelector)

Writes new content to the main flow of the story. The content you pass in must be either valid DOM elements already, or else be a string containing text in Display Content format.

The elementSelector is optional. If you provide it, then the new content will be added after the DOM element in the document that matches the selector. This allows you to do out-of-order addition of content. Simply add a paragraph with an id in your game, then later you can give this id as a selector to write, and the new content will be inserted immediately following that paragraph, regardless of how much extra content has been added since that point. If no selector is given then #content is used, i.e. the content is added at the end of the document. The writeBefore method inserts content at the start of the document, or before a selector.

The story will scroll to the start of the insertion point. If you do not wish to animate this scrolling, but just jump right there, you can switch off jQuery's animation system by adding jQuery.fx.off=true to your initialization code. This is particularly useful when debugging.

writeBefore(content, elementSelector)

Writes content into the story. This method is identical to write, above, except that the content is written at the start of the story, or if a selector is given, inserted before the matching element. On browsers that support it, the story will be scrolled to the insertion point.

doLink(URL)

Carries out the action associated with the given URL, as if it had been the href of a HTML link that the user clicked. This allows you to procedurally change situation and carry out actions from your code.

rnd

This holds a general purpose random number generator. It is an object derived from the Random prototype, so see Random below for details on its API.

time

This is a numeric value holding the current time, in seconds, since the player began playing the game. This value is correctly propagated across saves, so it is the only way you should track timing. In particular you should never call new Date() and use that value to determine the outcome of any event. You can use the current date to display the current date, for example, but not to control what actions or situations are available. See the section on Loading and Saving for more details of why this is important.

setQuality(qualityId, newValue)

Sets the character's given quality to the given value. This function also updates the character panel, animating the change in value if that is necessary. Do not directly set quality values in the character, because the user-interface will not detect and reflect those changes.

animateQuality(qualityId, newValue, options)

Like setQuality, this function changes the current value of the given quality. In addition, however, it displays a progress bar that shows to the user how the value has changed. The options parameter should be an object containing options for how the bar should display. The available options are:

setCharacterText(content)

Sets the block of character text that appears in the character panel. As for the write method, this text should be either valid DOM elements, or a string meeting the Display Content requirements.

clearLinks(URL)

Call this function with an Undum link URL (e.g. ballroom, or ballroom/open-cabinet). It will remove all occurrences of that link from the page. This is equivalent to what happens when you change situation, or when you click a link marked with the once CSS class. It allows you to control what options are available dynamically, from your code.

Random

The Random object provides a set of tools for simple random number generation, in a way that is guaranteed to work with the Loading and Saving functionality in Undum. An instance of Random is provided in the rnd property of the System object, so you will never need to create your own. It has the following methods:

random()

Generates a random number between 0 and 1, where 0 is inclusive, and 1 is exclusive. You can use this to check against known probabilities, such as:

if (system.rnd.random() > 0.5) {
    ...
}

To check for a 50/50 chance.

randomInt(min, max)

Return a random number between the given two values, where both values are inclusive. So randomInt(2,3) generates either 2 or 3.

dice(n, dx, plus)

Rolls n_ dice with _dx sides and adds plus to the result. This allows you to easily get results for rolls from regular RPG-style games, such as 3d6+2. The plus parameter may be negative or positive.

aveDice(n, plus)

Rolls n_ averaging dice, and adds _plus to the result. Averaging dice are a special type of d6 with sides marked [2,3,3,4,4,5]. They represent the fact that most people are fairly average, and results should not lie at the extremes.

diceString(definition)

Rolls dice according to the given definition string. The string should be of the form xdy+z, where the x component and z component are optional. This rolls x dice of with y sides, and adds z to the result, the z component can also be negative: xdy-z. The y component can be either a number of sides, or can be the special values 'F', for a fudge die (with 3 sides, +,0,-), '%' for a 100 sided die, or 'A' for an averaging die (with sides 2,3,3,4,4,5).

Situation

The Situation object is the prototype of all the situations in your game. It can be used directly, or through its more common derived type, SimpleSituation. The base Situation gives you maximum flexibility, but SimpleSituation provides more functionality and can produce terser code.

new Situation(options)

Creates a new situation. The options array can specify your implementation of any or all of the three functions that control the behavior of a situation: i.e. enter, act, or exit. This allows you to easily create situations that override certain behaviors with code such as:

Situation({
    enter: function(character, system, from) {
        ... your implementation ...
    }
});

See below for information on enter, exit and act.

Note that the methods defined in the Situation object are never called by your code. They are methods that you implement so that Undum can call them to respond to user interaction.

enter(character, system, from)

This is called when Undum enters a situation. The character and system are instances of Character and System as described above. The from parameter is a string containing the situation identifier for the situation that we're arriving from.

This method is the most commonly overridden. It is commonly used to describe the current situation (by sending content to system.write()) and to update the character (by calling system.setQuality() or by changing data in the character's sandbox object)..

exit(character, system, to)

This method takes the same character and system parameters as enter. Its third parameter, to, is a string containing the identifier of the situation we're exiting to.

act(character, system, action)

This method again takes the same character and system parameters as before. Its third parameter is a string containing the action identifier corresponding to the link the player clicked. It is common to use an if statement or a switch to query this action identifier and decide what to do accordingly. For situations in which many different actions are possible, consider using the SimpleSituation prototype, which provides this switching behavior for you.

SimpleSituation

This prototype builds on the basic Situation, providing tools to make it easy to output content in the enter method, and to switch between different functions depending on the action identifier passed into the act method. The exit method of SimpleSituation is exactly as for the base type Situation.

new SimpleSituation(content, options)

Creates a new simple situation that will display the given content when its enter method is called. The given options array provides further control of the behavior of this type. Valid options are:

An example SimpleSituation definition might be:

new SimpleSituation(
    "<p>...content...</p>",
    {
        heading: "Title",
        actions: {
            listen: function(character, system, action) {
                if (character.qualities.hearing > 5) {
                    system.write("<p>You hear a tinkling inside.</p>");
                } else {
                    system.write("<p>You hear nothing.</p>");
                }
            },
            search: "<p>You find nothing.</p>"
        }
    }
);

notice how the listen function is responsible for its own output, where the search property is a string in Display Content format, ready for output.

Functions in SimpleSituation

Both the content and the heading of a simple situation can be provided either as plain text, or as a function. If you provide a function, then it will be called with no arguments, and it should return a string to use for the output. This enables SimpleSituation to be used with other formatting and templating systems.

QualityDefinition

Quality definitions tell Undum how and where to display a quality in the character panel. Each quality definition has one method, format, which is responsible for converting a numeric quality value into a displayable quantity.

You define your qualities in your undum.game.qualities property.

new QualityDefinition(title, options)

Creates a new QualityDefinition. It is rare to call this constructor yourself, most often one of the derived types of QualityDefinition are used. They are defined below.

The title should be a string, and can contain HTML. It is used to label the quality in the character panel. It can be any string, it doesn't have to be in Display Content format.

Options are passed in in the options parameter. The following options are available.

format(character, value)

This is called by Undum to get a human readable string representing the given quality value for the given character. The method may return an empty string if the value has no need to be displayed, or it may return null if the quantity itself shouldn't be displayed. The difference here is significant. If your QualityDefinition returns the empty string, then the quality will appear in the character panel, but it will have no value marked. If it returns null, then it will be removed from the character panel entirely.

Most commonly the character parameter is ignored, but in your own derived types you can take advantage of being able to access other information about the character.

You may call this function yourself, but there is commonly no need. It will be called by Undum any time the corresponding quality needs to be displayed.

IntegerQuality

This is a derived type of QualityDefinition that displays the quality value by rounding it down to the nearest integer. This is ideal for most numeric statistics.

NonZeroIntegerQuality

This is a derived type of IntegerQuality that only displays its value when it is non-zero. If it is non-zero then it formats in the same way as IntegerQuality. Whereas IntegerQuality whould show zero values as '0', this type of quality displays nothing.

NumericQuality

This is a derived type of QualityDefinition that displays the quality value directly, as a full floating point value.

WordScaleQuality

Sometimes you want qualities displayed in words rather than numbers. This is a derived type of QualityDefinition that allows you to define words corresponding to possible quality values.

new WordScaleQuality(title, words, options)

The title parameter is exactly as for QualityDefinition.

The words parameter determines what words will be used. It should be an array of strings. By default the first string will be used to represent a value of zero (after rounding down), and the second string a value of 1, and so on to the end of the array. Values outside the array are treated differently depending on the value of useBonuses in the options parameter.

The options parameter supports the same three options as QualityDefinition. It also takes the following additional parameters:

FudgeAdjectivesQuality

This is a derived type of WordScaleQuality that doesn't require you to specify the words you wish to use. It uses the word scale from the Fudge RPG: "terrible", "poor", "mediocre", "fair", "good", "great" and "superb".

new FudgeAdjectivesQuality(title, options)

The parameters title and options are as for the WordScaleQuality constructor. The offset option defaults to -3, however (in WordScaleQuality it defaults to 0), making "fair" the display value for 0.

OnOffQuality

An OnOffQuality returns null from its format method (i.e. removes itself from the character panel) when the corresponding quality value is zero. Otherwise it returns the empty string (i.e. it is shown in the panel, but doesn't have a value label). See QualityDisplay.format above for more details on this distinction.

new OnOffQuality(title, options)

The constructor for this type is the same as for QualityDefinition from which it is derived. It accepts one extra option:

YesNoQuality

A YesNoQuality displays one of two strings depending whether its value is zero or not.

new YesNoQuality(title, options)

The constructor for this type is the same as for QualityDefinition from which it is derived. It accepts two extra options:

QualityGroup

A quality group defines a set of qualities that should be displayed together in the character panel, under an optional subheading. You could use quality groups to distinguish between qualities representing a character's innate abilities and their equipment, for example.

You define your quality groups in your undum.game.qualityGroups property.

new QualityGroup(title, options)

Constructs a new quality group that will have the given title for a subheading. The title may be null, indicating that this group does not need a heading.

The options parameter should be an object with the given optional parameters:

Loading and Saving

There is no API for you to manually access loading and saving functionality. The load and save functions are triggered by the buttons on the user interface. But the way loading and saving work does have a big impact on the way you write your code.

Undum makes a big feature of the fact that you can scroll back through the story you've helped create. To save your character's progress you would have to save not only the current situation and the character's qualities, but also all the text that has been generated up to that point. This would not be a problem in a downloaded game, because your hard-drive is plenty big enough. Unfortunately in a browser game, there is a limit on how much data you can store. Worse, this data limit is per-domain. So if you hosted 10 Undum games on a server, the saved games from all ten would have to fit into the limited space. This makes it impractical to save the complete text history.

Instead Undum saves all the actions you've taken as a player. It doesn't save the character's qualities or any other information, just the actions. It expects to be able to rebuild the complete text, and the complete current state of the character, by playing back these actions when the file is loaded. We call this determinism, because the current state of the character and the content needs to be determined from just the actions they take.

This means that you must take care to write all your code in a way that will generate exactly the same results if the game was played again in the same way.

There are two major limitations to this, firstly it means you can't use random events, and secondly you can't use timed events. Both of these are such serious limitations that Undum provides ways around them.

Undum provides your code with a random number generator in System.rng, which is an object with the prototype Random. This random number generator works with the loading and saving code to make sure that, when you load a saved game, the exact same random number requests will produce the exact same result. This means you get all the benefits of randomness (i.e. two separate games may have different results for the same action), but we can always replay a game exactly. Dealing with random actions is a difficult issue in testing any interactive fiction game. This approach solves that problem. You should, therefore, never use Javascript's built-in random number generate (Math.random), only ever use the one provided by Undum.

Finally, Undum also provides your code timing information in System.time, which is the number of seconds (and fractional seconds) elapsed since the player began the game. You can use this timing information to implement puzzles in your game (such as asking the player to complete a series of tasks in a specified amount of time). This timing system coordinates with the loading and saving system to make sure that, when you save and load the game, the timing picks up where it left off. Feel free to make decisions in your code based on the current value of System.time, but never use Javascript's Date object.

If you do not follow these restrictions, then it is likely that saved games will not load correctly. A player may save their game at one point and find most of their progress wiped out when they load it again.

Translation and Internationalization

Undum provides support for translations and internationalization. In particular, if you feel you want to translate Undum, then a lot of work has already been done for you. I am very grateful to Oreolek for this assistance. He translated the Russian version within days of the code being released, and advised on the tools that would make translation easier.

To write a game in another language, you need only to write the game content in that language. The identifiers you use in the game (to represent situations, actions, qualities and quality groups) must use only unaccented lower case latin letters and numbers, but the text you generate can contain any content you choose. Including right to left content or ideographs.

Undum itself has a small number of error messages and pieces of text that it uses. These include the default names for the FudgeAdjectivesQuality values. These strings are all found at the end of the undum.js file. They can be overridden. Simply define a new language file for your language (e.g. lang/gk.js) and override the appropriate strings. In your HTML file, after importing undum.js, import you strings file. For example, the end of the Russian translation of the tutorial (tutorial.ru.html) has:

<script type="text/javascript" src="media/js/undum.js"></script>
<script type="text/javascript" src="media/js/lang/ru.js"></script>

These translation strings are given as an object mapping the string name to the translated strings. This object is given as part of the undum.translation object, with a property name equal to the language name. So, for example, the UK English translation might begin:

undum.lanuage["en-UK"] = {
    no_group_definition: "Couldn't find a group definition for {id}.",

Within the translation strings, data to be incorporated later is given in curly braces.

Language Codes

Undum uses a simplified version of the IETF standards for language code. For our purposes this consists of three parts, only the first of which is required: language-Script-REGION where language is a two letter lower-case ISO language code, Script is a four letter title-case script identifier, and REGION is a two letter country or region code, all capitalized. The script is omitted when it is the default script for a language and locale. You would specify a script if you were using romaji (Latin letters) to write Japanese, but not if you were writing it in Kanji and kana.

The major virtue of this standard is that it allows fall through when a translation is not available. For example a translation into Brazillian Portuguese pt-BR might be different to one into Angolan Portuguese pt-AO, but there may be some strings they have in common. This allows a translator to create a base file for just plain Portuguese pt, then each country's dialect can define its own file that just overrides a few of the strings.

Filename Conventions

It is only a convention, but for all files that occur in language specific versions, I have used the filename convention of placing the language code before the extension, e.g. filename.lang.extension. The game file is similar filename.game.lang.js. You are free to use any format you choose, of course, but if you want to contribute back to the trunk, please follow this convention, to save having to rename things and connect up the links later.

API

The translation system provides a simple API based on the Globalite package of Nicolas Merouze (the implementation is unique to Undum). It adds a l() method to the Javascript String prototype. This method has the signature l(data), where data can be any object mapping strings to other strings.

The method attempts to figure out what the current language is by looking at the lang attribute of the top level HTML tag (you'll notice in the tutorial games this is defined in all cases, you should do the same). It then tries to find a matching object containing translations in undum.language. If it finds such an object, then it looks up the original string in that object to find a translation. It then merges any data passed into the l method into the string, before returning the result.

The translation look-up honors the IETF rules for language fallback, so (continuing the previous example) if your game file is in Brazilian Portuguese pt-BR, and a translation isn't found, then generic Portuguese translation pt is also checked. Finally, if no valid translation is found, then the default version is used. Since Undum was written in English, this default version is the English version. This is purely by my convenience, and isn't part of the IETF spec!

Changelog

2011-05-27

2011-08-18