§25   Extending and redefining the world model

A circulating library in a town is as an ever-green tree of diabolical knowledge! It blossoms through the year!
— R. B. Sheridan (1751–1816), The Rivals

In The History of Zork, Tim Anderson summed up the genesis of the ‘Zork I’ world model – and perhaps also its exodus, that is, its adaptation to other games – when he commented that “the general problem always remained: anything that changes the world you're modelling changes practically everything in the world you're modelling.” Substantial changes to the world model often have profound implications and can lead to endless headaches in play-testing if not thought through. Even a single object can upset the plausibility of the whole: a spray-can, for instance, unless its use is carefully circumscribed. On the other hand, introducing whole categories of objects often causes no difficulty at all, if they do not upset existing ideas such as that of door, location, container and so on. For instance, the set of collectable Tarot cards in the game ‘Curses’ have numerous rules governing their behaviour, but never cause the basic rules of play to alter for other items or places.

· · · · ·

In making such an extension the natural strategy is simply to define a new class of objects, and to take advantage of Inform's message system to make designing such objects as easy and flexible as possible. For example, suppose we need a class of Egyptian magical amulets, small arrowhead-like totems worn on the wrist and made of semi-precious minerals, with cartoon-like carvings. (The Ashmolean Museum, Oxford, has an extensive collection.) Each amulet is to have the power (but only if worn) to cast a different spell. Almost all of the code for this will go into a class definition called Amulet. This means that

if (noun ofclass Amulet) ...

provides a convenient test to see if an object noun is an amulet, and so forth. (This imposes a restriction that an object can't start or stop being an amulet in the course of play, because class membership is forever. If this restriction were unacceptable, a new attribute would need to be created instead, in the same way that the standard world model recognises any object with the attribute container as a container, rather than having a Container class.)

Suppose the requirement is that the player should be able to type “cast jasper amulet”, which would work so long as the jasper amulet were being worn. It seems sensible to create an action called Cast, and this necessitates creating an action subroutine to deal with it:

[ CastSub;
  "Nothing happens.";
];
Verb 'cast' 'invoke' * noun -> Cast;

Nothing happens here because the code is kept with the Amulet class instead:

Class Amulet
  with amulet_spell "Nothing happens.",
       before [ destination;
           Cast:
               if (self hasnt worn)
                   "The amulet rattles loosely in your hand.";
               destination = self.amulet_spell();
               switch (destination) {
                   false: "Nothing happens.";
                   true: ;
                   default: print "Osiris summons you to...^";
                       PlayerTo(destination);
               }
               rtrue;
       ],
  has  clothing;

Thus every Amulet provides an amulet_spell message, which answers the question “you have been cast: what happens now?” The reply is either false, meaning nothing has happened; true, meaning that something did happen; or else an object or room to teleport the player to.

From the designer's point of view, once the above extension has been made, amulets are easy to create and have legible code. Here are four example spells:

amulet_spell "The spell fizzles out with a dull phut! sound.",
amulet_spell [;
    if (location == thedark) {
        give real_location light;
        "There is a burst of magical light!";
    }
],
amulet_spell HiddenVault,
amulet_spell [;
    return random(LeadRoom, SilverRoom, GoldRoom);
],

An elaborate library extension will end up defining many classes, grammar, actions and verb definitions, and these may neatly be packaged up into an Include file and to be placed among the library files.

▲▲ Such a file should contain the directive System_file;, as then other designers will be able to Replace routines from it, just as with the rest of the library.

· · · · ·

So much for extending the Inform model with new classes: the rest of the section is about modifying what's ordinarily there. The simplest change, but often all that's needed, is to change a few of the standard responses called “library messages”, such as the “Nothing is on sale.” which ends to be printed when the player asks to buy something, or the “Taken.” when something is picked up. (To change every message, and with it the language of the game, see §34.)

To set new library messages, provide a special object called LibraryMessages, which must be defined between the inclusion of the "Parser.h" and "Verblib.h" library files. This object should have just one property, a before rule. For example:

Object LibraryMessages
  with before [;
           Jump: if (real_location ofclass ISS_Module)
               "You jump and float helplessly for a while in zero
               gravity here on the International Space Station.";
           SwitchOn:
               if (lm_n == 3) {
                   "You power up ", (the) lm_o, ".";
               }
       ];

This object is never visible in the game, but its before rule is consulted before any message is printed: if it returns false, the standard message is printed; if true, then nothing is printed, as it's assumed that this has already happened.

The Jump action only ever prints one message (usually “You jump on the spot.”), but more elaborate actions such as SwitchOn have several, and Take has thirteen. The library's variable lm_n holds the message number, which counts upwards from 1. In some cases, the object being talked about is held in lm_o. The messages and numbers are given in §A4. New message numbers may possibly be added in future, but old ones will not be renumbered.

An especially useful library message to change is the prompt, normally set to "^>" (new-line followed by >). This is printed under the action Prompt (actually a fake action existing for this very purpose). You can use this to make the game's prompt context-sensitive, or to remove the new-line from before the prompt.

EXERCISE 59
Infocom's game ‘The Witness’ has the prompt “What should you, the detective, do next?” on turn one and “What next?” subsequently. Implement this.

LibraryMessages can also be used as a flexible way to alter the rules governing individual actions. Here are two examples in the guise of exercises.

EXERCISE 60
Under the standard world model (¶6.7.4 in §24 above), a player standing on top of something is not allowed to type, say, “east” to leave the room: the message “You'll have to get off… first” is printed instead. Change this.

EXERCISE 61
Under standard rules (¶6.6.1 in §24 above), a player trying to “push” something which is not static or scenery or animate will find that “Nothing obvious happens”. Add the rule that an attempt to push a switchable item is to be considered as an attempt to switch it on, if it's off, and vice versa. (This might be useful if a game has many buttons and levers.)

· · · · ·

The Library is itself written in Inform, and with experience it's not too hard to alter it if need be. But to edit and change the library files themselves is an inconvenience and an inelegant way to carry on, because it would lead to needing a separate copy of all the files for each project you work on. Because of this, Inform allows you to Replace any routine or routines of your choice from the library, giving a definition of your own which is to be used instead of the one in the standard files. For example, if the directive

Replace BurnSub;

is placed in your file before the library files are included, Inform ignores the definition of BurnSub in the library files. You then have to define a routine called BurnSub yourself: looking in the library file "Verblib.h", the original turns out to be tiny:

[ BurnSub; L__M(##Burn,1,noun); ];

All this does is to print out library message number 1 for Burn, the somewhat preachy “This dangerous act would achieve little.” You could instead write a fuller BurnSub providing for a new concept of an object being “on fire”.

▲▲ Inform even allows you to Replace “hardware” functions like random or parent, which would normally be translated directly to machine opcodes. This is even more “at your own risk” than ordinary usages of Replace.

· · · · ·

What are the implications of fire likely to be? One way to find out is to read through the world model (§24) and see how fire ought to affect each group of rules. Evidently we have just created a new possible internal state for an object, which means a new rule under ¶1, but it doesn't stop there:

1.4.4. Some objects are “flammable” and therefore in one of two mutually exclusive states, “on fire” and “not on fire”.

2.4. If an object on fire is placed in a flammable container or on a flammable supporter, that too catches fire.

2.5. If a container or supporter is on fire, any flammable object within or on top of it catches fire.

4.3.3.1. Any object on fire provides light.

4.4.3. The player cannot touch any object on fire unless (say) wearing the asbestos gloves.

5.4.1. All flammable objects have a “lifespan”, a length of time for which they can burn before being consumed. The each-turn daemon subtracts one from the lifespan of any object on fire, and removes it from play if the lifespan reaches zero.

One could go further than this: arguably, certain rooms should also be flammable, so that an object on fire which is dropped there would set the room ablaze; and the player should not survive long in a burning room; and we have not even provided a way to douse the flames, except by waiting for fires to burn themselves out. But the above rules will maintain a reasonable level of plausibility. ¶1.4.4 is provided by defining a new class of Flammable objects, which contains an each_turn routine implementing ¶5.4.1, and an on_fire attribute. The same each_turn can take care of ¶2.4 and ¶2.5, and can give the object light if it's currently on_fire, thus solving ¶4.3.3.1. But, while it would be easy to add simple rules like “you can't take an object which is on fire”, ¶4.4.3 in its fullest form is more problematic, and means replacing the ObjectIsUntouchable routine. Giving any object on fire a before rule preventing the player from using any of the “touchy” actions on it would go some of the way, but wouldn't handle subtler cases, like a player not being allowed to reach for something through a burning hoop. Nor is this everything: burning objects will need to be talked about differently when alight, and this will call for using the powerful descriptive features in Chapter IV.

REFERENCES
‘Balances’ implements the ‘Enchanter’ trilogy's magic system by methods like the above.   Approximately seventy library extensions have been contributed by members of the Inform community and more are placed at ftp.gmd.de with each month that goes by. Often short and legible, they make good examples of Inform coding even if you don't want to use them. Many are cited in “references” paragraphs throughout the book: here are others which seem more appropriate here.   "money.h", by Erik Hetzner, is a textbook case of a class-based extension to Inform providing a new aspect to the world model which doesn't much overlap with what's already there: notes and coinage.   Conversely, Marnie Parker's "OutOfRch.h" exemplifies a change that needs to permeate the existing world model to be effective: it defines which areas of a location are within reach from which other areas, so that for instance a player sitting on a chair might only be able to reach items on the adjacent table and not a window on the far wall.   In some graphical adventure games, interactivity sometimes cuts out at a significant event and an unchangeable movie animation is shown instead: this is sometimes called a “cut-scene”. Such games sometimes allow the player to replay any movies seen so far, reviewing them for clues missed previously. "movie.h", by L. Ross Raszewski, projects the textual version of movies like this, thus providing a framework for cut-scenes.   "infotake.h", by Joe Merical, shifts the Inform model world back to the style of ‘Zork’: printing Zorkesque messages, providing a “diagnose” verb and so on.   Anson Turner's "animalib" retains the core algorithms of the Inform library (principally the parser and list-writer) but redesigns the superstructure of properties and attributes with the aim of a cleaner, more consistent world model. Although this alternative library is in its early stages of development, its code makes interesting reading. For instance, some child-objects represent items held inside their parents and others represent items on top of their parents. The standard Inform library distinguishes these cases by looking at the attributes of the parent-object – whether it is a supporter or a container. Contrariwise, "animalib" distinguishes them by looking at attributes of the child, so that the different children of a single parent can all be contained in different ways.