This is the ALAN Manual, the reference documentation for the Alan IF Language System.
The current documentation is written in the AsciiDoc format thanks to substantial conversion work by Tristano Ajmone.
It can now be provided in HTML and PDF formats, with better presentation, like syntax highlighting of Alan code examples.
This document is subjected to the Artistic License 2.0 terms.
Introduction
Text adventures or, using a more appropriate term, interactive fiction, is a form of computer game which has many things in common with fiction in book form, role-playing games and puzzle-solving. To create a high quality interactive fiction game, you need to be more of an author than a programmer.
Alan is a special purpose computer language specifically designed to make it very easy to create such adventure games requiring only limited programming skills.
The main principle of the design of the language is simplicity. That is, it should be very easy to do common things, but it should also be possible to do more complicated things by constructs that are more complex. This means that wherever a construct is optional, the system supplies some sensible default.
The author and a very good friend designed the first crude version of the Alan language in 1985. During many years of incremental improvement and use, it has now reached its third major version. This means that the language has a sound foundation, based on practical use. Therefore, features have been added as experience has grown, from actual use and understanding of the most prioritised needs.
In this version, modern and novel object-orientation features have been incorporated into the language that allow definition of classes, instantiation and inheritance of attributes and other features. Do not worry if you find these terms incomprehensible at this point, Alan is still an easy language to use and by reading this manual, you will understand how these new features may aid you in your quest for adventures.
Programmer’s Pitch
Alan is an application-oriented language. It features constructs that are natural to an author of interactive fiction. Alan is a strictly typed, compiled, object-oriented language with single inheritance. Classes inherit properties from their super-classes. The class system allows polymorphism so that instances of subclasses are valid wherever a super-class is specified. There are no explicit type declarations, except for instances of classes; instead, types are automatically inferred from expressions such as integers, strings or instances of a particular class.
To the Reader
There are probably four major types of readers of this document:
-
Readers completely new to interactive fiction — read the whole document from the beginning.
-
Readers familiar with writing interactive fiction but new to Alan — read from Chapter 4 onwards.
-
Alan v2 users wanting to upgrade — you should read the separate document on conversion, then Chapter 4 and onwards, with frequent use of Part II as a reference while doing your conversion.
-
Alan v3 users looking for detailed answers — use the Index (PDF version only), look up the relevant sections in Part II but also glance through Part I from Chapter 4. Visit www.alanif.se for a collection of examples.
All readers are encouraged to share feedback on the documentation, particularly if you could not find the answer to what you were looking for by using the index, the table of content or skimming through what you thought might be relevant parts of the documentation.
You can contact the authors through the www.alanif.se website, and you can also enrol in the Alan IF mailing list, a virtual meeting place for new and seasoned Alan users alike!
Part I: Concepts
This chapter introduces the concepts used in the Alan language. You might already have a good idea about these things, especially if you are a seasoned adventure player, and perhaps even author.
But I would suggest that you read through it anyway since it introduces some important concepts that are specific to how Alan treats them.
1. What Is an Adventure?
As long as man has been around there have been stories, fairy tales and fantasies. In the early days, storytellers told their stories to silent and astonished audiences. After Gutenberg, the stories were printed and the readers partook in the fantasies of the author. In our days, passive viewers are fed from the silver screen or through the tube.
In our time, at last, there has evolved a way for the “audience” to take part in the story themselves. It started in the forties and fifties and continued to develop into the games today known as Dungeon and Dragons, Tunnels and Trolls, etc. Games where a game leader designs the story, but the players decide (and perform) the actions of the characters in the story.
These games, of course, have a computerized counterpart.
These games are played interacting with the computer. The program describes a scene or situation (usually in text, but pictures may also be used), the player decides on some action and gives orders to the computer to carry out his wishes. Usually there are objects to manipulate, traps to negotiate and puzzles to solve, the object being to find the hidden treasures or save the world.
Crowther & Woods started this form of games in the late sixties when they designed the famous Colossal Cave Adventure, which became available on many mainframe computer systems. Inspired by this, Lebling et al. (then at MIT) took a giant step forward in adventuring by creating the Great Underground Empire and making it available for venturing Adventurers in the game Dungeon. This game contained a much more developed story and could handle much more complex commands.
Later, Dave Lebling & Co started Infocom, a company where they continued to develop their technique, first with Zork I, II and III (the first a re-implementation of Dungeon, the others equally successful sequels). Since then, a host of games has been released (Starcross, Witness, Enchanter are some of the names that come to mind). Although the original authors are long scattered, the Infocom games are still highly appreciated even today.
Other companies have followed Infocom’s example and a handful of them seem to make a living out of creating adventure games. However, today most of the works are created by devoted people that do it for the fun of it, releasing their games as shareware or completely free.
There have been many attempts to use computer graphics to display the surroundings and objects in adventure games. Some of the more successful early examples are the Sierra games (notably the Leisure Suit Larry and the Kings Quest series) which had mouse oriented moves but also allowed single line text commands, games from ICOM Simulations (DejaVu and The Uninvited) which were purely graphics games with mouse and icon interfaces. Other manufacturers have tried to use (sometimes optional) pictures to accompany the text, for example Magnetic Scrolls games (e.g. The Pawn), which shift the picture automatically as you move around using the normal directional commands.
Currently, a community of addicted authors and players of text-based adventure games are still out there. Visit the vaults of interactive fiction on the Internet, and you will be surprised by the abundance of modern, high quality interactive fiction available.
The Alan Adventure Language has been designed to aid construction primarily of pure text adventures or, in the words of Infocom, interactive fiction. Some sound and graphics functions are also available to spice up your game if you so desire.
The main feature of adventures is the interaction between the player and the game through commands input via the keyboard and descriptions printed on the screen. In Appendix B you can find a sample of this type of interaction.
2. Elements of Adventures
The success of all Infocom games can probably be attributed to three distinctive features. First, they all have a ‘believable’ and consistent plot, which is flavoured with humour and wittiness. Second, the descriptions are extensive and give a lot of atmosphere to the game. Third, the command handler recognizes and understands a large vocabulary and complex input. Add to this the worlds best graphics device (the human brain) and you are unbeatable!
Looking at adventures in more detail, we can see some common features. There is always the world or universe (called the map) where the adventure is taking place. Although you can move around quite freely there are usually some problems getting into certain parts of the world (e.g. locked doors, no air to breathe or even finding the entrance). The size of the map ranges from hundreds of locations to just two or three, or even a single location.
Then, there are the objects in the game. These range from your tools, like lamps and shovels, to immaterial things like a hole in the ground; in short, anything you can manipulate. Ideally, everything that is mentioned in a description should be an object, but this is normally impossible because of storage limits (and perhaps the stamina of the games designer!).
Most objects have uses. You can easily guess how to use a key, but what about the velvet pillow? Red herring objects are also common in adventuring.
The player must be able to express his wishes. Complete understanding of natural language commands from the player is probably overkill, but single verb-object input is not sufficient for a good game either. The player must be able to say things like
> take all except the blue vase
or
> put the ring and the bag in the box
3. Alan Fundamentals
Alan is all about adventure games, or interactive fiction. In this manual, we will use both terms interchangeably since they convey two slightly different views on the purpose. But the technical platform, the Alan language and its support system, is the same, works the same and looks the same, regardless if you are designing a treasure hunt featuring an elaborate combat and hit point system or if you are competing with Sir William Shakespeare himself.
3.1. What Is a Language?
A computer language is usually described as a set of rules for textual instructions for a computer. The idea is that a computer can follow those rules and perform the necessary and/or intended actions.
The Alan Adventure Language is a high-level computer language designed to make it easy to create text adventures. This means that the language have been designed so that the textual instructions are relatively easy to read and write if you understand the mechanisms that adventures are made from. In addition, it requires only for minimal additional instructions to make those mechanisms work.
Compared to programming in a typical programming language, the Alan system handles most of the tiresome tasks and supplies reasonable defaults so that you can concentrate on the plot, the puzzles, the objects and the map. This makes Alan a true high-level computer language.
The Alan system consists of two computer programs, one of which analyses an input following (or at least intended to follow) the Alan language. This program is called the compiler and the analysis ensures that the input (the game description, in fact) makes sense. The compiler also, at the same time, converts the input into something more compact, the game file. This game file can be transferred and used without the compiler. Instead, to run an adventure the interpreter is needed. The interpreter is another program that reads the information in the game file, communicates with the player of the game (or reader of the work, if you like) and interprets all the complex mechanisms in your game logic so that it gives the player the illusion of the activities and events that you have designed.
To create works of interactive fiction using Alan, you also need a program with which to construct your Alan source code, a standard text editor, like Notepad or similar programs. However, you cannot use a word processor, like Microsoft Word, since the files created with those usually contains formatting information that the Alan compiler doesn’t understand.
There are also special editors, or additions to standard editors, available, which supports Alan coding and helps with formatting and even compiling and running your game.
You might wonder why the game is not a single executable program. The answer is simple, compare the game with a Word-document. In order for the document to be visible, you need the Word-program that reads the Word-file and displays the content on the screen. As you probably know, the same program does not run on all computers. For example, you cannot install Word for Windows on a Macintosh.
In view of this, it might be considered a nice thing that there are programs for Macintosh that read, display and print Word-documents. This makes the document files portable. Once you have a reading program on your computer, you can use all similar files on it. This is also one reason behind the compiler-interpreter design of Alan.
3.2. The Alan Idea
The Alan language does not focus on variables, subroutines or other traditional programming constructs, because Alan is not primarily a programming language. Instead, Alan takes a descriptive view of the concepts of adventure authoring. The Alan language contains constructs that make it possible for you, the author, to describe the various features of these concepts. By describing for example, how the locations in the adventure are connected you have described the geography in which the story will take place. Much of what should be described is in terms of ordinary text shown to make the player experience the story that you have designed by reading them.
You will still need to understand how to vary your output depending on various conditions or information, how the player input controls which events will happen, how to connect one location to another and how to store information for later use. In a way this is programming, but in an unusual sense.
In order to understand the rules of the Alan language we will have to establish some common ground. As an author you’ll have to learn to view works of interactive fiction in the same way as the Alan language does. It will not be hard as most of the concept are familiar already.
3.3. What’s Happening?
The execution of an adventure is primarily driven by the input of player commands. A command is analysed by the interpreter according to the command syntax prescriped by the author and, if understood, it is transformed into execution of verbs or movements.
After the player has had his turn, any special conditions, or rules, that might have been programmed by the author are evaluated.
Then, other, scripted non-player characters, or actors, will move. Their movements and actions are controlled by the computer, again according to the definitions in the source.
Scheduled events are then run, and then the player takes another turn.
This is described in more detail in Chapter 33. |
The following sections describe a number of the fundamental concepts that are present in an adventure game and what the Alan view of them is.
3.4. The Map
The scene for the game is a map of a number of connected locations. A location has a description that is presented to the player when that location is entered. A location may also have a number of exits stating in which direction there are exits and to which locations they lead. Alan places no restrictions on the layout of the map, any topology is allowed.
In Alan, exits are always one-way, and an explicit declaration of a backward path (if such is desired) must be made. Although, normally you would probably want them to be two-way, if they where automatically two-way, it would be very hard to handle the rare, but important, cases when you don’t want them to be so. |
3.5. The Things
Most objects in an adventure are things that in real life would be objects too, like a knife or a key. In addition, other things that should be possible to manipulate by the player, e.g. parts of the scenery, must be declared as an object. For example if you require the player to ‘whistle the melody,’ then the melody must be an Alan object.
Objects, like locations, have a description that is presented when they are encountered during the game.
Every object may also have a set of properties, like edible and movable, which may be changed during the execution of an Alan program. Most objects would e.g. probably not be edible so there is also a mechanism for declaring how these properties should be set by default, as well as mechanisms to override them, both for a particular object and for groups of objects.
Some player actions (verbs) have special meaning or effects when applied to a certain object. These verbs and their special effects are also declared within the object declaration.
3.6. Other People and Monsters
An extra thrill and dimension are additional characters in the game. In Alan, these are called actors and may have a life of their own. For each move the player makes, these programmed characters also get a turn to do their thing. An actor may be a thief running around and stealing your collected treasures or a dragon guarding the entrance to its lair.
Actors get their behaviour from scripts that, step by step, describe what is going to happen for each player interaction.
One of the interesting things about playing adventure games with actors is to figure out how to interact with and influence the other characters.
3.7. Acting
The player commands action by typing imperative statements. These statements are analysed and result in verbs executions (“calls”). The effects of these commands must be declared in verbs by the game author, either in an object (describing the effects of the verb when applied to an object) or as a general (global) that only applies without object.
3.8. The Input
To make it possible for the player to input more complex commands, a means to specify the syntax for a verb is also available. A particular syntax is connected to a verb and describes how the player must phrase his input in order to command the triggering of a particular verb. Using this mechanism, verbs can also be made to operate on literals (strings and integers) giving the player the possibility to input things like
> write "Merry Christmas, Mr. Lawrence" on the xmas card
4. Introduction to the Language
Alan is an adventure language, i.e. a language designed to make it easy to write adventures. This means that constructs in the Alan language reflect the various concepts encountered when creating an adventure plot.
A common step after having come up with a plot for your adventure is to draw a map of the world where the adventure is taking place.
For this purpose, we use Location
s.
The next step is to introduce tools, weapons and other objects possible to manipulate.
These are the Object
s.
Then the player will need words to command action.
The Alan language construct to supply these is the Verb
.
Using the Syntax
construct, you can also define more complex player input.
Additionally, you may also want other characters and creatures in your adventure.
For this the Actor
class is provided.
4.1. Notation
In this document, there are some typographical clues. Example Alan source code is typeset in separate sections with a mono-spaced font:
This is an example of some source code.
You will also encounter sample game-play which will be formatted using a surrounding border (like paper…) thus:
Grandma’s House
You are outside your grandma’s house.
Later in the manual, you will find semi-formal definitions, grammar rules, for how various constructs may be constructed. These sections are typeset against a coloured background:
The rules for the rules are available in Appendix D.
In running text, words that are keywords or signify an Alan construct are written in a mono-spaced font.
This helps distinguish the English word ‘the’ from the Alan keyword The
.
As shown in the last example, Alan keywords are written with the first letter capitalized.
This is simply a convention and has no effect other than the visual.
A keyword can be written Keyword
, KEYWORD
, keyword
, or even KeYwOrD
(if you are keen to show how good you are with a keyboard…).
This manual tries to be consistent with using the first version (except in grammar rules).
And this is a note! |
4.2. The Locations
The scene for your adventure is a series of “rooms” or, rather, locations.
Location
s are connected by Exit
s, leading out of one location into another.
This makes it possible for the hero to travel through the world of your design, exploring it and solving the puzzles.
What is required if we want to describe a location?
Every location
must have an identifier.
This is so that you, the designer, may refer to that location easily, instead of having to remember a magic number for it.
Unless you plan to provide other means for transportation from a location, you should also describe in which directions there are Exit
s and to which location
s they lead.
In fact, this is all that is necessary in a location
, so lets look at an example.
The Kitchen IsA location
Exit East to hallway.
End the kitchen.
The Hallway IsA location
Exit West to kitchen.
End the hallway.
Start at Kitchen.
This is a complete Alan adventure (although very primitive).
As you can see, every Alan construct ends with a period (.
) and there is a “Start At
” sentence at the end, indicating in which location to put the hero when the game starts.
Type the above text into a text file, e.g. using a notepad program. Run this little Alan source through the Alan compiler and try the adventure (see Appendix A on how to do this). After starting the adventure, two lines will be shown on your screen.
Kitchen
>
The first line contains “Kitchen”, the name of the initial location, and the second a “>”, which is the default prompt for the player to input a command. Now try typing “east” and press the RETURN/ENTER key. The word “Hallway” and the prompt will appear. Typing “west” will take you back to “Kitchen” again. (Use Ctrl+C to exit the game if you are running it in a console window.)
The identifier for a location
is automatically used as a description, a heading, shown when that room is entered.
And the words listed in the Exit
parts are translated into directional commands the player can use in his input.
You should remember that exits are strictly one-way.
An Exit
from one location to another does not automatically imply the opposite path.
Thus, you must explicitly declare the path back, in the definition of the other location.
However, just the name of the location is not much of a description.
So in order to provide the “purple prose” descriptions often found in many adventures there is an optional Description
clause that you can use.
Let us describe the Hallway.
The Hallway IsA location
Description
"In front of you is a long hallway. In one end
is the front door, in the other a doorway.
From the smell of things the doorway leads
to the Kitchen."
Exit West to kitchen.
End the hallway.
We introduce another feature in this example, namely the text enclosed in double quotation marks ("
) which is called a string or, when used on its own like this, an output statement.
When executed, this string will be presented to the player and formatted to suit the format of his screen.
Invent a description for the Kitchen, enter it in the Alan source and run the changed adventure. You notice, of course, that the text in the output statements is reformatted during output to suit your screen, in order to make room for as much text as possible. Note also that you do not have to worry about this at all — in your source file, you may format the text any way you like, even spanning multiple lines with extra white-space included.
This type of output statement is just one of the statements in the Alan Language, and we will see more of them later.
It is also possible to have conditions and statements in the Exit
clauses of a location
to restrict the access to the next location or to describe what happens during this movement.
Exit West to kitchen
Check kitchen_door Is open
Else "The door is closed."
Does
"As you enter the kitchen the smell of
something burning is getting stronger."
End exit West.
4.3. The Objects
Another essential feature in Alan are the objects.
Like the location
, the object
is a means to describe the “physical” world where your adventure is taking place.
Many objects are probably used to provide puzzles, such as closed doors, keys, and so on, but other objects should be promoted to object
s too.
A large number of objects that can be examined and manipulated make a game so much more enjoyable.
Object
s, like location
s, have an identifier and a Description
, so you might guess the general structure of an object:
The door IsA object at hallway
Is closed.
Description
"The door to the kitchen is a sliding door."
If door is closed then
"It is closed."
Else
"It is open."
End if.
End the door.
An object
may initially be located at a particular location
.
This is indicated by the At
clause, in this case telling us that the door is initially located in the Hallway.
Objects do not necessarily have to start at a particular place, in which case they are not present in the game until located, by executing some code, at some place where the player may lay his hands on them.
In addition, objects may have attributes indicating the state of certain properties of the object.
In this example with a door, the Is closed
part indicates that the door should have the attribute closed
, which initially is set to TRUE (implying that the door is initially closed).
The opposite would be indicated with a Not
, (i.e. Is Not closed
).
Alternatively, attributes may be numeric (e.g. Has weight 5
) or be of string type (e.g. Has inscription "Kilroy was here"
).
The example also introduced another Alan statement, the If
statement.
The If
statement allows you to select which statements to execute according to some condition.
In the example, the closed
attribute of the door selects which description to show.
There are further variations of expressions and the If
statement, but we will come back to these later (Chapter 26 and Sect. 23.6.1).
Instead, let’s look at some other statements in relation to objects.
It must of course be possible to change the attributes value of an object.
You can do this using the Make
or the Set
statements.
For example, if the door should be opened (the player having said “open door”, perhaps) this could be performed by stating:
Make door not closed.
To close it (i.e. setting the closed
attribute to TRUE again) you write:
Make door closed.
The Make
statement changes Boolean (or True/False) attributes.
The Set
statement changes numeric or string attributes, for example:
Set level of bottle to 4.
These statements only change attributes. The implications of such a change must be implemented by writing Alan code that tests these attributes and provides differing text output to the player. This is what gives the player the illusion of a door being open or closed, for example. |
Alan does not understand, nor enforce, any semantic in the identifiers for attributes, they are only identifiers. The illusion of the effects of differences in the value must be implemented by varying the output. In addition, Alan does not understand that an attribute ‘closed’, for a human would be the opposite of an attribute ‘open’. You should choose one and stick to it. |
Of course, attributes are not only available on objects, but on locations and other types of entities also.
Another manipulation statement is the Locate
statement.
This is the statement to use when moving objects from one location to another.
Opening a lid might cause a previously hidden object to fall to the floor, something that could be performed by moving the object from limbo to the current location with:
Locate treasure here.
You could also relocate it to a particular place using the statement:
Locate vase at hallway.
4.4. The Actors
Actors can be used to populate the adventure with creatures, beings and other people. They might be pirates or monsters, but the thing they have in common is that they move around or at least perform various actions more or less in the same way as the player does.
An actor
may have a Description
and attributes, like object
s and location
s do.
An actor performs his movements by following Script
s, each having a number of Step
s.
Each step corresponds to one player move.
The charlie_chaplin IsA actor
Name Charlie Chaplin
Script going_out
Step
Locate actor at outside_house.
Step
Locate actor at hallway.
Use script going_out.
End the charlie_chaplin.
4.5. Inheritance and Object-Orientation
Object-orientation is a term that is often used when talking about programming. The concept is modelled after a natural phenomenon first described by the Swedish botanist Carl Linnaeus (or Carl von Linné). He devised a naming system for flowers and plants that was based on features common between various species and families. The idea is that a general concept such as a mammal is defined by listing some features which all mammals share. Specialisations such as sub-species in turn have other, more specialised, features in common.
In nature, we talk about species and individuals. In object-oriented programming we talk about classes and instances, which are similar. Classes are abstract definitions of what the common features are and instances are individuals (data objects) having those features.
Inheritance and Instances
Inheritance means that a more general class can be restricted or specialised into new sub-classes. We say that the specialised class inherits from the more general. Most object-oriented programming languages allows creating instances from any class, which does not happen in nature, there are no individuals that are mammals, they are individuals of some specific species of horse for example.
In programming, we can use this concept to make some things easier for ourselves. By collecting features that are common to many types of data objects into classes and sub-classes we can inherit those features. In this way, we can avoid explicitly, and repeatedly, stating those for every data object. One small drawback is that we have an implicit declaration of features, which can make reading a bit more obscure. We need to look up the parent class (or classes) for complete information about the object.
Polymorphism
By using inheritance, we can also guarantee the properties of similar, or related, instances.
If every mammal is a vertebrae, we know that all properties of vertebrates also applies to mammals.
We can use this knowledge to handle commonalities without knowing anything about the more specialized kinds, or classes.
One example of this might be lockable things like doors and drawers.
If they inherit from a common ancestor lockable_things
, then we do not need to know if it was a door
or a drawer
, if we are only interested in the locked
property.
This flexibility, know as polymorphism, is possible in programming only through object-orientation and inheritance.
EVERY and THE
The Alan language supports object-orientation and inheritance via two constructs:
Every mammal IsA vertebrate ...
The house_pet IsA cat ...
The Every
construct defines a class and its properties, including inheriting from another, even more general class.
The The
construct declares an instance, which in this example inherits from the class cat
.
The IsA
construct defines from which class properties are inherited.
The Predefined Classes
To make it easy to get started there are eight classes predefined in the Alan language.
They are entity
, thing
, location
, actor
, object
, literal
, string
and integer
and have the relationship, inheritance tree, shown in the figure above.
The semantics of these predefined classes are in short:
-
Only locations (instances inheriting from
location
) can be visited by the hero (the player’s alter ego). -
Only actors may have scripts that they perform.
-
Only things will be described automatically when encountered.
-
Literal and its sub-classes cannot be sub-classed. They are used to handle integers and strings in player input.
See the subsections of Instances for more detailed descriptions.
Creating Classes and Instances
In the sections above about locations, objects and actors the examples show how to create an instance of a class. Those examples show how to do it from the predefined classes. However, it is the identical if you have defined the class yourself. In general the format is:
The <instance identifier> IsA <class identifier> ...
Defining a class is pretty much as you would expect it to be:
Every <class identifier> ...
After this, declarations of all the properties for that class follow. This could include inheriting from another class, e.g.:
Every door IsA object
End every.
Every openable_door IsA door
Is open.
End every.
The kitchen_door IsA openable_door
End the kitchen_door.
In this example, the kitchen_door
has the attribute open
although it does not specifically show in the declaration.
It is initially set to true as specified in the declaration of the class openable_door
.
Specialising and Overriding
Sub-classing, or specialisation, is usually employed to add properties and thus make the instances of the sub-class more restricted, or specialised.
In the example above, openable_door
s are specialisations of door
s since they have an attribute that the more general class does not have.
However, a sub-class can also redefine a feature.
In the example above a class named closed_openable_door
could be defined as:
Every closed_openable_door IsA openable_door
Is not open.
End every.
This makes all instances of the new class have the same attribute but it is set to false instead.
The important thing is that the feature of having the attribute is common to all openable_door
s.
This is called overriding a property.
This concludes our brief presentation of object-orientation and how the Alan language supports it. The most important thing to remember is that you can extend the inheritance tree with new classes and that most features can be inherited along that tree and be overridden, both by intermediate classes in the inheritance chain and explicitly in the instance itself.
4.6. Containment, Classes and Transitivity
One basic property of instances is that they may contain other instances. Although conceptually simple there are twists that you should know about.
Containers Containing Containers
Containers might contain other instances that are in turn containers and so on, of course. If you want to consider everything inside a container recursively, you might actually get types of instances you did not expect.
As an example, let’s consider a container named cont
, that takes a subclass of object
, named subobject
.
Assume there is also an instance of that class, named inst
, which is also a container, but takes object
instead.
Every subobject IsA object
End every subobject.
The cont IsA object
Container taking subobject.
End the cont.
The inst IsA subobject
Container taking object.
End the inst.
When you search, recursively, for instances in the container cont
, you might then get instances that are both of class subobject
and object
, e.g. if the inst
is inside the cont
and in turn contains something, which would then be of class object
.
This might lead to, completely correct, but surprising, error messages from the compiler indicating that an attribute or other property that you thought existed is not available.
Especially surprising is perhaps the case where the classes are not even decendants of each other.
In this case the contained instances can only be guaranteed to be their common ancestor, which might be a quite general class like thing
or even entity
.
Transitivity
For many scenarios the above works well, and as expected. But for other cases the notion of transitivity is introduced. Transitivity describes how e.g. containment should be interpreted.
The scenario above may be described as “transitive containment” meaning that something is in a container if it is in that container or in any container it contains, recursively.
There are two other types, namely direct and indirect transitivity. Direct transitivity actually means no transitivity. If you investigate a container with direct transitivity you will only get its direct contents, not the contents of the containers within it.
Finally, indirect transitivity means instances indirectly contained by a container. In a way it is the opposite of direct transitivity, all instances recursively contained except the directly contained. Here’s a rule to remember:
Transitive = Direct + Indirect |
What this means is that if you use transitive containment you get the same instances that direct and indirect will give you. And it is usually the indirect ones that you should look out for.
4.7. The VERB Construct
The Verb
is the construct that implements the effects of an action requested by the player.
Verbs are usually associated with a class or an instance.
We will look at the implications of various combinations of these in the next few sections.
To implement a Verb
you need a name for it (which is also the default word the player should input to request that action).
You must also decide which effects this verb should have under various circumstances.
If we want to implement the Verb
‘open’ for the door
we could use the following code:
Verb open
Does
Make door open.
End verb open.
A Verb
is either a simple command taking no parameters, like ‘look’, ‘save’ or ‘help’, or it involves one or more parameters that the player can reference.
Simple verbs should be declared at the top level, globally, i.e. outside of any other declaration.
Verbs taking parameters, on the other hand, must be declared within the class or instance with which they are associated.
For example, if a verb will handle objects it should be declared in the object class.
The example above should probably best be placed in the door object itself.
The kitchen_door IsA object
Verb open
Does
Make kitchen_door open.
End verb open.
End the kitchen_door.
This defines the effects of applying the open
verb to that specific door.
The implementation makes direct references to the kitchen_door
, so to make the verb more general it should be possible to apply to all door
s.
So let’s move the definition from the kitchen_door
instance to the door
class:
Every door IsA object
Verb open
Does
Make this open.
End verb open.
End every door.
With this definition it is possible to apply the verb to all doors.
A reference to the exact object the player mentioned in his command is propagated in the parameter (see Sect. 4.8 and Chapter 15 for a more thorough discussion).
Since this verb is now applicable to all door`s, the attribute `closed
must also be available for all instances of the door
classs.
We can do that by ensuring that the attribute exists in the class using a normal attribute declaration.
(See Chapter 14 on how to add an attribute to a predefined class such as object
).
Of course, there are often also conditions that need to be checked before we can execute this code (perhaps to see if it was possible to open the object!).
Therefore, Verb
s may have Check
s, as we will see next.
Checking Things
In order to ascertain that the correct conditions are fulfilled before the body of a Verb
is actually executed, the verb may have an optional Check
part.
Verb open
Check o is openable
Else "You can't open the $o."
Does
Make o open.
End verb open.
This definition of the open
verb is more realistic than the previous one.
It specifies that before the statements after Does
are executed, the condition after Check
must be checked (which, in this case, checks that it’s really possible to open the object indicated by the player).
If that condition is TRUE then the requirements are fulfilled and the body of the Verb
(following the Does
) can be executed.
If this is not the case the Else
part is executed instead (normally showing some message).
A Check
may have multiple conditions, as the following code shows:
Verb take
Check o takeable
Else "You can't take that."
And o not in hero
Else "You already have it."
Does
Locate o in hero.
End verb take.
Here we also encounter a variation on the Locate
statement — the capability to place an object inside a container (the inventory).
You can never destroy an instance or remove it from the game. Instead, you can define a limbo location, i.e. a location that is not accessible to the player and may thus be used as a storage for “destroyed” objects and other things the player is not supposed to see. |
4.8. The SYNTAX
Normally a verb acts on an object or actor, henceforth called a parameter, referenced by the player in a command. This means that the format of player input is usually something like:
> take vase
This form, or syntax, is the default form if you don’t specify anything else. The default syntax might thus be described as:
Syntax
? = ? (parameter)
The question marks are placeholders and should be interpreted as the name of the verb.
In order to allow different and more complex player input the Syntax
construct is supplied.
The Syntax
construct is a way to describe the words and parameters the player may use in order to execute a particular verb (its global and more specialised parts).
Below is the syntax for put_in
, the verb to put something inside a container.
Syntax
put_in = 'put' (obj) 'in' (cont).
This syntax defines the put_in
verb to be executed when the player has input the word ‘put’ followed by a reference to an object or actor (a parameter named obj
), followed by the word in
followed by a reference to a second parameter (the container, referred to as cont
), as in:
> put the green pearl in the black box
This will bind the parameter obj
to the instance that represents the green pearl and the parameter cont
will be bound to the black box.
It is also possible to restrict the types of the parameters:
Syntax
put_in = 'put' (obj) 'in' (cont)
Where obj IsA object
Else "You can't put that into anything."
And cont IsA container
Else "Nothing fits inside that."
This restricts the parameter obj
to being an instance inheriting from the object
class (as opposed to an actor
for example) and the parameter cont
to a Container
(an instance with the Container
property).
The parameters are used as normal identifiers in the Alan source code.
The parameters can only be referenced if they are defined in the current context, i.e. they can only be used in the various bodies of the Verb
for which the Syntax
applies (see also Chapter 36 for a detailed discussion).
The Syntax
construct allows for more than one parameter, in order to make it possible to define more complex player commands.
Therefore, the verb execution order previously described regarding execution of verbs in one instance must be generalised to verb bodies in all the parameters.
In the example above, verb bodies in the objects or actors referenced as obj
and cont
(the green pearl and the black box) are executed (if the verb is present in their definitions).
4.9. Text Output Formatting
Text output on the screen is caused by what you have written in the Alan source code. However, since text is coming from various places it is not easy, or even possible, to anticipate the full context of a particular text.
Therefore, the Alan system takes care of some specific formatting issues. First, text will always flow neatly inside the window or screen. Long lines will be automatically split without breaking in the middle of words.
Secondly, a few special cases are also handled automatically:
-
After a full stop (period, the character ‘.’), an exclamation (‘!’) or question mark (‘?’), and in the beginning of paragraphs, including location headings, the first character will be guaranteed to be upper case, automatically converted if necessary. This means for example that you don’t have to consider the case when the name of an object might be printed as the first thing in a sentence. The name will automatically be capitalized. For example:
The postmen IsA actor at postoffice ... The postoffice IsA location Description Describe postmen. ...
Given the above snippet from a game source, the transcript would read:
Postoffice
Postmen are working behind the counters.This would be the case even if the description of the postmen started with a lower case character.
-
Two outputs following each other will automatically be separated by a space (a blank character). Except for the following case:
If an output is immediately followed by another output starting with a full stop (period, the character ‘.’), an exclamation, a question mark or a comma, and it is the only character in that output or it is followed by a space (blank character), no space will be inserted before that output. This rule will ensures that the full stop in the following source is automatically adjacent to the previous text, without the need to suppress spacing:
"You can't take" say p. "."
5. Strict and Safe
The Alan language is strict and type safe. This means that the compiler will attempt to prevent any constructs that might generate a problem for the player, such as assigning values of one type to attributes of another type, accessing properties that are not guaranteed to exist on the instance, and so on.
A simple example is
1. Every animal IsA object
2. Has fur.
3. End every animal.
4.
5. The house_cat IsA object ...
6. ...
7.
8. If house_cat has fur then ...
At line 8 we will get an error saying that the house_cat
does not have the attribute fur
.
Now, if that is true, it’s a good thing that the compiler caught the error, otherwise the game might try to access that attribute — and blow up in the face of the player!
Can you spot the problem?
The house_cat
is declared as an object
, and not as an animal
.
Other examples include trying to use a script for an actor (or actor subclass) that does not have one, locating something inside something that is not a container and so on.
Here is a more complicated example:
1. The cont IsA object
2. Container taking thing.
3. End the cont.
4.
5. Add to every object
6. Has someAttribute.
7. End add.
8.
9. The box IsA object
10. Container taking object.
11. End the.
12. ...
13. For each f in box do
14. If f has someAttribute then
15. ...
On line 14 we will get an error saying that someAttribute
is not available since the class f
can only be guaranteed to be thing
.
Is it?
Well, the variable f
(in the loop) enumerates all things in the box
and since the box
takes object
s, it is possible that it may contain the cont
.
And since that takes thing
s, and In box
is transitive (see Sect. 4.6.2), f
may take on any thing
that is contained in the cont
too.
Of course, there are various ways to fix this:
-
change the transitivity of the filter in the loop (
Directly In
) -
add a class-restricting filter in the loop (
IsA object
) -
rethink your class hierarchy
The Alan compiler is trying to protect you, and your players, but sometimes the error can be hard to spot.
Part II: Language Reference
6. General Rules
The Alan language is divided into syntactic components of different kinds.
Each component may be composed of text and/or other components.
A component is terminated by a period or full stop (.
).
This indicates that that component is complete.
Some components start with a keyword or initial phrase, such as Description
or Exit east To kitchen
.
If it is to be followed by further components, such as statements or output strings, that keyword or phrase should normally not be followed by a period, but by its continuing components.
For example:
Exit East to Kitchen.
But:
Exit East to Kitchen
Check kitchenDoor is open
...
End exit.
Note that the first is terminated, but the second example is continued with a Check
, and not terminated until the End Exit
.
7. An Adventure
An adventure starts with an (optional) set of Option
s (see Chapter 8) followed by a set of declarations.
adventure = {option} {declaration} start_section
According to the rules it is actually possible to have no declarations at all (as indicated by the curly braces) but there would be no adventure without a single location, right? So, in practice you’ll need at least one declaration.
The declarations constitute the major part of the adventure. The declarations can be declared in any order and repeated freely, and are of many different possible types.
declaration = import
| class
| instance
| addition
| syntax
| verb
| rule
| synonyms
| event
| messages
| prompt
The adventure source text must end with a Start
section.
start_section = 'START' where '.' statements
It indicates where the hero is when the game starts but can also be used to set things up, welcome the player and so on.
The Start
section is mandatory.
Start at bedroom.
Schedule alarm_clock after 2.
"Slowly you come to your senses, your numb limbs
starting to feel the blood flowing through them..."
You can look up the meaning of the rules "WHERE" and “statement” elsewhere in this chapter.
8. Options
Option
s define things concerning the overall behaviour of the generated Alan adventure.
As is implied they are optional and are only required if you need to change the value of an option from its default setting.
An option follows the grammar:
options = 'OPTIONS' {option}
option = id '.'
| id id '.'
| id integer '.'
The example below illustrate how options may be written, following the above rules.
Options
Debug.
Language Swedish.
No pack.
Width 128.
The available options are:
Option name | Possible values | Default value |
---|---|---|
|
|
|
|
24-255 |
80[2] |
|
5-255 |
24 |
|
Boolean (on or off) |
Off ( |
|
Boolean (on or off) |
Off ( |
The Language
option specifies the language in which the adventure is assumed played, and selects different default message texts.
Alan is primarily designed for adventures in the English language, but it is also possible to write adventures in other languages.
To make this possible, the default messages output by the interpreter may be generated in different languages.
It is completely possible to write in other languages, but then you must customize all the message texts.
See Appendix C, Sect. C.1, for a complete list of such messages.
The Alan compiler and interpreter will always allow multinational 8-bit characters as input and the default messages are generated for 8-bit character sets, internally representing national characters according to the ISO multinational character set (ISO-8859-1) requiring 8 bits. On output, this is converted to the native character set of the machine (whenever possible). This means that portability between platforms should be good even for text containing multinational (non-ASCII) characters.
Width
specifies how long the lines the interpreter outputs should be (formatting is automatic!).
The Length
option will instruct the interpreter on how many lines to show on the screen without any player interaction (<More>).
These values are only used if the interpreter itself cannot get the actual values.
The Pack
option will cause the compiler to compress the texts to occupy less space.
As a bonus, this also makes it impossible for the player to cheat by dumping the adventure code file.
As a minor drawback, it does make the execution of the adventure a bit slower (noticeable only on some very old, smaller, computers).
In order to allow debugging of the generated adventure (see Chapter 53), the Debug
option must be turned on.
This may also be performed using the debug compiler switch (see Sect. A.2).
9. Types
The Alan language handles information in bits, values. Each such bit of information, or data, is of a specific type. Alan is a strictly typed language, which means that assignment, comparisons and other statements will require that rules concerning the compatibility between such values are not broken.
In the Alan language, you cannot explicitly state the type of a value.
Instead, this is inferred from how values are used, e.g. the initial value of an attribute or the restrictions put on a Syntax
parameter.
9.1. Basic, Simple and Compound Types
The basic types of values available in the Alan language are:
-
Integer — e.g. a simple integer constant, a reference to an integer typed attribute or a numeric expression using any of the mathematical operators.
-
String — e.g. a string constant or a reference to an attribute typed as a string.
-
Boolean (true or false) — comparisons yield Boolean values, Boolean attributes.
Two other simple types are available:
-
Instance — a reference to an instance or an attribute typed as a reference attribute that refers to an instance.
-
Event — a reference to an
Event
or an attribute typed as a reference attribute that refers to an event.
There is one compound type in the Alan language:
-
Set — an unordered list of values.
9.2. Instance Type
Every time a reference to an instance is made, it can be considered an expression of instance type. In these cases, the class of the instance also often matters. E.g. assigning a reference attribute can only be made if the new value refers to an instance that belongs to the same class or a subclass of the initial value of that attribute.
Some types of expressions return a value referring to an a class or instance in the Alan source. Examples include an identifier bound to a parameter allowing instances and a reference attribute.
9.3. Event Type
Event
is a set of statements that can be scheduled to execute with a specified delay.
Each reference to an identifier of an Event
is of course of the Event type.
Event
s can be referenced by attributes and any reference to such an attribute is of Event type.
Expressions of Event type can be used in Schedule
and Cancel
statements.
9.4. Set Type
A Set is a collection of values that may be referenced as a single value, but also investigate, added to and removed from. An example might be a set of cards in a dealt hand, the set of spells that the hero have learned, or the set of numbers guessed so far.
The order of elements in the set is not specified. Each member can only occur once in the same set, but a member can occur in multiple sets. You could for example include one set of numbers (integers) in one set and another set of numbers in another set. It is then possible to investigate the sets and remove all members that are members in both.
The Set type is a compound type since it is not complete without a member type. You can only include members in a set if the type compatibility rules allow it. A Set may include members that are instances or integers.
If the Set includes instances, the subclass compatibility rule applies. All members in the set must inherit from the same class. See the section on type compatibility below.
The fact that an instance is in a Set does not affect the instance. In fact, there is no way to find out in which Sets, if any, a particular instance is included. In particular, it does not affect the instances location. |
9.5. Type Compatibility
Assignment and comparisons between values requires the values to be compatible. The three basic types (integer, string and Boolean) are only compatible with themselves.
Values of the Instance type can be compared without restriction, except that there is no notion of lesser or equal, so only equality can be tested. Assignment can be made if the new value is of the same class, or of a subclass, as the attribute or variable that receives the value. This class is normally inferred from the initial value of the declaration.
For example, a reference attribute (an attribute referencing an instance) is inferred to be restricted to instances of the class of its initial value. Any subsequent change of the attribute (setting it to refer to another instance) requires that the new instance be of the same class or a subclass thereof.
These rules ensure that attribute references and other properties are always retained during the execution of the whole game. Thus, it will never cause a run-time error on the player.
9.6. Type Requirements
Some statements require their arguments to be of a specific type. This is enforced by the compiler. The compatibility rules apply here also, given that the required type is given by the statement itself.
Examples include the conditional If
statement, that requires a Boolean value (or expression) to test, and the Use
statement, which requires references to instances that are subclasses of the predefined class actor
.
10. IMPORT
The source text for a large adventure might become entangled and complex.
A way to break up a large text is to divide it into separate files.
Each such file can then be imported into the main source using the Import
statement.
import = 'import' quoted_identifier '.'
The quoted identifier is the name of the file to import (see Chapter 32).
The Import
may be placed anywhere in a file where a declaration can occur, and the effect will be the same as if the contents of the named file had been inserted at that position in the file.
Import
s may be nested, so an imported file may in turn import more files, without limits.
An imported file is searched for first in the current directory and then in any of the directories indicated using the Import
switch as described in Compiler Switches, this search is performed in the same order as the Import
switches occurred on the command line.
The Import
statement is the way to use the Standard Library (or a library of your own design).
Place the library files in a directory where the compiler will find them, either in the same directory as your other source files or somewhere else (see Sect. A.2 on how to make the compiler look in more folders than just the one where the main source file resides).
In your source you would refer to the main file of such a library by:
Import 'library.i'.
Another use is for dividing your own source into multiple files to make them easier to handle:
Import 'harbor.i'.
Import 'city.i'.
Import 'desert.i'.
Import 'actors.i'.
Start at city.
11. Classes
Classes are definitions of templates of instances. That means that a class declaration only describes instances, and does not add anything to your game in itself. Instead, you have to create an instance of the class to make it available in the game (see Instances below).
class = 'EVERY' id
[inheritance]
{property}
'END' 'EVERY' [id] ['.']
The id is the identifier used by the author to refer to this class throughout the source code, e.g. when referring to it in the inheritance clause of other classes and instances.
The properties are described in Chapter 13.
11.1. Inheritance
Every instance must inherit from a class (see Sect. 4.5). Furthermore, user-defined classes must also inherit from other classes. A class or an instance inheriting from a class will get all properties of that class. All properties explicitly declared in a class or instance inheriting from another class will extend, override or complement those properties as specified in the original, parent, class. This way, you can easily create new classes by extending existing ones.
You specify which class another class or an instance inherits from using a clause following the grammar:
inheritance = 'ISA' id ['.']
For example:
The door IsA object ...
and
Every coin IsA treasure ...
12. Instances
The most important part of an Alan game source is probably the declarations of instances.
Instances are the object
s, location
s, actor
s and other thing
s that fill your game universe.
The player traverses and interacts with these in his quest to negotiating your game.
instance = 'THE' id
[inheritance]
{property}
'END' 'THE' [id] ['.']
Every instance must inherit from a class (see Inheritance above) keeping all properties of that class.
Each inherited property can be amended or overridden by specifying it in the declaration of the instance, and new attributes, Exit
s and Script
s can be added in the same way as in class declaration.
Exactly the same rules for declaring properties apply to instances. The only difference is that an instance will actually show up in the game when it is run. Remember also that properties declared in an instance are not common to any other instances (unless the declaration overrode the value of a class property).
Instances inheriting, directly or indirectly, from the predefined classes thing
, entity
, object
, location
, actor
and literal
, are subject to special semantics and restrictions.
Here are two examples of instance declarations following the rules above:
The red_ball
IsA object
At bedroom
Name red ball
Is hidden.
Description
If this is not hidden then
"An ordinary ball is laying under the bed."
End if.
Verb roll
Does
"You roll the ball a bit. Nothing exciting happens."
End verb.
End the red_ball.
The mr_brown
IsA actor
Name Mr Brown
Article "".
Pronoun him.
Is working.
Description "Mr. Brown is here, working at his desk."
End the mr_brown.
In these examples the source lines between The
and End The
all declare various properties that we will learn more about in Chapter 13.
The rest of the lines are fairly easy to match up to the rules of the Alan language as described by the earlier box.
All capitalized words in the examples above are keywords in the Alan language (see Table 5 for a complete list), the rest are author defined words or identifiers (with the exception of the words object
and actor
, which are identifiers predefined to be special classes).
12.1. Entities
The base class entity
represents the lowest denominator of all instances.
All other predefined classes inherit from entity
.
So adding a property to entity
will add it to every instance.
Entities cannot have an initial location, nor can they be located anywhere.
On the other hand, they can be considered to be available everywhere.
They are not described when encountered.
They can only be shown by explicitly executing a Describe
statement.
So, if you want an instance to always be available but invisible, create an instance of entity
.
It is also possible to create subclasses of entity
.
Instances of such classes will follow the same rules.
12.2. Things
Thing
is a predefined subclass of entity
that adds the property of having a location.
This means that they can have an initial location and be located at locations and into containers.
They will, however not show up in descriptions or listings, but the player can refer to and interact with them.
They can be described by explicitly executing a Describe
statement.
Creating an instance of thing
is a good choice if you want an invisible instance that should only be available at particular locations, or under specific circumstances.
Note that a |
12.3. Objects
Objects are instances inheriting directly or indirectly from the predefined class object
.
Objects are all the things that can be manipulated by the player.
They can be picked up, examined and thrown away (if the author has allowed it).
In addition to the properties inherited from thing
, any present object will by default, be described when the player enters a location or otherwise encounters it.
12.4. Actors
The predefined class actor
is intended for providing so called NPCs, non-player characters, in your game.
Like the player, they can move around but to do this they have to be scripted, i.e. programmed with some behaviour using scripts.
An instance inheriting from the actor
class will be described when encountered.
Actors can be located, as can any thing
, but not be inside a container.
In addition, they can have scripts.
Actors also exhibit special behaviour when they are described, e.g. when they are encountered.
If an actor is executing a Script
with a Description
, (see SCRIPTs) this description will be used instead of the one declared in the description clause.
The kirk IsA actor Name Captain Kirk at control_room
Has health 25.
Container
Header "Kirk is carrying"
Else "Captain Kirk is not carrying anything."
Description
"Your superior, Captain Kirk, is in the room."
End the kirk.
The george IsA actor
Name George Formby
Description
"George Formby is here."
Script cleaning.
Description
"George Formby is here cleaning windows."
Step ...
Script tuning.
Description
"George Formby is tuning his ukelele."
Step...
:
The HERO
There is one very special actor, the hero
, which represents the player.
This actor is always pre-declared with some basic properties, so you don’t have to declare it.
But if necessary, it may be re-declared in the same way as any other actor.
One situation when this is required is if you need attributes on the hero, such as “sleepy” or “hungry”. A declaration like the following can then be used:
The hero IsA actor
Name me
Is not hungry.
Verb examine does
If hero is hungry then
"Examining yourself reveals a poor, hungry soul."
Else
"You find nothing but a poor beggar."
End if.
End verb examine.
End the hero.
The hero is predefined with a simple Container
property taking object
s with no Limits
.
It seems natural to use that as the “inventory” of the player, the storage for everything the player is picking up and carrying around.
You will probably need to handle carried items in some manner, and the pre-declared container is one suggestion.
You can also redeclare the Container
property of the hero so that it suits your needs.
12.5. Locations
A location
is a declaration of a place (a “room”) in the game that (normally) can be visited by the player, and have objects lying around, etc.
In fact, the map of your game is a set of interconnected locations.
A location is any instance inheriting directly or indirectly from the predefined class location
.
Inheriting from location
implies the following semantic properties:
-
only locations can be visited by the player
-
only locations may have the
Entered
clause -
things and locations may be located at locations
-
Exit
s can only lead to locations and only locations can haveExit
s -
the
Start
location must be alocation
-
locations can’t have
Container
properties -
Verb
s in locations are executed only when the hero is at that location
When a location is described (for example when entering it) it is presented with a heading (the location name), the description (in the Description
clause) followed by descriptions of any present objects and actors not already, explicitly, described (using a Describe
statement) in the description.
An interesting property of locations is that a location can be located at another, both initially and during run-time. The result of having such nested locations is that all things present at the “outer” location are also present in the inner. This can be used in multiple levels to allow access to sky, ground and other scenery items available at many locations at once. It can also be used for grouping locations into sets of similar locations and for implementing vehicles.
12.6. Literals
The classes literal
, string
and integer
cannot be instantiated explicitly.
Instead, you might say that they are implicitly instantiated when the player inputs a literal.
For example:
> turn dial to 12
The second parameter (see Chapter 15) in this player command is the integer 12.
This parameter is automatically considered an instance of the predefined class integer
.
It is possible to add Verb
s to literal
and its sub-classes.
This way it is possible to create verbs that take strings and integers as parameters.
13. Properties
An instance or class can be given number of different properties by declaring them in the declaration of the class or instance.
property = initial_location
| name
| pronouns
| attributes
| initialization
| description
| articles
| mentioned
| container_properties
| verb
| script
| entered
| exit
Attributes, exits, verbs and scripts can be repeated any number of times in the same declaration. You cannot use the same identifier for more than one such property, e.g. you cannot declare two attributes with the same name.
13.1. Inheriting Properties
A property can be inherited from the parent of the class or instance. It is not necessary to repeat the declaration in the inheriting class or instance if it should retain its inherited value. Each inherited property may be amended or overridden by specifying it also in the declaration of the inheriting class or instance according to the following table.
Property | Inherited as |
---|---|
Initial location |
Overridden |
Name |
Accumulated, the inherited names are appended at the end of the list of |
Pronoun |
Overridden, each |
Attribute values |
Overridden, attribute declarations using the same name as an inherited can give the attribute a different value but must match the type of the inherited. Accumulated, you can add further attributes in a class or instance. |
Initialize |
Accumulated. Inherited |
Description check |
Accumulated. |
Description |
Overridden. |
Articles & Forms |
Overridden. |
Mentioned |
Overridden. Also overrides names. |
Container |
Overridden, all clauses are overridden. |
Verb declarations |
Accumulated. |
Scripts |
Overridden, for same |
Entered |
Accumulated. |
Exits |
Overridden, for same direction. |
The table also show which properties are inherited separately from the parent.
E.g. you can override the Description
but keep the description check, or even add another (since they are accumulated).
You cannot override the Limits
of a container and keep the Header
section since the Container
property is overridden in its entirety.
In an inheriting class, you can also add new properties.
More attributes, Verb
s, Exit
s and Script
s can be added to those already present through the inheritance.
The properties available for use in classes, and thus also for instances, are described in detail in the following sections. In general, all of these can be mixed freely, however, some semantic restrictions apply as to when a particular property is legal or not.
13.2. Initial Location
Where an instance will be located when the game starts is set using an optional WHERE clause.
If no such clause is used the instance will have no location.
An instance without location is not present (in the view of the player) in the game until it is moved somewhere by a Locate
statement.
initial_location = where
Only the At what
and In what
forms of the WHERE construct (see Chapter 24) are allowed when describing the initial location of an instance.
The chest IsA object at treasury
...
An instance inheriting from location
cannot have an initial location that is In
something, but it can be At
some other location, creating a nesting of locations.
13.3. NAMEs
By default, the identifier (“author name”) of an instance is also the name shown to the player, and by which he will be able to refer to it.
Normally you would want to override this with more elaborate and alternative names.
You can do that using the Name
clause.
name = 'NAME' id {id} ['.']
The Name
clause consists of a list of identifiers optionally followed by a full stop.
The identifiers given in the Name
clause are used when the instance is presented to the player, and the player can use them in his commands to refer to the instance.
For example:
The south_door IsA object at south_of_house
Name door
...
The south_of_house IsA location
Name 'South of House'
...
The use of a quoted identifier in the last example causes the name to be a single string of text. (See Chapter 29 for more details.) This works fine for locations, since the player usually does not need to refer to them in his commands; but a more sophisticated mechanism is available for things which the player needs to interact with (i.e. things, objects and actors).
The chair3 IsA object
Name little wooden chair
In this example, the name is a sequence of words.
The semantics of this declaration is that the word “chair” is a noun and “little” and “wooden” become adjectives.
When the player wants to refer in a command to the object with the author name (identifier) chair3
, he may use just “chair” if it’s the only accessible object with “chair” as its noun, or he may distinguish between multiple chairs by also providing one or more adjectives to be more precise about which chair he means.
The |
An explicit |
It is possible to give an instance multiple names by listing a number of Name
clauses.
Each clause will define adjectives and a noun, as described above.
As a result, the player can use any of those names to refer to the object.
For example:
The rod IsA object at grate
Name rusty rod
Name dynamite
...
This would allow the player to refer to the object using either ‘rusty rod’ or ‘dynamite’. (Or, as a side effect, even ‘rusty dynamite’.)
The first Name
clause is used for building a default description, if necessary (see Sect. 13.7).
The letter case used in the original words is preserved in the adventure output, but player input will always be matched without taking into account letter case (i.e. case-insensitively). This allows you, for example, to give capitalized names to people actors, which would then be shown correctly in the output.
Inheriting Names
Names can of course be inherited.
This is done in an additive way so that any names inherited are appended to the Name
clauses in the declaration.
This ensures that the class or instance itself can control the primary name (the first Name
clause).
Furthermore, this has the effect that an instance inheriting from a class defining a Name
can also be referred to using the inherited name(s).
Here is an example with fruits:
Every fruit IsA object Name fruit ...
Every apple IsA fruit Name apple ...
Every pear IsA fruit Name pear ...
The Gravensteiner IsA apple ...
The McIntosh IsA apple ...
In this example, both pear and apple can be referred to using the word “fruit”. Both the Gravensteiner and the McIntosh would be apples, not only by name, but also by all other properties of apples.
Displaying Instances
When an instance is to be shown to the player, it must be displayed in form of text. An instance can be printed in several different ways, it can be described or only mentioned. A description of an instance is a complete and usually more elaborate description of it (see Sect. 13.7). However, often an instance must be mentioned as a part of a sentence, or in a list.
Such a mentioning of an instance will involve the articles, the name and possibly the Mentioned
clause.
The basis for this mechanism is the short form, which by default is the first of the Name
s.
It will, however, be overridden by any existing Mentioned
clause (see Sect. 13.8.4).
The short form can be automatically transformed to a description (for instances that have no Description
) by inserting the article (see Sect. 13.8) and the short form in a default message.
In the following transcript example, output of the article is shown in bold, and the short forms in italic, the rest is the default message templates.
There is a little black book, a green pearl and an owl here.
The interpreter also uses this principle when constructing lists of instances in container content lists (as the result of the execution of an implicit or explicit List
statement, see Sect. 23.1.5).
13.4. PRONOUNs
In player input, it is often handy and natural to refer to items using pronouns, such as “it”, “them” or “her”. Alan provides a means to define which pronouns each instance can be associated with.
pronouns = 'PRONOUN' word { ',' word }
The effect of associating a pronoun with an instance is that the player can refer to that instance explicitly in one command and then in a subsequent command use that pronoun to refer to it again. Assume the player input:
> ask the priest about the bible
If the priest has been associated with the pronoun “him” and the bible with the pronoun “it”, the next command could be:
> give it to him
Pronouns are inherited as any other property, but are overridden as soon as a Pronoun
clause is present.
The predefined class |
13.5. Attributes
An attribute is a labelled value that instances have. Attributes declarations are placed either inside a class definition (in which case they will apply to all instances of that class or any of its sub-classes) or inside an instance declaration (in which case only that instance will have those attributes, unless it’s overriding inherited attributes with new values). An attribute declaration, or a set of declarations, is introduced using one of the keywords:
is = 'IS'
| 'ARE'
| 'HAS'
| 'CAN'
And the actual declaration of an attribute follows the structure:
attribute_declaration = id
| 'NOT' id
| id integer
| id string
| id id
| id '{' values '}'
An attribute can be of Boolean (having truth values), numeric, string, event, instance or set type. The type of an attribute is automatically inferred from the type of its initial value.
Combining the keywords with well chosen attribute names can give natural reading to your attributes:
The rats are hungry
The cowboy Can shoot
The chest is heavy
The combination_lock has numbers {1,2,4,8}
If you want some attributes to be present on every instance of a given class, then you must declare them in that class.
E.g. to declare a Boolean attribute that will be shared by all instances of the animal
class, the following code can be used:
Every animal ...
Is
Not human.
...
The attribute human
will now be available in all instances of the class, without further declarations, and it will be false.
If you want the attribute to have a different value in a particular instance, you must declare it specifically in that instance and assign it the desired value, which will be effective only for that instance.
You can override the value in a subclass, e.g.:
Every person IsA animal ...
Is
human.
...
Boolean Attributes
A Boolean attribute is declared by simply giving the attribute name, or its name preceeded by the keyword Not
(indicating a FALSE initial value):
thirsty.
Not human.
Numeric and String Attributes
Numeric and string attributes are declared by simply typing the value after the attribute name:
weight 42.
message "Enter password:".
Note that string type attributes are mainly intended for saving string parameters from the player input, like in:
> scribble "Kilroy was here" on the wall
They are not intended for storing long strings of descriptions, especially not as attributes to classes, as they (in the current implementation) require memory and take time to initialise when starting the game.
Event Attributes
Attributes can refer to events. Such an attribute is declared by giving the identifier of an event as its initial value.
Event e1
"This is e1 running."
Set e of l to e2.
End event.
The l IsA location
Has e e1.
End the l.
An attribute of the event type can for example be used to dynamically remember which event is scheduled, so that it can be cancelled.
Reference Attributes
Reference attributes store references to instances. Such an attribute is of instance type; the class is determined by the class of the initial instance that the attribute is referring. You may for example store a reference to the other side of a door:
The east_door IsA door.
Has otherside west_door.
...
You must initialise a reference attribute with a reference to an instance belonging to a class having the required properties. Any subsequent assignment to the attribute will require that the new value is either a member of the same class or a subclass of it. This ensures that operations on instances referenced by that attribute will always be possible.
Inside a class declaration, reference attributes may be initialised with a class identifier instead of a reference to an instance. This makes the attribute an abstract attribute, since it is defined but not initialised. Any instances inheriting from this class must then initialise the attribute, either explicitly or indirectly (by initialising it in an intermediate class). E.g.:
Every door IsA object ...
Has otherside door.
End every door.
The east_door IsA door.
Has otherside west_door.
...
If you need to set the initial value to refer to an instance of a sub-class of the actual class you want to allow, you can use an instance of the required class in the declaration and set its correct initial value in the |
Set Type Attributes
A set is an unordered set of either integers or instance references. Initial members must be listed in the declaration of the set. See Sect. 9.4 for details on the set type.
The type and class of allowed members is inferred from the actual values in the initial set. If they are instance references, the common ancestor of all members is used as the class of the allowed members. An empty set is only allowed as an initial value if the attribute is an inherited attribute, since in this case the member class is known from the inheritance and need not be indicated in the declaration.
You can also initialise a set type attribute with a set consisting only of a single class identifier. This will create an empty set with instance type members restricted to that particular class.
If you require an initially empty set of another type, e.g. integer, and you cannot give the member class by inheriting it, you can initialise the set with a single value of the correct type and remove that value in the |
Inheriting Attributes
Attributes are inherited like any other property. An attribute declaration employing the same name of an attribute already present in any ancestor of the instance or class will inherit the type of that attribute, for you cannot change its type in subsequent declarations. This means that any declaration of a value different from the inherited one must therefore follow the rules of type compatibility for assignment. (See Sect. 9.5.)
This also applies to classes of instances in the reference and Set types attributes. Both these types allow references to instances. The initial value given at the point where the attribute is introduced determines the required class of the Set members or referenced instances. This is retained throughout the complete inheritance of that attribute even if a subsequent initial value would imply a more specialised class. An example:
Every door IsA object
Has otherside someDoor.
End every door.
Every lockable_door IsA door.
Has otherside someLockableDoor.
End every lockable_door.
The someDoor IsA door
Has otherside someLockableDoor.
End the someDoor.
The someLockableDoor IsA lockable_door
Has otherside someDoor.
End the someLockableDoor.
In this example, the reference attribute otherside
is introduced in the class door
.
Its initial value is referring to the class door
.
This makes the attribute refer to doors.
In the subclass lockable_door
the attribute is used with another initial value, here it refers to a subclass of door
.
Despite this, the attribute in the two door instances will allow reference to doors, as indicated by the first declaration (in the class door
).
As a contrast, the same example can be used with abstract reference attributes (reference attributes that are defined, but not initialised, in the class declaration).
Every door IsA object
Has otherside door.
End every door.
Every lockable_door IsA door.
Has otherside lockable_door.
End every lockable_door.
The someDoor IsA door
Has otherside someLockableDoor.
End the someDoor.
The someLockableDoor IsA lockable_door
Has otherside someDoor.
End the someLockableDoor.
Now the class declarations refer to classes instead of instances in their declaration of the otherside
attribute.
This changes the semantics so that the subclass indicated by lockable_door
actually makes it illegal to use a door
as the declaration in someLockableDoor
does, instead a lockable_door
is required.
Using abstract reference attribute declarations in class declarations allows you to progressively refine the class of the instances that that attribute may refer to.
13.6. INITIALIZE
The attributes of an instance can be initialised using values in the attribute declaration.
This is usually sufficient for many situations.
For more flexibility, the Initialize
clause can be used.
initialize = 'INITIALIZE' statements
The clause makes it possible to execute arbitrary statements before the game is started.
The statements are executed before the Start
clause is executed.
This enables calculation of more complex initial attribute values to be located within the instance, or class, that requires it.
Of course general statements are also allowed so any prerequisites can be catered for.
Initialize
Set first_course of this to random in first_courses of menu.
Set second_course of this to random in main_courses of menu.
Set third_course of this to random in desserts of menu.
The current location is set to the Start
location, and the current actor is the hero
during the execution of all Initialize
clauses.
If the Initialize
clause is inherited it will accumulate all clauses with clauses from base classes executing before the clause from the subclass.
This lets the base classes do their initialisation before the initialisation of the more specialized, class or instance is performed.
13.7. DESCRIPTION
The statements in the Description
clause should print a description of the instance.
These statements are executed when the hero encounters the instance.
Depending on which base class the instance inherits from, this can be a location description presented when the hero enters the location or when executing a Look
statement.
Other possibilities are descriptions of objects and actors.
See Chapter 12 for descriptions of what inheriting from the predefined base classes means.
The |
See also Special Statements, concerning the Visits
statement.
The syntax for simple descriptions is:
description = 'DESCRIPTION' {statement}
If the Description
clause is missing for an instance (and no Description
is inherited), the Alan system will supply a default description such as “There is a round ball here.”.
If there is a Description
clause but it contains no statements, the object will be ‘invisible’, i.e. no description of it will be printed, not even a default one.
This can be useful for objects already described by the location description, or for objects with particular properties.
Here are some examples of simple description declarations:
The south_of_house IsA location
Name 'South of House'
Is outdoors.
Description
"You are facing the south side of a white
house. There is no door here, and all the
windows are barred."
...
The door IsA object
Description
"In the north wall there is a large wooden door."
If door is closed then
"It is closed."
End if.
...
Before executing a Description
, you can check for various conditions to be met.
A common example is the dark room.
If there is no light source present, the description should not be printed.
The syntax for such a description is:
description = 'DESCRIPTION' [checks] [does]
You can guard the description with a Check
in the same form as with Verb
bodies (see Sect. 16.3 for a detailed description of checks).
Of course, there are no qualifiers possible here.
To be able to separate the checks statement from the actual description statements the keyword Does
is required.
This is an example of the checks for a dark location:
Every dark_location IsA location
Description
Check sum of light_source here > 1
Else "It is pitch black. You are likely
to be eaten by a grue."
End every dark_location.
Note that it does not specify any description statements. This is because the checks and the actual description are inherited separately, as described in Table 2. The actual descriptions are left for the instances.
If multiple Description Check
s are available in the inheritance chain, they are all tested and must be met before any description is attempted.
So the inheritance of description checks is “additive”.
If any check fails, the description will not be executed. This particularly also implies that the default listings and description of present objects and actors in location instances will not occur either. Note, however, that any events and actor actions will be shown. See Locations below for a description of the default description mechanism for locations.
If neither a check nor any description statements occur after the keyword Description
this is a description, but it is empty.
You should not put statements that changes game state in the |
13.8. Articles and Forms
The syntax for articles and forms is:
forms = indefinite | definite | negative
definite = 'DEFINITE' article_or_form
indefinite = [ 'INDEFINITE' ] article_or_form
negative = 'NEGATIVE' article_or_form
article_or_form = 'ARTICLE' {statement}
| 'FORM' {statement}
The optional Definite
, Indefinite
and Negative
Article
s and Form
s can be used to define how an instance is printed in its definite, indefinite and negative forms.
There are two cases for each form, either as an article prepended to the short display form of the instance (its names or Mentioned
clause), or a complete form replacing the normal name printing.
Indefinite forms are used in e.g. inventory listings and when presenting instances that have no Description
clause.
Definitive forms are usually used in messages of the type:
The door is locked.
The negative forms are used in standard messages of the type:
I can’t see any door here.
Article
s and Form
s can of course, be inherited.
The predefined base class |
ARTICLE
Printing the indefinite (or definite or negative) form of an instance having an indefinite (or definite or negative) article is simply performed by executing the Article
statements and then the normal printing of the instance, usually the first set of names.
For example:
The owl IsA object
Indefinite article "an"
...
This results in output like:
There is an owl here.
You are carrying an owl.
An article is not used when the instance is displayed as the result of acting on multiple objects, e.g.:
> take everything
(owl) Taken.
For instances that should not have any article at all, like ‘some money’ or ‘Mr Andersson’, an Indefinite Article
clause containing no statements must be used:
The money Name some money
Article
...
Instead of:
There is a some money here.
This will produce the expected:
There is some money here.
FORM
If an instance has a Definite
(Indefinite
or Negative
) Form
, either through declaration or inheritance, the printing of its definite, indefinite or negative form will be by executing the corresponding statements only; no article declaration is involved.
In this way, the author gets complete control over the spelling and inflection of the instance name in definite, indefinite or negative forms.
Some human languages will probably require more use of the Form
form (like Swedish), and some less (like English).
The forms are particularly useful if the natural language used, have different forms of the noun itself in definite an indefinite forms.
An example is the Nordic languages, which use definite suffixes instead of articles.
Both Article
and Form
are inherited as a single property.
That means that an instance may override its inherited form using either of the two forms, regardless of how its parent defined the form.
Printing
You can use various forms of the Say
statement (see Sect. 23.1.4) to choose in which form the instance will be presented.
In addition, the embedded parameter references allow selection of the form (Sect. 23.1.1).
MENTIONED
The optional Mentioned
clause overrides the name for displaying an instance in a short form that will be used when the instance is mentioned e.g. in listings of containers or when the ALL form of player input is used.
A typical use of the Mentioned
clause is to let some internal state of the instance be reflected in the short form, e.g. if you want the short form of a box to show if it is open or closed you cannot rely on the Names since they are static.
Instead, the Mentioned
clause can print a different short name depending on an attribute.
mentioned = 'MENTIONED' {statement}
For example:
Mentioned
If mirror is broken then
"broken"
End if.
"mirror"
...
> take all
(little black book) OK!
(green pearl) OK!
(broken mirror) OK!
A |
13.9. CONTAINER Properties
An instance can also be a container.
This is declared by using the Container
property clause.
The grammar is
container_properties = ['WITH'] ['OPAQUE'] 'CONTAINER'
['TAKING' id]
[limits]
[header]
[empty]
[extract]
For example:
The chest IsA object
With container
Limits ...
Header ...
Description ...
:
End the chest.
A Container
is something that can contain instances.
By default, the instances it can contain must be inheriting from the base class object
, but by using the Taking
clause, you can allow any instances.
Instances with the container property, “inherits” a special, predefined, Boolean attribute, opaque
.
This attribute can be manipulated in the same way as any other attribute.
Its current value indicates if the instances inside the container are visible and accessible or not.
By default, containers expose their content, but by placing the keyword Opaque
in the container declaration, you indicate that this container declaration will initially prohibit access to the contained instances.
A typical use of this is to prohibit access to contents of closed cases, drawers and boxes.
Once open such containers usually reveal the content, which then can be accessed.
You can implement such behaviour by modifying the built in opaque
attribute.
For example:
The drawer IsA object
With opaque container
Header "The drawer contains"
Verb open
Does
Make drawer not opaque.
List drawer.
End verb.
End the drawer.
If you want to hide the content of a container, you have to take care so that a |
The predefined |
When an instance with the Container
property is encountered during game play, it will be described as usual.
By default the contents of the container will be listed unless it is empty or opaque.
If the default description has been overridden, the author will have to explicitly decide on how to handle the container aspect and its content.
LIMITS
The Limits
clause of the Container
property declaration makes it possible to define limitations on what and how much can be put in any given container (as a class or a instance).
These limitations are enforced by the interpreter during gameplay.
limits = 'LIMITS' limit {limit}
limit = limiting_attribute 'ELSE' {statement}
limiting_attribute = attribute_definition
| 'COUNT' integer
If any of these limits are exceeded when trying to put (i.e. locate
) anything inside the container, the statements in the corresponding Else
part will be executed and the player turn aborted.
In fact, these checks are performed because of the execution of a Locate
statement (usually, but not limited to, as a result of the player issuing a command with the intent of placing something in a container).
This means that the execution of a sequence of statements can actually be interrupted by a Locate
statement triggering limitations.
Each Limits
specification requires either a limiting attribute or the special meta-attribute Count
.
Multiple limitations can be defined on the same container.
In case of a limiting attribute, it must be a numeric attribute defined on the class taken by the container (by default, object
), and its implications are that the sum of this attribute on all objects in the container cannot exceed the specified value.
As for the special attribute Count
, it defines a limitation on the number of instances allowed in the container.
Container
Limits
weight 50 else "You can not lift that much."
Count 2 else "You only have two hands!"
The Count
limit considers all instances directly in the container.
This might differ from the number of instances listed e.g. if the container takes Thing
s (which are not automatically shown to the player).
E.g. if a container contains one instance of Thing
and one instance of Object
a listing will only mention the object, but the Count
limit will still consider the count to be 2.
The calculation of a limiting attribute always sums the values of that attribute for all contained items recursively. This includes the container itself and the added item, if it happens to be a container too. In effect the calculation is transitive (see Sect. 4.6.2). As described in Sect. 4.6.1, this process might involve instances that do not have the limiting attribute, in which case these are simply disregarded from the sum.
If an item is being located into a target container which also happens to be inside a container, the limits of that “outer” container are also checked. This is repeated so that the limits of any containing containers are checked.
Container
properties are inherited in its entirety.
Locations can’t have container properties.
HEADER and ELSE
Syntax:
header = 'HEADER' {statement}
empty = 'ELSE' {statement}
Header
is used when the contents of the container is listed.
It is intended to produce something like:
"The box contains"
or:
"You are carrying"
It is followed by a list of instances mentioned. Sect. 13.8.4 describes this listing.
The Else
part is used instead of the Header
if the container is empty.
If Limits
or Header
is missing, the Alan system supplies the default of no limits, and the messages output will be equivalent to:
Header
"The <container> contains"
Empty
"The <container> is empty."
(<container> is replaced by the actual name of the instance.)
EXTRACT
The Extract
clause defines what happens when anything is extracted from a container.
Any Locate
statement that moves an instance out of a container is considered an extraction.
The extraction will be subject to the restrictions enforced by the Extract
clause.
extract = 'EXTRACT' [check] [does]
| 'EXTRACT' {statement}
The Extract
clause, including optional Check
and Does
clauses, allows the author to prohibit the extraction of an item from the container depending on some condition.
If the Check
is present, it works the same way as for Verb
s (see Sect. 16.3), i.e. a Check
without a guard expression will unconditionally prohibit extractions; a Check
with an expression will evaluate that expression and, if false, execute its Else
clause, and then abort the move.
The Does
clause will be executed if the optional Check
passes, or if there was no Check
.
An Extract
clause without a Check
, but with a Does
clause, executes the Does
clause and then allows the extraction to take place.
So, in a way, Check
s, if triggered, prevents the extraction, and the Does
clause amends to it, being an extensions of the normal case, much like the Check
and Does
clauses for Verb
s (see Sect. 13.10).
The second form of the clause, with just the statements, is equivalent to an Extract
with only a Does
clause.
An example use of the Extract
clause is to prohibit, put restrictions on, or modify the behaviour when the hero attempts to take things carried by another actor.
The waiter IsA actor
At bar.
Is not annoyed.
Description
"A slow-moving, traditionally dressed waiter is here."
List waiter.
If waiter is annoyed then
"He is rather annoyed."
End if.
Container
Header "The waiter is carrying"
Else "The waiter is empty-handed."
Extract does "The waiter is annoyed by your presupposition."
Make waiter annoyed.
End the waiter.
Both limits and the |
13.10. VERBs
Verb
s declared inside an class or instance are inherited in the same way as other properties.
See Sect. 13.10 for a description on how to declare verbs.
The verbs in a class or instance will only be a candidate for execution if the instance bound to a parameter is of the corresponding class, or is the instance. See Sect. 16.7 for a detailed explanation.
13.11. ENTERED
Syntax:
entered = 'ENTERED' {statement}
The Entered
clause is only allowed in instances inheriting from the predefined class location
.
This clause will be executed whenever any actor enters the location.
Game state changes can be made without restriction.
However, the Entered
clause is primarily intended for setting up the location in a correct way, not for describing events, actions and states changes.
For this the Description
clause is recommended.
The Entered
clause can also be used to restrict the movements of actors other than the Hero
. (The hero’s travels are controlled by Exit
checks as described in EXITs).
If some of the statements should only apply to a particular actor, it is possible to test for the Current Actor
with a simple If
statement.
The actor is located at the location before the clause is executed so Current Location
will be the location having the clause.
Entered
clauses are inherited and locations can be nested (see Locations).
The order of execution is explained by the following table:
Outer Region | … | Current Location | |
---|---|---|---|
Base class |
Outermost |
🡳 |
🡳 |
⋮ |
🡳 |
🡳 |
🡳 |
Leaf class |
🡳 |
🡳 |
🡳 |
Instance |
🡳 |
🡳 |
🡳 |
This means that the first Entered
clause to be executed is the clause in the base class of the outermost location, if any, then moving down the inheritance of the outermost.
After that, any parent classes for any intermediate locations are considered in the same way.
Finally, running any Entered
clauses in the parents of the new location, ending with the clause in the location itself.
The |
If it is the hero that is moving, the |
13.12. EXITs
To build a traversable world of locations, they must be connected.
This is done using Exit
s.
The syntax for an Exit
declaration is:
exit = 'EXIT' id {',' id} 'TO' id [exit_body] '.'
exit_body = [checks] [does] 'END' 'EXIT' [id]
An Exit
has a list of identifiers, all of which are considered directional words.
I.e. when any of those words is input by the player, he will be located at the location identified as the target of the exit.
It is possible to customize the exit using a Check
, that must be satisfied to allow passage through the exit, and statements (Does
) that will be executed when the player passes through.
The checks work as described in Verb CHECKs.
If either of the Check
or Does
clauses is present, the End Exit
is required.
Two interconnected locations might be declared like this:
The east_end IsA location Name 'East End of Hall'
Description
"This is the east end of a vast hall. Far
away to the west you can see the west end."
Exit w to west_end.
End the east_end.
The west_end IsA location Name 'West End of Hall'
Description
"From this western end of the large hall it
is almost impossible to discern the
opposite end to the east."
Exit e to east_end.
End the west_end.
If an exit is declared from one location to another, and you want there to be an exit in the opposite direction, you have to define the reverse passage. It is not created automatically. |
Exits are only allowed in classes or instances inheriting from the predefined class location
.
13.13. SCRIPTs
The Script
is the way actors perform things.
In a way, it corresponds to what the hero is ordered to do by the player’s typed-in commands.
script = 'SCRIPT' id ['.'] [description] {step}
Every script has an identifier (the id) to identify it.
A script is selected by the Use
statement.
When an actor starts following a script, it will continue with one step after the other, with all the other actors, including the hero, taking turns.
The optional Description
allowed in the beginning of a script is used instead of the general description (from the instance declaration) whenever the actor is executing that particular script.
If it is not present, the general description is used.
Actor george
Name George Formby
Description "George Formby is here."
Script cleaning.
Description
"George Formby is here cleaning windows."
Step ...
Script tuning.
Description
"George Formby is tuning his ukelele."
Step ...
...
An actor continues executing its script until:
-
it reaches the end
-
another
Use
statement is executed for that actor -
the actor is stopped using the
Stop
statement -
something fails
There are a few things that might fail when an actor executes.
One example is an |
STEPs
A script is divided into steps.
Each Step
contains statements representing what the actor will do in what corresponds to one player move.
A step can be defined to be executed immediately, at the next move, to wait a number of moves before it’s executed or even to wait for a special situation (condition) to arise.
step = 'STEP' {statement}
| 'STEP' 'AFTER' expression {statement}
| 'STEP' 'WAIT' 'UNTIL' expression {statement}
For example:
Step wait until hero here
Locate waiter here.
"From the shadows a waiter emerges: $p
'-Bonjour, monsieur', he says."
Step after ticksleft of train
"The train driver enters the train, and after
a brief moment the train starts to move."
When an actor has executed the last step of the current script, it will do nothing more until the next Use
statement is executed for this actor (the actor will not act, but still present at the location where it was).
If this is not what you wanted, you can end each script with a new Use
statement.
14. Additions
In certain circumstances, you need to add properties to a class after it is defined.
A simple example of this is adding attributes to the predefined classes.
For this purpose, the Add
construct is available.
It follows the grammar:
addition = 'ADD' 'TO' 'EVERY' id
{property}
'END' 'ADD' ['TO'] [id] '.'
Using this construct, you can add any property to a class without having access to its declaration.
A standard library would make heavy use of this since it would be structured so that related verbs, their syntax and synonyms are packaged together.
If such a package required particular attributes in classes, they could be added using the Add
construct.
15. SYNTAX Definitions
The Syntax
construct is used to specify the allowed structure of the player’s input.
Each definition defines the syntax for one Verb
.
The effects triggered by the player input are declared using the Verb
construct (see Sect. 13.10).
syntaxes = 'SYNTAX' {syntax}
syntax = id '=' {element} syntax_end
element = id
| '(' id ')' [indicator]
syntax_end = parameter_restrictions
| '.'
The syntax is defined as a number of syntax elements each being either a player word (a single id) or the name of a parameter (an identifier enclosed in parenthesis). Parameters may be in any position, including the first. Bare in mind that a syntax definition containing only parameters might be tricky for the interpreter to match as you intended, for the complete set of allowed input can easily become ambiguous.
Syntax
quit = 'quit'.
examine = 'examine' (obj).
command_north = (act) 'north'.
unlock_with = 'unlock' (l) 'with' (k).
When the player types a command, it is compared to the set of declared syntaxes. This provides a very flexible way to extend the allowed command set (see also Chapter 34 for general details on player input).
After the player input has been matched to an allowed syntax, the parameters are bound to the instances referred to by the player, and the parameter identifiers in the Syntax
declaration will now refer to those entities.
Any references to attributes, etc., will be done in the instance referred by the parameter.
Syntax open = open (obj).
...
If obj is open then ...
...
In the above example, the parameter obj
can be used in the declaration of the open
verb and, at execution time, it will refer to the actual instance it was bound to.
Consider the unlock_with
syntax declaration of the previous example, the following table illustrates which instances the parameter identifiers l
and k
will be referring to in the actual game:
Player input | l |
k |
---|---|---|
> unlock the door with the key |
door |
key |
> unlock the bottom drawer with the rusty knife |
bottom drawer |
rusty knife |
> unlock the skeleton with the tiny blue chair |
skeleton |
tiny blue chair |
This, of course, provided that there is an instance that will match the player input, given the adjectives and nouns in the input and those in the instance declaration.
It is allowed to define multiple syntaxes for the same identifier (verb). See Syntax Synonyms.
15.1. Indicators
Following a parameter, indicators are allowed in Syntax
declarations.
indicator = '*'' | '!''
There are two indicators available (multiple and omnipotent):
-
*
— This parameter can reference multiple instances (for example by the player using ALL or concatenating a number of parameters using a conjunction like AND ; see Chapter 34). -
!
— The parameter (the instance the player refers to in this position in the syntax) need not be present at the current location. The default behavior of the Alan interpreter requires that a referenced instance must be present at the same location as the hero, if the parameter inherits fromthing
. (Note that entities are always accessible). For cases when the player must be able to refer to objects and actors that are not present (e.g. in a verb liketalk_about
) this omnipotent indicator can be used to force the interpreter to accept references to any object or actor.
An example:
Syntax
take = 'take' (obj)*.
drop = 'drop' (obj).
This shows the syntax definitions for the verbs take
and drop
; where take
also allows multiple objects.
This would make the following inputs possible:
> take everything except the pillow
> drop the vase
Refer to Chapter 34 for details on multiple parameters references in player’s input (such as objects).
The above declarations would force the interpreter to reject player input like:
> drop the shovel and the bucket
This is because the syntax for the verb drop
does not allow multiple references, as it doesn’t include the multiple indicator.
Another example, this time using the !
indicator:
Syntax
talk_about = 'talk' 'to' (act) 'about' (subj)!.
find = 'find' (obj)!.
Even if the robber or the key are not present, it will allow the player to say:
> talk to the policeman about the robber
> find the key
For more information on player inputs, refer to Chapter 34.
Indicators given in one syntax declaration can affect other syntaxes if they have identical beginnings, like:
> put everything on
and
> put everything on the table
Even if only one of the syntax declarations indicate that the first parameter should allow multiple instances, both syntaxes will actually allow this because they have the same syntax part before the parameter, in this case the verb “put”.
15.2. Parameter Restrictions
To restrict the types of entities the player may refer to in the place of a parameter, its class can be defined by using explicit test in the syntax declaration.
parameter_restrictions = 'WHERE' restriction
{'AND' restriction}
restriction = id 'ISA' restriction_class
'ELSE' {statement}
restriction_class = id
| 'CONTAINER'
Any predefined or user defined class can be used. |
Don’t forget that also |
The following example describes the syntax for a verb that only allows object
s as its parameters (this is however also the default, see below).
Syntax
take = 'take' (obj)
Where obj IsA object
Else "You can't take that."
Each parameter may be restricted to refer only to instances of particular classes or instances with the container
property, or numeric or string literals.
The statements following the Else
will be executed if that restriction is not met, i.e. if the player refers to an instance not belonging to the specified class or classes.
The default restriction is object
, i.e. if no class restriction is supplied for that parameter identifier the player may only refer to objects at that position in his input.
A more elaborate example of prerequisites for conversation might look like:
Syntax
talk_about = 'talk' 'to' (act) 'about' (sub)!
Where act IsA actor
Else "Don't you think talking to a person
might be better?!?!"
And sub IsA subject
Else
Say act. "does not know anything about that."
...
You can provide multiple restrictions, even for the same parameter; but if they refer to the same parameter they must be presented in increasingly restrictive order. For example:
Where obj IsA object else ...
And obj IsA openable_object else ...
And obj IsA door else ...
References to attributes in the source are only allowed if it can be guaranteed that they exist during run-time.
The class restrictions placed on a parameter are used by the compiler to make this guarantee for code executed by player input (verb bodies).
The same applies for other semantic restrictions, e.g. you can only use a parameter in a List
statement if it has been restricted to having the Container
property.
You can use IsA Container
to restrict instances to only those entities that are containers (have the Container
property).
If there is no restriction for a parameter, it is restricted to the class object
.
15.3. Syntax Synonyms
It is possible to create multiple syntax declarations for the same verb. The semantics of this is that any of the input formats will be accepted and trigger the same verb action. This is a way to define syntactical synonyms, which are useful to allow multiple forms of input for the same action, increasing chances that the player will find the correct form. For example:
Syntax give = give (o) to (p) ...
Syntax give = give (p) (o) ...
The syntaxes must be compatible in the sense that the parameters must be named the same. However, the order of the parameters may differ, they will automatically be mapped as appropriate.
Restrictions are only allowed in the first of such syntax declarations. These restrictions will be applied regardless of which syntax was used.
15.4. Default Syntax
If no Syntax
is defined for a Verb
at all, this is handled with one of two default syntaxes according to the two templates below:
Syntax <1> = <1>.
Syntax <1> = <1> (<2>).
The placeholders represent 1) the name of the verb, and 2) the class in which the verb is first encountered.
The first template is used for verbs that are declared globally, i.e. outside of any class or instance.
Since these are only applied when no parameters are used, this will effectively work for simple ‘verb-only’ Verb
s, such as ‘quit’, ‘look’, ‘save’, etc.
When a Verb
declared in an instance or a class has no Syntax
counterpart, it automatically receives a default syntax of the common verb/object type corresponding to the second template above.
This is a reasonable syntax for many cases and restricts the parameters to instances of the class where the verb was declared.
It also implies that the default name for the single parameter is the same as the name of that class, e.g. object
, actor
, thing
, etc. (See Chapter 25 for the implications of this.)
A |
When a |
15.5. Scope
If the player inputs a command following a syntax which requires parameters, the interpreter first determines if the referenced instance is in scope. This is performed even before the restrictions are executed.
There are a number of ways to get an instance into scope:
-
Instances of
entity
, and of any user defined subclasses thereof, are always in scope. -
Instances of
thing
and its subclasses at the current location, including any nested locations, are in scope. -
Instances of any class inside a container that is in scope are in scope too, unless that container is opaque and closed. (See Container Properties for details.)
-
If the syntax indicated a parameter as omni-potent, any instance is in scope for that parameter position. (See Indicators for details.)
If the interpreter finds multiple instances matching the input (the set of given adjectives and noun), it will try to disambiguate giving preference to present instances — i.e. at the location of the hero. If there are still multiple candidates left after this, the interpreter will print a message and abort execution of the current command.
When all parameter positions in the syntax have been resolved this way, the restrictions are executed.
16. VERBs
A Verb
declaration specifies what to check and the effects of something the player does (i.e. commands using a syntactically legal input).
verb = ['META'] 'VERB' id {',' id}
verb_body
'END' 'VERB' [id] '.'
verb_body = simple_verb_body
| {verb_alternative}
simple_verb_body = [check] [does]
Verb take, get
...
End verb take.
Verbs can be declared at two different levels:
-
global (outside any other declaration), or
-
inside a class or instance declaration, including inside an
Add
construct.
A global declaration will only be considered when the verb is not applied to any instance (i.e. such as the player referring to an object). In fact, a global verb cannot include any parameters in their syntax declaration.
A verb declaration inside a class or instance definition will be considered if that instance (or an instance inheriting from that class) is used as a parameter in the input.
The identifiers in the list (take
and get
in the example above) will become the default player words that can be used to invoke the verb.
But if a Syntax
is declared for the Verb
(see Chapter 15), the identifiers in the list will not be accessible to the player, instead the sequence of words and parameters specified in the Syntax
must be used.
If there is more than one identifier in the list, as in the example above, this can be viewed as a shorthand for declaring identical checks and bodies for all the verbs in the list. This will create synonymous actions for different verbs at the level of the verb declaration. They may differ in implementation at other places, i.e. if they are declared in the same verb declaration on one level in an inheritance tree, they can still have different bodies on another level.
16.1. META VERBs
Any action from the player usually takes one ‘tick’ in the default simulated game time. Sometimes you want a player command to not consume a ‘tick’, for example administrative commands like ‘help’, ‘score’ etc.
You can achieve this by attaching Meta
in front of the verb definition:
Meta Verb 'score'
Does
Score.
End verb.
If your Verb
has multiple definitions (e.g. for various classes) applying Meta
to any one of them will make the verb a meta verb, meaning that if the player uses that verb in any context and on any instance, it won’t consume a tick, even if that particular definition did not have the Meta
property explicitly expressed.
A library might decide that 'score'
was a meta verb and there is nothing you, as an author, can do to override that short of editing the library source.
Furthermore, a Meta
verb does not trigger evaluation of rules and Event
s either, so they are genuinely “outside” the game and should only be used with verbs that are not considered part of the players progression inside the game.
The |
16.2. VERBs in Locations
A special case is a Verb
declared in, or inherited by, the location
where the player is currently located.
If this verb is used, any checks or body of that verb will be considered before the verbs in the parameters.
An example might be a location representing walking on a high wire.
Anything dropped at the following location will disappear:
The high_wire IsA location
Verb drop
Does only
Locate o at limbo. –- Instead of "here".
End verb.
End the.
16.3. Verb CHECKs
Syntax:
check = unconditional_check
| check_list
unconditional_check = 'CHECK' {statement}
check_list = 'CHECK' expression 'ELSE' {statement}
{'AND' expression 'ELSE' {statement}
To determine if the action is possible to carry out, the checks in Check
are executed.
Which checks to run, is determined by the class of the instances bound to the verb by the parameters.
All checks in the inheritance tree are tried by starting at the base class.
This way, the more general checks are tried first, followed by increasingly more specific checks.
A typical use of Check
is to verify if the parameter has a particular property:
Verb take
Check obj is moveable
Else "You can't take that."
...
End verb take.
If no expression is specified for a Check
, that check will always fail, in effect becoming an unconditional Check
.
This is useful for preventing certain actions, for example at specific locations, since the checks are always executed first:
The jumpless IsA Location
Verb jump
Check "You can't do that here."
End verb jump.
End the jumpless.
If any check should fail, the execution of the current verb is interrupted and the statements following the failing check are executed.
The user (player) is then prompted for another command.
So in the above example, the verb “jump” will always result in “You can’t do that here.” at the location jumpless
.
|
With this in mind, Check
s are also used when handling the user input ALL (see Chapter 34 for details on possible player input).
The mechanisms for this involve examining all objects at the current location and evaluating all checks for the verb.
Any objects that do not pass the checks are not considered for execution.
This limits the handling of ALL to executing only the verb bodies of objects that are reasonable, i.e. whose Check
s will not fail.
For example assuming the above definition of the verb take
and a location containing the objects ball
and box
, where only the ball is moveable
, the player input:
> take all
would result in ALL representing only the ball. See Chapter 34 for an explanation of the player view of this.
16.4. DOES Clause
Syntax:
does = [qualifier] {statement}
qualifier = 'BEFORE'
| 'AFTER'
| 'ONLY'
If all checks succeed, the execution of the verb will be carried out.
Multiple verb bodies may be involved.
The default order is to execute first the body of any verb declaration for the current location (including verb bodies inherited by it).
Each parameter is then examined to find any declarations of that verb for the instance (including inherited verb bodies).
These verb bodies are then executed in the order in which the parameters occurred in the Syntax
declaration, for each parameter starting with the body in the most basic class.
By default, all of the involved verb bodies are executed.
This is the most natural order and covers most cases.
In some unfrequent situations, another order may be necessary.
By using the qualifiers, Before
/After
/Only
, the author can decide which verb bodies will be executed and in which order (see Verb Qualification below for details).
A simple verb example:
Verb take
Check obj not in inventory
Else "You already have that."
Does
Locate obj in inventory.
End verb take.
16.5. Verb Alternatives
Syntax:
verb_alternatives = 'WHEN' id simple_verb_body
When a Verb
is declared within an instance declaration, verb alternatives are allowed.
These alternatives are used in conjunction with the Syntax
declaration defined for the verb and allow differentiating between the instance occurring at different parameter positions in the input.
When a player inputs a command, each parameter in the syntax (see above) is bound to an actual instance or receives the value of a literal, depending on the specified syntax. To determine which checks to test for, and which verb bodies to execute, the parameters are examined in turn according to the algorithm described in the section Verb Qualification below. Each instance may have different verb bodies executed depending on which position it occurred at (to which parameter it was bound).
For example, assume the following syntax definition:
Syntax break_with = 'break' (o) 'with' (w).
If used with the delicate_vase
, the resulting actions could differ depending on whether it occurs as the direct object (o
) or as the indirect object (w
).
To implement this, the Verb
body for break_with
should differentiate between the two cases.
For each parameter in the Syntax
, you may define different actions by supplying a verb alternative for each parameter identifier.
The verb declaration could look like:
The feather IsA object
Verb break_with
When o Does
"The feather is even more flat than before."
Make feather flat.
When w Does
"There is not much that you can break with a feather!"
End verb break_with.
End the feather.
If no alternative is explicitly specified the verb body will be considered for all positions in the syntax. The compiler will warn for this when it detects a syntax that allows the class or instance to occur at every parameter positions.
16.6. Verb Qualification
Syntax:
qualifier = 'BEFORE'
| 'AFTER'
| 'ONLY'
The order in which the different verb bodies are executed is normally from the most general to the most specific. But, to allow for local differences (i.e. special handling of the verb at the current location) any definition of the verb in the current location (including inherited verb bodies) are considered first. Then, the verb bodies in the parameters on which the verb was applied are examined (in their order of appearance in the syntax definition) to find and execute their verb definitions. For each parameter, its most general definition is executed first, then verb bodies down the inheritance tree are executed next, ending with any verb body declared in the specific instance bound to that parameter.
In most circumstances, this is the most logical order, but if another order is required, the Verb
qualifiers After
, Before
and Only
may be used to alter this behaviour.
These qualifiers alter the order of execution, as described in full detail below.
16.7. Verb Execution
First all parameters are evaluated according to the Syntax
restrictions (see Parameter Restrictions).
Then, if they passed, the Check
s of all verb declarations are evaluated (see Sect. 16.3).
Finally the Verb
bodies are executed in the normal order, as explained in the following table.
Outer Region | … | Current Location | First parameter | … | Last parameter | |
---|---|---|---|---|---|---|
Base class (entity) |
Outermost |
🡳 |
🡳 |
🡳 |
🡳 |
🡳 |
⋮ |
🡳 |
🡳 |
🡳 |
🡳 |
🡳 |
🡳 |
Leaf class |
🡳 |
🡳 |
🡳 |
🡳 |
🡳 |
🡳 |
Instance |
🡳 |
🡳 |
🡳 |
🡳 |
🡳 |
Innermost |
The table above illustrates the normal order of execution of verb bodies and checks. Starting with any base classes to the outermost region (containing location), continuing to the actual instance of that location, as illustrated by the first column. It then continues with any inner regions (second column) and the current location itself (third column). The execution then proceeds to the parameters of the syntax in order (columns four through six), traversing the inheritance tree from the base class to the instance.
If you add a verb to the class |
Controlling Execution with Qualifiers
There are cases where you don’t want all the bodies to be executed, or there is a special need to execute them in a different order.
The most common case is to prohibit other bodies to be executed, e.g. a verb body in a location might want to stop the player from throwing any object.
This verb body must then ensure that it is the only verb body to be executed.
This can be done using the Only
qualifier (see Sect. 16.6).
Qualifiers control the order of execution of verb bodies. How does this work?
First, starting at the “innermost” according to the table above, the verb in the last parameter (if any) is investigated and, if any of its (inherited) verb bodies have the Before
or Only
qualifier it is executed.
If the qualifier was Only
then execution is also aborted at this stage and no more verb definitions are examined, otherwise the other parameters are examined in the same way.
In the next step, the current location is examined and, if it contains (or inherits) a verb definition with a Before
or Only
qualifier, that definition is executed now (and if the qualifier was Only
, execution is also aborted).
Since locations can be nested, the surrounding locations are then examined in the same way.
As a result of this behaviour, a Before
qualifier in the verb definition in an object parameter will supersede an Only
qualifier in the location.
At this stage, all Before
and Only
qualifiers are handled appropriately.
This only leaves the definitions without any qualifier or with the After
qualifier.
The outermost verb body (as indicated in the table above) is examined and if it did not have the After
specification, it is executed (if it had an Only
qualifier then no furhter executions will be taken into consideration).
Any definition of the verb in the current location is again examined and, if it did not have the After
qualifier, it is executed.
What remains is to execute the verb definition in the parameters if they have not been executed already, and to execute the location definition if they where declared with the After
qualifier.
So in short (with base class definitions of the outermost location being the outermost and the instance bound to the last syntax parameter the innermost):
-
From the inside out, find any
Before
orOnly
definitions and execute them (stop ifOnly
found). -
From the outside in, execute any definitions not already executed and not declared with the
After
qualifier. -
Execute the remaining verb definitions (those with an
After
qualifier) from the inside out.
The second item in the above list is equivalent to the normal order of execution, without any qualifiers.
The qualifiers are a powerful but confusing concept. The normal order of execution is usually appropriate and only in special cases should qualifiers be used. When they are needed, you will find that one qualifier at the correct definition will normally do the trick. The above algorithm is used to get a strict definition of the execution order. It is not expected that all this complex behaviour will be needed in practice.
All |
An example of the use of qualifiers is to ensure that only the verb body within the object is executed:
The bomb IsA object
Verb examine
Does only
"Your curious fingering at the intricate
mechanism sets it of. BOOOM!"
Quit.
End verb examine.
End the bomb.
This also illustrates the fact that the most commonly used qualifier is the Only
qualifier since it is used whenever all other behaviour is replaced by some special behaviour.
17. EVENTs
An Event
is a sequence of statements executed at a specified time (count of turns).
It is also executed at some specific location.
An event can e.g. be used to create an explosion where the bomb is three moves from now or to let the ceiling of the cave fall down in five moves.
Event nearby_explosion
"Somewhere in the distance there is an explosion."
Make bomb gone_off.
Schedule small_avalanche after 2.
End event.
The body of an event can be any sequence of statements.
however, they can not refer to any parameters, since no verb is executing, nor to the Current Actor
.
See Run-Time Contexts.
Events may be scheduled and cancelled with the Schedule
and Cancel
statements (see EVENT Statements).
18. Rules
Syntax:
rule = 'WHEN' expression ('THEN' | '=>')
{statement}
['END' 'WHEN' '.' ]
A rule is an arbitrary expression, which, when true, results in the execution of some given statements. Rules can only be declared on the global level (not inside classes or instances). The main intended use of rules is to detect particular situations and then trigger some action. Typically they can be used to make things happen when certain situations arise, such as starting an actor when the hero enters the cave.
Here is an example that investigates if the hero is in the cave and if so, activates the monster:
When hero at cave and monster not active then
Use script hunting for monster.
End when.
The expression that is tested may of course have any level of complexity:
When hero at cave
And (monster is hungry or monster is angry)
And sword not in hero
=>
Use script eat_hero for monster.
End when.
Each actor action and event execution is considered atomic (it can’t be divided into smaller parts). All rule conditionals are evaluated after each actor (including the player) has acted (script step and player command respectively) and after each event has executed. In effect this will mean that a change in state will be detected almost immediately, if there is a rule for detecting that change.
The statements within the rule are triggered when the condition becomes true.
In the first example, this means that if the monster is not active, the statements will be executed when the hero enters the cave (hero At cave
becomes true).
A rule body can never be executed twice in succession unless the conditional has been evaluated to false in between.
In the example above, the triggering of the hunting
script for the monster will not happen again unless either the hero has left the cave and entered it again, or the monster has been active
and then become NOT active
again.
The use of parameters, Current Actor
, Current Location
, Here
and Nearby
is not allowed in rules conditionals or bodies.
Rules are executed at no location.
Therefore, within rules it’s not possible to communicate directly with the player via output statements (since the hero cannot be where the rule is executing, see Sect. 23.1).
Triggering an Event
that handles the output intended for the player is the recommended solution to this.
The following is a complete game using a rule:
The kitchen IsA location
Exit x to kitchen.
End the kitchen.
When count isa actor, at kitchen = 1
then schedule whee at actor after 0.
End when.
Event whee
"Whee!"
End event.
Start at kitchen.
In this example the rule conditional (i.e. Count IsA actor, At kitchen = 1
) is using an aggregation (count
, see Sect. 26.13) over two filters (see Chapter 27) that will count the number of actor
s at the kitchen
, and when that number becomes one, the rule will trigger and execute the statements, in this case scheduling an event that handles the presentation of the output to the player.
Again, remember that rules are checked after each actor has moved. What happens if there are more actors in play and they move in and out of the kitchen, is left as an exercise to the reader.
19. SYNONYMS
Syntax:
synonyms = 'SYNONYMS' {synonym_declaration}
synonym_declaration = word {',' word} '=' word '.'
A Synonyms
declaration declare words that, when used in player input, are always interchangeable.
For example:
Synonyms
'i', 'invent' = 'inventory'.
'q' = 'quit'.
The word on the right hand side of the equal sign must be a word defined elsewhere in the adventure source, such as (part of) an instance name (a noun or adjective), a direction or a verb. The list of words on the left-hand side contains new words (not defined elsewhere) that always will be interpreted as being replaced by the word on the right in the player input.
Synonyms are player words that can be interchanged. Defining synonyms for verb names will not always give you the result that you expect. The following example is incorrect.
Synonyms
'examine' = look_at.
Syntax
look_at = 'look' 'at' (obj).
Verb look_at ...
This will result in an error message indicating that the synonym word look_at
is not defined.
This is because the Syntax
(see Chapter 14) defined the verb look_at
to have the specified syntax (including the player words ‘look’ and ‘at’), the player word look_at
is not defined, which is as well as the player would not be able to input a word with an underscore (see Chapter 34).
You can achieve the desired effect by instead giving multiple verb identifiers in the verb declarations; this will give the same verb bodies (checks and actions) to multiple verbs. See the section on VERBs for details on verb declarations.
It is also possible to define multiple names for an instance to achieve other effects similar to synonyms. See Sect. 13.3 for a description of this.
20. MESSAGEs
The Alan system has a number of standard messages built in. These messages are presented to the player in various situations, both normal and otherwise. An example is the following:
> go north
You can’t go that way.
The response “You can’t go that way.” is a typical example of such system messages (for details see Sect. C.1).
To better adapt the user dialogue to the settings you select, Alan allows you to define your own version of these messages. The grammar for this is
messages = 'MESSAGE' {message}
message = id ':' {statement}
An example would be:
Message
NOWAY: "There is no exit in that direction."
If the above where used in the source for the same game as the previous example, it would instead look like:
> go north
There is no exit in that direction.
The Message
constructs allows general statements following the message identifier:
Message NOWAY:
If random 1 to 2 = 1 then
"There is no way in that direction."
Else
"You can't go there."
End if.
The standard message for Noway
is replaced by the output from the statements in the definition.
For a complete list of all the identifiers of messages and their use, see Appendix C.
20.1. MESSAGE Parameters
Message
sections must be declared at the global level, but to make it possible to create high-quality messages, parameters are available in message sections.
Which parameters are available vary depending on the message, the details for each message is available in Sect. C.1.
The parameters can be used in the same way as in verb bodies. The names of the parameters are “parameter1”, “parameter2”, etc. The type of the parameters will also vary.
For some messages, a parameter is an instance.
In these cases, the instance is always of the predefined entity
class.
Any attribute available for this class will be available in message sections with instance parameters.
If the message must be modified according to the case of the noun, which is the case with adjectives and negative forms in many languages, an attribute available on all instances can be used to select the correct form. |
21. PROMPT Section
The Prompt
section allows you to customize the way players are prompted for their input.
prompt = 'PROMPT' {statement}
The default prompt for player input, which will be used if no Prompt
section is declared, looks like this:
>
Using the following Prompt
section it can be set to something else:
Prompt "What now?"
Then the player will of course see:
What now?
In fact, the Prompt
section allows any statements, not just strings.
So you can have the prompt change during the game.
Prompt
"Hello" say hero. "!$n"
"Where do you want to go from"
Say current location. "?"
This will give the following output:
Pirates Bay Harbor
You can see the town of Pirates Bay to the north, and your ship is at the docks, to the south.
Hello Jack Sparrow!
Where do you want to go from Pirates Bay Harbor?
The prompt section is global and applies to the whole game.
There is currently no way to dynamically customize the prompt except by using the statements inside the |
22. START Section
The Start
section defines where the player (the hero) will be at the start of the game.
This must be a location.
Optionally this may be followed by statements to be executed at the beginning of the game, such as hello-messages or short instructions as well as starting any actors and scheduling events.
start_section = 'START' where '.' {statement}
An example would be:
Start at outside_house.
Schedule bird_chirp after 5.
Only the At What
form of the WHERE construct (see Chapter 24) is allowed in the Start
section.
Any statements are allowed in the start section but they cannot refer to any parameters.
The start section must be the last declaration in an Alan source.
23. Statements
23.1. Output Statements
There are various ways to present output to the player: string output, descriptions, printing expressions, listing container content and showing pictures.
The interpreter intersperses your output with spaces whenever needed. This might for example occur between two output strings:
"There is a door into the kitchen."
If kitchenDoor is open then
"It is open."
End if.
If handled simple-mindedly the two texts would be adjoined and you as an author would need to cater for this.
Instead Alan realizes that a space is required between them.
This space is automatically inserted by the interpreter during game play.
This is also the case if the output from a Say
statement is followed by an output string.
"Your wristwatch shows" say hours of watch.
". Time to go."
However, as in this example, this is not always the intended output.
Particularly, if the Say
statement terminated the previous sentence, as in the example, we want the full stop to be placed immediately after the output.
So, the Alan interpreter will leave out the space between two outputs if the second starts with a period (full stop) followed by a space, or is the single character in the string.
This special handling also applies to strings starting with a comma.
Whenever an output statement is executed, the result will be printed on the players terminal with the following important exception: if an output statement is executed at a game location other than the hero’s location, the output won’t be shown. This important feature will relieve the author from the burden of constantly considering what the player will see. It can be used in the following way:
"Charlie Chaplin leaves the house through the front door."
Locate charlie_chaplin at outside_house.
"Charlie Chaplin comes out from the nearest house."
If the hero is inside the house or out in the street, he will get different views of the situation. This feature ensures that the player only sees what is going on at the current location, and allows for easy adaptation to various viewpoints on the events without the need for any special tests. But see Distant Events for a solution in case the hero need to be informed about things happening in other locations.
String Statement
Syntax:
output_statement = string
The simplest case of output is just a string, i.e. any text, possibly stretching over multiple lines, surrounded by double quotes. See also Chapter 31 for some detailed descriptions on the definition of strings.
Some character combinations have special meaning for the printout:
$p New paragraph (usually one empty line) $n New line $i Indent on a new line $t Insert a tabulation $$ Escape from automatic space insertion and capitalization $a The name of the actor that is executing $l The name of the current location $v The verb the player used (the first word) $_ Print this as a '$' if in conflict with other symbols
You might want to output “$400” but “$4” will be interpreted as the indefinite form of the fourth parameter, as described below.
So you need to use the ‘_’ to make that happen ( |
The following can be used to refer to parameters while executing a verb, but the Say
statement (see below) is safer and preferred whenever possible:
$<n> The parameter <n> (<n> is a digit > 0, e.g. "$1") $+<n> Definite form of parameter <n> $0<n> Indefinite form of parameter <n> $-<n> Negative form of parameter <n> $!<n> Pronoun for the parameter <n> $o The current object (first parameter)
The |
The use of |
STYLE Statement
Syntax:
style_statement = 'STYLE' style '.'
style = 'NORMAL'
| 'EMPHASIZED'
| 'PREFORMATTED'
| 'ALERT'
| 'QUOTE'
The style of the text output can be controlled using the Style
statement.
With the exception of the Emphasized
style, the styles are intended to be applied to whole paragraphs.
The style indicated in the statement applies until another Style
statement is executed.
The exact visual appearance of the styles is implementation dependent. In fact, there is no guarantee that the styles will actually differ. |
DESCRIBE Statement
Syntax:
output_statement = 'DESCRIBE' what '.'
The Describe
statement executes the description part for an instance, such as an actor, an object or a location.
If no such description exists a default description, such as:
There is a coin here.
is used instead.
In this case, if the instance has the container property, a List
statement is also executed for that object automatically (see below).
If a Describe
statement is executed for another instance during the execution of the Description
clause, the system will recognise this and make sure that the second instance is not described more than once.
This makes it possible to use instances as parts of a location and embedding their description at the correct place in the longer description of the location.
"This office is dusty and probably hasn't been used for
many years."
Describe desk.
"To the west is an open door, and to the east you can see the
staircase.""
SAY Statement
Syntax:
output_statement = 'SAY' [form] expression '.'
form = 'THE' | 'AN' | 'IT' | 'NO'
The Say
statement will output a short description of what is referred to by the expression.
If it refers to an instance, it will print its name or execute its Mentioned
clause if one is available.
If it refers to an attribute, it will print its value, such as an integer or a string.
Parameter names are also allowed in the Say
statement, which, of course will result in a short description of the instance to which it is bound, or in a literal printout if the parameter was a string
or integer
.
If contents of bottle > 0 then
"In the bottle there are still"
Say contents of bottle.
"litres of water left."
Else
"The bottle is empty."
End if.
If the WHAT part refers to an instance, the optional form may be used to control in which form the instance will be output.
If The
is used, then the definite form will be employed, which is usually the short form preceded by a definite article.
Correspondingly, the use of An
indicates an indefinite form.
A third form, using No
, is also available.
It indicates that the negative form (as defined by the negative article or form) should be output.
Refer to Sect. 13.8 for a description of the definite/indefinite articles and forms.
Finally, the It
form will print the pronoun associated with the instance.
LIST Statement
Syntax:
output_statement = 'LIST' expression '.'
The List
statement lists all objects in a container together with the Header
as specified for the container.
If the container is empty, the statements in the Empty
clause of the container are executed instead.
"The chest is heavy."
If chest is open then
List chest.
End if.
Of course, the instance being listed must be an instance that has the Container
property, which may be inherited.
This instance can be referred to by being bound to a parameter or a reference attribute, for example.
23.2. Multimedia Statements
Alan has some multimedia provisions, although they may not be available on every platform and implementation.
The Show
statement, presents an image in the output window, and the Play
statement plays a sound.
The Alan compiler will always support the multimedia statements, but a particular interpreter might not do so. Most GLK-based interpreters will support them, but other kind of interpreters might too. The game will still play fine, but the multimedia resources will be silently ignored. There is also no way to check for this in your source code. So, don’t rely on them for your story, particularly do not give the player necessary information only through pictures.
Image and sound files are analyzed by the compiler and copied into an Alan v3 resource file (file extension .a3r) that must be distributed with your game file, otherwise they will not be available during game play. The original file will be left untouched.
The format of the resource file follows the standard interactive fiction resource file format “blorb” and supports images of JPEG and PNG types, and sounds in MOD and AIFF formats.
If a resource file is referenced from multiple statements, it will only be copied once. The Alan compiler uses the file extension to determine the media type of the file. The following extensions are recognized: .jpg, .jpeg, .png, .mod, .aif and .aiff.
SHOW Statement
Syntax:
output_statement = 'SHOW' id '.'
The id should be the name of an image file. Since filenames may contain various special characters, a quoted identifier (see Filenames) is usually required.
Alan currently supports the JPEG and PNG formats only.
PLAY Statement
Syntax:
output_statement = 'PLAY' id '.'
The id should be the name of a sound file. Since filenames may contain various special characters, a quoted identifier (see Filenames) is usually required.
Alan currently supports the MOD and AIFF formats only.
23.3. Manipulation Statements
LOCATE Statement
The Locate
statement is a way of transferring instances to new locations.
When executed, the indicated instance will be placed at the specified location.
locate_statement = 'LOCATE' what where '.'
For a description on how to specify WHERE, see Chapter 24.
When an actor is located at a new location the Does
clause of that location is always executed.
One special case of the Locate
statement is when the predefined actor hero
is located somewhere.
This is analogous to the player typing a direction, i.e. the hero will be located at the appropriate location.
Under particular circumstances, you may want to locate the player at a different location as a side effect of another action.
For example:
Event explosion
"Suddenly the door seems to bulge outwards, it bursts
open throwing rocks and splinters everywhere. The
impact of the explosion literally throws you back
out in the hallway."
Locate hero at hallway.
End event explosion.
In this case, the new location will be described and the Does
clause of that location executed.
Another special case is when locating something inside a container.
The Locate
statement will then cause the execution of the Limits
of that container, and if any of the limits are exceeded the complete player turn is aborted immediately, resulting in no more statements being executed.
So, if a player command should result in locating an object inside a container, it’s better to place the Locate
statement as early as possible, as this enforces the Limits
checks at the beginning of this player turn.
A very special third case is locating a location at another location. Locations can be nested this way, resulting in an outer location working as a region or surrounding for the inner location. The effect of this is that any instances present in the outer location are reachable from the inner.
EMPTY Statement
The Empty
statement locates all instances currently located inside the given container (instance with the Container
property) at a certain location.
empty_statement = 'EMPTY' what [where] '.'
The meaning of the WHERE part is the same as in the Locate
statement.
If it is not specified the instances will be placed at the current location.
Empty inventory here.
"You seem to have lost most of your possessions. Well,
you can't have everything."
Locate hero at restart_point.
STRIP Statement
The Strip
statement is used to manipulate the contents of strings.
strip_statement = 'STRIP' [direction] [count] [size]
from_clause [into_clause] '.'
direction = 'FIRST' | 'LAST'
count = expression
size = 'WORDS' | 'CHARACTERS'
from_clause = 'FROM' expression
into_clause = 'INTO' expression
You can use it to remove words or characters from a string, starting from the beginning or the end.
The words or characters that are removed may be placed in an attribute as specified by the optional Into
clause.
If the statement is used to manipulate words, blanks and separators are used to separate the words.
In this case, any resulting string is also free of leading and trailing blanks.
A short example:
The eliza IsA actor
Has topic "".
Verb talk_to
Does
Set topic of eliza to "sailing music cooking reading".
Strip random 0 to 2 words from topic of eliza.
Strip first word from topic into topic.
"And how do you feel about" say topic of eliza. "?"
End verb.
End the eliza.
23.4. EVENT Statements
SCHEDULE Statement
Schedule
will queue an event to occur at a specified location after the number of player turns specified by the expression.
event_statement = 'SCHEDULE' what [where]
'AFTER' expression '.'
For example:
Schedule ringing at clock after 60 - minutes of clock.
The number of moves can be zero, i.e. After 0
means that the event will occur now (during this player turn, probably last, though).
If no location is specified, Here
is assumed, i.e. it will be executed at the current location, the location where the statement itself was executed.
An important case is when a Schedule
statement without a WHERE clause is executed inside a rule.
Since rules are executed at nowhere so will the event.
This means that any printout will occur nowhere, and will thus be invisible to the player.
The WHERE expression can be any expression yeilding an instance type, such as the identifier of a location or actor, an attribute referring to an instance. The event will occur wherever that instance is when the event occurs. The event will ‘follow’ the instance.
There is an interesting difference between the following two statements:
Schedule explosion at hero after 1.
Schedule explosion at location of hero after 1.
The first statement will schedule the event at the hero and will execute where the hero is after one turn. The second will schedule the event at the location where the hero was when the statement was executed, even if the hero has moved after that.
Executing a second Schedule
statement for the same event before it has occurred will reschedule the event to the new time.
An event can only be scheduled for one execution at a time.
The event can be specified by giving an event identifier or referring to an attribute of |
CANCEL Statement
Cancel
will remove the referenced event from the queue of scheduled events.
cancel_statement = 'CANCEL' what '.'
Canceling an Event
which is not currently scheduled is not considered an error.
Event ticking
"Tick..."
If timer of bomb = 0 then
Schedule explosion after 1.
Else
Decrease timer of bomb.
Schedule ticking after 1.
End if.
End event ticking.
Verb defuse
Does
Cancel ticking.
Cancel explosion.
"Phuuui! That was close."
End verb defuse.
Start at office.
"The bomb is ticking..."
Schedule ticking after 1.
The event can be referenced using any expression of Event
type, e.g. an attribute.
23.5. Assignment Statements
There are a number of statements for changing values of attributes.
MAKE Statement
The Make
statement is used to set or reset Boolean attributes.
make_statement = 'MAKE' what something '.'
something = ['NOT'] id
Examples:
Make door open.
Make door not open.
INCREASE and DECREASE Statements
Syntax:
increase_statement = 'INCREASE' what [by] '.'
decrease_statement = 'DECREASE' what [by] '.'
by = 'BY' expression
The Increase
and Decrease
statements modify the value of numeric attributes by increasing or decreasing it by the value of the expression given in the optional By
clause.
If no By
clause is specified the attribute is changed by one.
Increase level of bottle by contents of mug.
Decrease lives of hero.
SET Statement
The Set
statement is used when assigning values to numeric, string, reference or set valued attributes.
set_statement = 'SET' what 'TO' expression '.'
Examples:
Set mood of king_tut to 3.
Set hour of clock to hour of clock + 1.
Setting attributes of reference or set type requires that the expression follows the type and subclass compatibility rules. For example, you can only assign:
-
integer type expressions to an integer attribute:
Set intAttr to 4. -- Correct if intAttr is of Integer type Set intAttr to "hi". -- Incorrect
-
instance type expressions to a reference attribute whose class is a parent of the instance in the expression:
Has suspect butler. Set suspect of detective to someLocation. -- Incorrect
-
set type expressions to a Set type attribute if all members in the expression are instances of a subclass of the member class of the target attribute. Here are some examples, given the natural types of the instances:
Has friends {monica, ross, chandler, rachel, phoebe, joey}. -- 'person's Set friends of mine to {book}. -- Incorrect, probably not a 'person' Set friends of mine to {}. -- Correct, an empty set is always OK Set friends of mine to {suspect Of detective}. -- Maybe correct
INCLUDE Statement
The Include
statement is used to include a new member in a Set.
include_statement = 'INCLUDE' expression 'IN' set '.'
Typically, this is used to add an instance or value to a collection of similarly typed members. See Sect. 9.4 for an explanation of the Set type. Attempts to include a member already present in the Set will be silently ignored without generating duplicate entries.
The Set may be identified using an expression involving reference attributes:
Include hitchhiker in friends of driver of car.
And vice versa:
Include driver of car in friends of hitchhiker.
EXCLUDE Statement
The Exclude
statement is the reverse of the Include
statement.
It removes a member from a Set.
exclude_statement = 'EXCLUDE' expression 'FROM' set '.'
Attempts to remove something not included in the Set will be silently ignored; therefore, it’s guaranteed that after executing this statement the instance or value of its expression will not be in the Set.
The inclusion or exclusion of an instance will not affect its location. A member may be included in multiple Sets. |
23.6. Conditional Statements
In Alan there are two conditional statements: the common If
statement and the Depending On
statement.
IF Statement
The If
statement is essential for varying output and otherwise change the activities in the game.
if_statement = 'IF' expression 'THEN' statements
{ elsif_part }
[ else_part ]
'END' 'IF' '.'
elsif_part = 'ELSIF' expression 'THEN' statements
else_part = 'ELSE' expression 'THEN' statements
The expression is evaluated (see Chapter 26 for details and examples of expression) and if it evaluates to true, the statements following the Then
keyword are executed.
Otherwise, the expressions in any following ElsIf
clauses are evaluated (in order) and the statements following the first expression that results in a true value is executed.
If none of the expressions in the ElsIf
clauses evaluated to true, or if there are no ElsIf
clauses, the statements following the Else
keyword are executed.
The Else
clause is optional.
If minute of clock = 59 then
Set minute of clock to 0.
Increase hour of clock.
Else
Increase minute of clock.
End if.
If level of bottle = 0 then
"You have no water."
ElsIf level of bottle < 5 then
"You have almost no water left."
Else
"You have plenty of water."
End if.
If
statements which have an IsA
expression (see Sect. 26.6) are particularly important.
As an IsA
expression test for the class of an instance, an If
statement like:
If i IsA actor then ...
will quarantee that for any statement inside the Then
part of that statement, the i
will be of the class actor
.
This means that references to attributes, container properties, actor scripts, etc. as if i
belongs to the class actor
, even if that was not known outside of the If
statement.
A typical example where this is helpful is inside verbs where parameters can be restricted to more general classes by the Syntax
and the Verb
body can still perform specific actions only allowed on more specialized classes.
Another example would be looping over a set of unknown instances:
For each e IsA entity, here do
If e IsA actor then ...
If e IsA object then ...
If e IsA container_object then ...
End for.
DEPENDING ON Statement
The Depending On
statement enables us to select one of a number of possible conditional cases depending on an expression.
depend_statement = 'DEPENDING' 'ON' expression
{case}
'END' 'DEPEND' '.'
case = right_hand_side 'THEN' statements
| 'ELSE' statements
The expression part can be any expression. The right_hand_side part is the right hand side of any valid expression. When combined with expression (as the left hand side of the expression) they will form a complete expression, which is evaluated.
A simple example of the Depending On
statement:
Depending on weight of obj
= 1 then "light as a feather"
Between 2 and 10 then "carryable"
Between 10 and 20 then "heavy"
> 20 then "immobile"
Else "weightless"
End depend.
The purpose of this example is to test the weight Of obj
and select one of the cases depending on its value.
If it’s equal to one, then the first case will be executed.
If none of the cases match, the optional Else
case will be executed (in this case it will only be executed for weights of zero or less).
The cases are tested in the specified order. At most, one case will be executed. In the example, a weight of ten will render as "carryable".
The above tests are thus equivalent to:
If weight of object = 1 then "light as a feather"
ElsIf weight of object between 2 and 10 then "carryable"
ElsIf weight of object between 10 and 20 then "heavy"
ElsIf weight of object > 20 then "immobile"
Else "weightless"
End if.
A Depending On
statement is preferable to a chain of If
statements when the same expression will be tested for multiple matches.
23.7. Actor Statements
Actor statements are statements that are used to control actors.
USE Statement
The Use
statement starts execution of a given Script
for a given actor.
use_statement = 'USE' script ['FOR' actor] '.'
The For actor
clause is optional when writing code within a certain actor — in this case, the statement applies to the actor (instance or class) containing the code.
Use script playing for george.
For the actor clause you can use an expression such as a simple identifier, a parameter reference or a reference attribute. |
STOP Statement
The Stop
statement stops an actor from proceeding with any script it may be executing.
In effect, it will abort it and put the actor in an idle state.
stop_statement = 'STOP' actor '.'
actor = expression
The most common case is the direct reference to an actor using its identifier. More complex expressions resulting in an actor type value, such as a parameter reference or a reference attribute, can be used as the actor clause.
23.8. Repetition Statements
The Alan language provides one compound statement for repetition, the For Each
statement.
repetition_statement = 'FOR' 'EACH' id [filters] 'DO'
statements
'END' 'FOR' 'EACH' '.'
filters = filter { ',' filter }
| 'BETWEEN' expression 'AND' expression
You can optionally leave out either For
or Each
, but not both.
The identifier is called the loop variable, and its semantics are similar to a Syntax
parameter.
It will be dynamically bound to instances, one for each repetition.
Inside the loop body (i.e. the statements) this variable can be referenced in the same way as a Syntax
parameter.
The optional filters can be used to restrict the values in the loop.
If the Between
form is used, the loop becomes an integer loop, resulting in the loop variable being of the integer type, and a loop range encompassing the values of the two expressions (included).
Otherwise, the loop variable will be of instance type and will consecutively assume the value of each instance fulfilling the filters.
See Chapter 27 for an explanation of filters.
Within the repetition, any references to the loop variable will refer to the bound instance, or integer value, in this repetition.
You can use any statements inside the repetition, e.g. to check for further conditions before operating on the instance. For example:
For each creature IsA actor do
If creature here then
...
End if.
End for.
23.9. Special Statements
QUIT Statement
Quit
prints a question giving the player the choice to restart the game, restore a previously saved game or to quit the game.
Any scoring or other printouts have to be made explicitly before executing the Quit
statement.
LOOK Statement
Look
describes to the player the current location and what it contains (which location is going to be the current location depends on which context Look
is being executed in).
First, the location name is printed out, then the Description
part for the location is executed.
If you do not want the name of the location to be included, you can use a Describe
statement instead.
Then, all object and actors at the location will be automatically executed by means of an implicit Describe
for each of them.
Any objects or actors that were explicitly described using a Describe
statements, will be excluded from this automatic Describe
.
The equivalent of a Look
is automatically performed when the hero enters a new location.
As the player will only see output generated at the same location as the hero, a
|
SAVE and RESTORE Statements
Save
saves the game on a file for later use with Restore
.
Both save
and restore
asks the player for a file name to use for storing and restoring.
This allows the player to use unlimited number of save files.
If the player should be shown the current surroundings after a Restore
, you will have to implement a player verb like:
Verb 'restore'
Does
Restore.
Look.
End verb 'restore'.
In the above example, the verb identifier |
SCORE Statement
Score
is a way of rewarding the player by giving points for certain actions.
This is done using the statement:
score_statement = 'SCORE' integer '.'
For example:
Score 25.
The first time every such statement is executed the points given are added to the player’s current score. Score
without any arguments prints a message indicating the current accumulated score.
The |
VISITS Statement
The Visits
statement changes the number of times a location can be visited before its long description is presented again:
visits_statement = 'VISITS' count '.'
The value of the argument (count, which must be an integer number) controls the number of visits to a particular location between full descriptions. The initial setting of 0 (zero) indicates that every time a particular location is visited its full description will be shown (which can also be expressed as: the full description will not be shown 0 times in between). Thus, a setting of 1 (one) would give a full description every second time the same location is visited. Therefore:
Visits 0.
will always show full descriptions (which is also the initial setting).
The classic and familiar commands verbose, brief, etc. can be imitated using different values in the |
When the description of a location already visited by the player is being omitted as a result of Visits
, the AGAIN
run-time message will be shown after the location name.
The handling of descriptions is rather conservative in that it also takes modified attributes into account.
If the |
Associated with this is a predefined attribute, visits
, that exists for all instances inheriting from location
.
It will count how many times the player have visited the location.
For example:
The secret_cave IsA location
Description
"This is the secret cave. You have visited it"
Say visits of this.
"times before."
End the secret_cave.
TRANSCRIPT Statement
Using the Transcript
statement you can turn transcripting on or off.
transcript_statement = 'TRANSCRIPT' ('ON' | 'OFF') '.'
When transcripting is turned on all player input and game output is recorded in a file (or similar) which can be studied afterwards. Example uses are:
-
reading the game output as a novel (player)
-
comparing the output to output from previous versions of the game (author)
-
comparing output to the output of a v2 game (porting from v2)
To enable player access to the transcripting function you need to implement global verbs:
Syntax script_on = 'script' 'on'.
Verb script_on
Does
Transcript on.
"Transcript turned on."
End verb.
Syntax script_off = 'script' 'off'.
Verb script_off
Does
Transcript off.
"Transcript turned off."
End verb.
24. WHERE Specifications
Many constructs in the Alan language require a specification of where the construct should operate. The general intention of such a WHERE specification is to specify a location.
where = 'HERE'
| 'NEARBY'
| 'NEAR' what
| 'AT' what
| 'IN' what
The meaning of the different constructs is as follows:
-
Here
is the location where the current activity is performed. Often this means where the hero is, but if the expression is evaluated in another run-time context then that context is the one used. See Run-Time Contexts for a detailed discussion, but examples include an event scheduled at a particular location, in which case that location isHere
. Note thatHere
is equivalent toAt Current Location
. -
Nearby
means at any adjacent location. An adjacent location means that there is anExit
from the other location toHere
(note that the direction is fromNearby
toHere
). It is allowed to refer to any instance using an identifier or expression. In particular, instances inheriting fromlocation
are allowed, which can be used to see if someone at that location can use an exit to get here. -
Near what
has a similar meaning toNearby
except that it refers to some other instance (the WHAT) instead of the current location. The results is a truth value indicating wether that other instance is at a location which is nearby (has anExit
to) the location of the first instance. -
At what
means at the location of the instance referenced by the WHAT specification (see Chapter 25). Note that an instance is alwaysAt
itself, i.e.x At x
is always true. This can come as a surprise, especially if you try to aggregate or loop over instances. (See Aggregates and Repetition Statements.) -
In what
must refer to a container and the expression refers to being inside that container.
These forms can be used in Locate
statements and in some expressions, for example.
When used in their basic form in expressions they all look inside containers (and in containers inside other containers too) to evaluate the expression.
See The Whereabouts of an Entity for more information about WHERE expressions.
Not all kinds of WHERE specifications are meaningful in every constructs.
For example, |
25. WHAT Specifications
Constructs in the grammar for the Alan language often refer to some class or instance defined in the Alan source.
This is generally called a WHAT specification, as it specifies what the construct refers to.
An example is the Locate
statement that must refer to something that should be relocated.
what = 'CURRENT' 'ACTOR'
| 'CURRENT' 'LOCATION'
| 'THIS'
| id
| attribute_reference
The meaning of the different forms of the WHAT specification are:
-
Current Actor
is always set to the actor currently active, e.g. when a non-player actor is running a script this refers to the actor instance that is running. -
Current Location
is the current location, i.e. the location where the current activity is performed. Normally this is the location where the hero is, but may also be where an event is executed or the location where a scripted actor is currently executing. See Run-Time Contexts for more details. -
This
refers to the instance in which the current code (e.g. a verb body or a script) is run. This can be used, for example, to test or set attributes in inherited code, thus testing or setting attributes in an instance while the code is defined in the class from which the instance inherits. It cannot be used in events or global verbs. -
An identifier, id, refers to the class or instance with that name, a syntax parameter, script or loop variable with that name. A syntax parameter may have the same name as an class or instance declared elsewhere in the source, in which case the parameter has precedence.
-
A reference to an attribute, as described in Sect. 26.3, might be used depending on its type and the context of the usage of the WHAT expression.
Not all kinds of WHAT specifications are meaningful in every contexts.
For example, it is not possible to use |
26. Expressions
The Alan grammar often refers to expression. This is a generic name for a number of constructs yielding a value. The following sections describe the different kinds of expressions available in the Alan language.
26.1. Types of Expressions
Expressions are used e.g. in If
and Set
statements.
The If
statement requires a Boolean expression, i.e. an expression yielding a true or false value, while the Set
statement can handle all other types of values.
See Chapter 9 for details on types.
26.2. Literal Values
A single integer (e.g. 42) is a numeric expression. A string is an expression and represents a string value, e.g.:
Set password of terminal to "xyzzy".
A value of the Set type can be constructed directly as an expression.
This can be used in a Set
statement or another expression.
E.g.:
Set suspectedWeapons of detective to {gun, bat, axe}.
Each member in the Set
expression can be an expression of integer or reference type in itself.
26.3. Attribute References
A reference to an attribute can be used as part of any expression provided its type matches the semantics of the context. The type of the expression is the type of the attribute.
attribute_reference = id 'OF' expression
| expression ':' id
There are two formats available; the first one resembles plain English:
Set password of terminal to password of manual.
The second format is more compact, which might be preferable when referring to chains of attributes referring to other attributes. See Reference Attributes for an explanation on how this works.
Say detective:suspect:weapon.
It might help to read the :
as a replacement for ’s.
In this example the detective must be known to have a reference attribute, suspect
, which can only refer to instances of a class that have an attribute named weapon
.
It would be the same as:
Say weapon of suspect of detective.
You can test Boolean attributes of an instance by following the pattern:
expression = expression 'IS' something
For example:
If bottle is empty then ...
The test can be reversed by adding a Not
:
If hero is not hungry then ...
LOCATION OF
There is a particularly useful predefined pseudo-attribute, location
, that can be used to query an instance of which location it is currently at.
Make location of magic_lantern lit.
This attribute is predefined on all instances and is guaranteed to return an instance of the class location
, and it will be the innermost location of the instance (bearing in mind that locations may be nested).
26.4. RANDOM Values
There are three types of random expressions. The first is the traditional random integer expression.
expression = 'RANDOM' expression 'TO' expression
The random integer expression returns a numeric value that is randomly selected between and including the values of the two expressions. Arbitrary expressions yielding an integer value can be used as the boundary expressions.
Set eyes of first_die to random 1 to 6.
Decrease temp of room by random 0 to temp of room.
The second and third types return a random member in a Set or in a container respectively.
expression = 'RANDOM' ['DIRECTLY'] 'IN' expression
If the expression refers to a container, the expression returns one of the instances currently in that container. The type of the entire expression is instances of the class accepted by the container. See Sect. 13.9 for details on how to determine the class of instances allowed inside a container.
If the expression refers to a Set, the result is one of the members in the Set. The type and class of the entire expression is determined by the allowed members in the Set. See Sect. 13.5.5.
The optional keyword Directly
is only allowed if the expression refers to a container.
The semantics are the same as for the WHERE expression, see Sect. 26.12.
Attempting to apply a random selection from an empty Set or container is one of the very few situations that could lead to a runtime error.
It is the author’s responsibility to ensure that this is not attempted.
As a safeguard against similar runtime errors, you should always surround a random member expression with an |
A |
26.5. Logical Expressions
The And
and Or
operators are standard binary Boolean operators, meaning that the result of an expression is true or false depending on the right and left expressions, which must also be boolean values or expressions.
expression = expression ('AND' | 'OR') expression
For And
both expressions need to be true for the expression to be true.
If using Or
either of them need to true.
Otherwise the expression will be evaluated to false.
And
has higher priority, but parenthesis may be used to change the order of evaluation.
If kalif here and mood of sultan is 0 then ...
If o IsA treasure and (size of o > capacity of c
Or thief is greedy) then ...
26.6. Class Expressions
It is possible to check if an instance belongs to, or inherits from, a particular class. The resulting value is a Boolean type value.
expression = something 'ISA' class_id
Example:
If p IsA object then ...
If opponent IsA enemy then ...
There is a subtle but very important side effect when checking for an instance class in an If
statement like the above.
Doing so will ensure that the instance or parameter being checked has all the properties.
This holds true for all statements in the Then
part of the If
statement.
This is used by the compiler to allow references to attributes, scripts, container properties, etc. that otherwise would not be allowed.
A very common use of this is to restrict parameters in a Syntax
to a more general class, like thing
or entity
, and then doing “manual” restrictions using If
statements to ensure that their usage does not conflict with the actual properties of the instance.
See also the section on IF Statement.
26.7. Binary Operators
All binary operators (plus, minus, multiplication, division) may be used on integer expressions. The result is another integer expression. The exact set of available operators is:
+, -, *, /
For example:
age of golden_child + 4
The plus operator (+
) may also be used on strings for concatenation.
The meaning of such an expression is that the two strings are concatenated into a resulting string.
For example:
string1 + " " + anotherString
26.8. Relational and Equality Operators
Equality (=
, meaning ‘equals’) and relational operators (<
, >
, <=
, >=
, meaning: ‘less than’, ‘greater than’, ‘less than or equal’, ‘greater than or equal’, respectively) are used to compare expressions.
The result is true or false and may be negated by using an optional Not
.
If temperature of oven not > 100 then ...
If weather of world not < protection of hero then ...
Comparing two string expressions using the binary equality operator =
will make a case insensitive comparison, i.e. it will give a true value if the strings are the same without considering the case of the characters.
The special identity operator, ==
, only works on strings and compares the strings for an exact match (i.e. considering character case).
Two values of instance type may be compared with the =
and <>
operators, and may e.g. be used to test if a parameter refers to a particular instance or is the same as another parameter.
For example:
Syntax put_in = 'put' (o) 'in' (c)
Where c IsA container
Else "You can't put anything in the" Say c.
Verb put_in
Check o <> c
Else "That would be a good trick if you could do it!!"
Does
...
Relational operations are not allowed on entities or strings, nor is it possible to compare values of different types.
A special relational operator is the Between
operator which makes it possible to test if a numeric expression is within a range of values.
The range is inclusive, i.e. the values are included in the accepted range.
For example:
If level of water between 2 and capacity of bottle then ...
26.9. String Containment
There is a string containment operator, Contains
, which can be used to test if a string contains another string.
The test ignores any differences in character case.
An example of an expression that is true is:
"A string" contains "a S"
An optional Not
(before Contains
) can be used to reverse the test.
"A string" not contains "a S"
The expression yields a Boolean value.
26.10. CURRENT Entities
There are two particularly interesting entities that you might want to learn about. They are:
-
Current Actor
-
Current Location
These two expressions can be used wherever a reference to an instance can be used. They will refer to the currently executing actor and the current location respectively. Details about execution contexts can be found in Run-Time Contexts.
26.11. THIS Instance
You can also refer to the instance that is actually executing the code containing the expression.
This is particularly useful when using inheritance since the class defining the code has no way of knowing which instance will actually execute it.
This expression is This
.
An example is the code for objects that can be opened:
Every openable IsA object
Is not open.
Verb open
Check ...
Does
Make this open.
End verb.
End every openable.
The door IsA openable
End the door.
> open the door
Given these two declarations, and some Syntax
declarations, the door
will inherit the open
attribute.
When the verb body, also inherited from openable
, is executed, it will set the attribute on the door
, because this is the instance running the code.
26.12. The Whereabouts of an Entity
An expression following the pattern below can be used to test if a particular instance, as specified by the WHAT, is (or is Not
), at the place indicated by the WHERE.
expression = what ['NOT'] [transitivity] where
Example:
If bottle in inventory then ...
or
If hero not nearby then ...
The forms available for the WHERE expression are described in detail in Chapter 24.
The default behaviour of a WHERE expression is to evaluate recursively through containers, e.g. if the bottle was inside a bag which was in the inventory, the first expression above would still be true.
This implicitly transitive evaluation can also be made explicit through the use of the keyword Indirectly
.
This would result in exactly the same semantics, but explicitly expressed, which can be useful.
transitivity =
| 'DIRECTLY'
| 'INDIRECTLY'
| 'TRANSITIVELY'
In addition, another qualifying keyword, Directly
, can be used to indicate that the expression should not evaluate recursively into containers.
To test if an instance is at a particular location but not in a container at that location you can use:
If key directly at treasury then ...
The qualifying keyword Directly
works in the same way with all WHERE expressions.
Adding a Directly
qualifier to the first example above would change the expression to only be true if the bottle was in the container but not inside any other container, even if that container was in the inventory.
See Containment, Classes and Transitivity for some background information.
If the transitivity is not |
26.13. Aggregates
Aggregates are functions to calculate values from Sets of instances.
aggregate_expression = aggregate filters
aggregate = 'COUNT' | 'SUM' | 'MAX' | 'MIN'
There are four aggregates available: Count
, Sum
, Min
and Max
.
Aggregates work by inspecting all instances available, applying the filters, which may remove some, or even all, from the Set of instances, and then calculate the value from the remaining instances.
You can use filters to filter out instances belonging to a particular class, at a particular location or having a particular Boolean attribute. See Chapter 27 for an explanation of filters.
Count
counts the number of instances in the Set, e.g.:
"You are carrying"
Say count IsA object, in inventory, is big.
"big objects."
In this example there are three filters applied, IsA object
, In inventory
and Is big
.
All of these filters must pass before an instance is counted.
The result of that count is an integer, which is then printed using the Say
statement.
The Sum
, Min
and Max
aggregates return the sum, minimum and maximum value respectively, of an attribute of all instances in the filtered set.
Any attribute referred to either in the aggregation itself or in the filters, must be an attribute of some class in order to ensure that the attribute is available for all instances. You must ensure this by filtering out only instances of the relevant class, e.g. objects, using a class filter.
Some examples:
If sum of weight at bridge > 500 then ...
If max of size in inventory > size of small_door then ...
If count IsA lightsource, is lit, here > 0 then
"Let there be light..."
End if.
These examples could be used to create various restrictions in the possible travels of the hero.
27. Filters
Filters can be used to filter out only particular instances to loop or aggregate over.
filters = filter { ',' filter }
filter = 'ISA' class
| is attribute
| where
If one of the filters is a IsA class
, only instances of that class will be bound to the loop variable or considered in the aggregation.
In particular, this is required if any of the other filters refer to attributes, which is only allowed if the class is known and that class is guaranteed to have that attribute.
Other ways to restrict the filtered instances is to use a WHERE filter which implicitly restricts to instances available at or in that location, container or Set.
See Sect. 26.12 for details on the various forms of the WHERE expression.
Multiple filters can be listed by using a comma to separate them.
Each filter must enumerate the Set of values to a compatible Set, e.g. using two IsA
filters for actors and locations respectively is not allowed since those two Sets can never be compatible.
Part III: Lexical Definitions
28. Comments
Comments may be placed anywhere in the Alan source. There are two types of comments, a line comment and a block comment.
A line comment starts with double hyphens (--
) and extends to the end of the line.
-- This is a comment
A block comment can be used to comment out longer sections of Alan code. The first (ignored) line of a block comment is a line where the first four characters are forward slashes, it may then be followed by anything. The block comment ends with the first line that has four slashes in the first columns. It may be follwed by more slashes, but nothing else.
//// This line starts a block comment ////
These lines will be
completely ignored.
//// So will this line ////
The block comment will end after the following line
////////////////////////////////
29. Words, Identifiers and Names
An identifier is a word in the Alan source, which is used as a reference to a construct, such as an instance. Identifiers may only be composed of letters, digits and underscores. The first character must be a letter.
identifier = letter {letter | digit | underscore}
29.1. Quoted Identifiers
There is also a second kind of identifier, namely the quoted identifier.
id = identifier
| quoted_identifier
quoted_identifier = quote {any_character} quote
A quoted identifier starts and ends with single quotes and may contain any character (including spaces) except single quotes — if you need to include a single quote inside a quoted identifier, you must escape it by doubling it, e.g.:
The 'Bob''s House' IsA location. -- ID will be printed as "Bob's House"
End the.
When the Alan compiler encounters two consecutive single quotes inside a quoted identifier, it will treat them as if they were one single quote which is part of the identifier, and not as a the single quote delimiter indicating the end of the identifier.
In the above example, the identifier will be printed as “Bob’s House” in the actual game — the enclosing single quote delimiters being ignored in print, and the two consecutive single quotes printed as one single quote.
Any sequence of characters enclosed within single quotes can become an identifier, except inside strings (where single quotes are treated as ordinary printable characters).
A single-word identifier can be written within or without single quotes, indifferently (unless it’s a keyword).
Therefore, |
29.2. Keywords as Identifiers
Quoted identifiers may also be used to create an identifier out of a reserved word such as Look
, The
, etc.
This practice is known as stropping, and it allows the Alan compiler to distinguish between user-created identifiers and keywords in the source code, thus avoiding clashes that would lead to a compiler error.
This is useful (indeed, required), for example, in the definition of the verb ‘look’:
Verb 'look'
Does
Look.
End verb 'look'.
Whenever an identifier contains an Alan keyword, it’s mandatory to use a quoted identifier. |
In the following example, the 'The Empty Room'
name needs to be enclosed within single quotes because it contains two Alan keywords (The
and Empty
), but there’s no need to do so with the empty_room
identifier because the word empty
doesn’t appear in isolated form (i.e. surrounded by spaces):
The empty_room IsA location --> No need to stropp 'empty_room'.
Name 'The Empty Room'. --> Keywords 'THE' and 'EMPTY' stropped.
End the.
Stropping occurs only when the quoted identifier contains at least one Alan keyword.
Since in Alan it’s always permissible to enclose an identifier within single quotes (where |
29.3. Names Containing Multiple Words
Quoted identifiers retain their exact content. They may contain spaces and other special characters, which make them useful as long names for locations as in:
The pluto IsA location Name 'At the Rim of Pluto Crater'
Description
...
One single-quoted identifier is used as the whole name of the location to preserve editing and avoiding clashes with the reserved words At
and Of
.
(This could also have been avoided by quoting just those words.)
Identifiers and words retain their capitalization. An example is:
The eiffel_tower Name Eiffel tower ...
The first word in the name will always be printed with a capital ‘E’. However, when comparing the word to player input and other occurrences of the same word in the source, letter casing will be ignored (i.e. comparison is case insensitive). This means that you cannot have two words or identifiers that differ only in case, they will be the same and stored in the game data as one of the occurrences, which one is implementation dependent.
Do NOT use a single quoted identifier with spaces or special characters in them as the name for anything other than locations, as the words in names are analysed separately and are assumed to be adjectives and nouns (where it is assumed that the last is the noun). Except for this you should only quote single words to avoid clashes with reserved words. |
Any one of the occurrences of a word might define its capitalization, which one is unspecified.
This might affect the output if you use capitalization for names of locations, such as “ |
Be careful when using quoted identifiers, especially if the player is supposed to use the word. A player cannot refer in the typed input to words which are defined in the code as single words containing spaces or other special characters or separators (the only exception being underscores and dashes).
Also, remember that a player input word must start with a letter; so the player won’t be able to refer to identifiers or names like “1st
”, “'70s
”, etc.
Escaping Single Quotes Inside Quoted Identifiers
To include a single quote as part of a quoted identifier, double it (e.g. This technique is known as escaping, for it allows the Alan compiler to distinguish between the quote character as delimiters of the quoted identifier and its occurrence within the identifier as a printable character. |
Some of the identifiers in the source of an Alan game are used by default as player words.
This is for example the case with verb names (unless a Syntax
statement has been declared for the Verb
) and object names (unless a Name
clause has been used).
If these contain special characters, the player won’t be able to refer to them in the typed input.
30. Numbers
Numbers in Alan are only integers and thus may consist only of digits.
number = digit {digit}
31. Strings
The string is the main lexical component in an Alan source. This is how you describe the surroundings and events to the player. Strings, therefore, are easy to enter and consist simply of a pair of double quotes surrounding any number of characters. The text may include newline characters and thus may cover multiple lines in the source.
string = double_quote {any_character} double_quote
When processed by the Alan compiler, any multiple spaces, newlines and tabs will be compressed to one single space as the formatting to fit the screen is done automatically during execution of the game (except for embedded formatting information, as specified in Sect. 23.1). You may therefore write your strings any way you like; they will always be neatly formatted on the player’s screen. You can use special codes (see Sect. 23.1.1 for a list) to indicate (but not precisely control) the formatting.
As strings may contain any character, a missing double quote may lead to many seemingly strange error messages. If the compiler points to the first word after a double quote and indicates that it has deleted a lot if IDs (identifiers), this is probably due to a missing end quote in the previous string. |
To get a double quote within strings repeat it ( |
32. Filenames
It is possible to write one adventure using many source files, having different parts in different files, and thus giving an opportunity for some rudimentary kind of modularisation.
The method for this is the import
statement.
import = 'IMPORT' quoted_identifier '.'
The Import
statement requires a filename, which must be given as a quoted
identifier (see Chapter 29).
Part IV: Running an Adventure
33. A Turn of Events
The player controls the execution of an Alan adventure.
Each player input is taken care of and acted upon by the run-time system of the interpreter.
The execution of every Alan adventure begins from the Start
section: the player is placed at the location indicated in the Start
section, the location is described, and the player is prompted for a command.
The player input is analysed according to the explicit and implicit Syntax
rules and converted to an execution of verb checks and bodies.
Global Verb
Check
s and bodies are used for verbs taking no parameters, otherwise the verb bodies are found in the parameter instances or their classes.
In case the player typed a directional command the corresponding Exit
check and code is executed.
After the player’s command has been taken care of, all rules are evaluated and possibly executed.
Then each of the other actor
s executes one step in its Script
(if active) and for each actor the rules are evaluated again.
Finally, each Event
that is scheduled for this round is fired, and then rules are evaluated yet again.
Finally the player is prompted for another command.
So, to summarise:
-
get and execute a player command
-
evaluate all rules
-
for each actor
-
execute one step (if active)
-
evaluate all rules as above
-
-
end for
-
for each pending event
-
execute it
-
evaluate all rules
-
-
end for
A player command may be either a verb command or a directional command.
A verb command is executed by checking the syntax of the input, performing any preconditions (Check
s) and then executing the verb bodies (as described in Verbs and Scope).
A directional command is executed by finding any Exit
in that direction, evaluating the Check
s and the body (if any) of that exit and locating the hero at the new location.
If the player enters an empty command, this is equivalent to forfeiting the turn. The empty command will result in no action from the hero, but counted as a player turn. Pending events will run and other actors will move as usual.
34. Player Input
The Syntax
defined in the Alan source is the basis for what verb commands the player is allowed to input.
If the syntax statement allowed referencing an instance (such as an object), then the syntax declares the basic form of that statement.
In addition, other combinations and variations are possible using special characters and words.
Obviously these words are different in each language, but here we’ll use generic English words, like “AND-word”, to denote all the words that can be used in the same manner. The exact list of these words, for every language natively supported in Alan, is available in Appendix E. |
The following built-in syntax variations are available to the player:
-
Concatenation of commands via AND-words, like:
> open the door THEN enter
> take the book AND read it
> west. north. east -
The use of pronouns to refer to the last object mentioned in the previous command, e.g.:
> take the book and read IT
> give key to guard and ask HIM to open the door with ITThe pronouns have to be defined by the author in the source (see Sect. 13.4) or by a library. The only built-in pronoun is the IT-word, which is automatically defined for the class
thing
. -
References to multiple objects using AND-word. This allows:
> take the blue vase AND the pillow
> examine the red key, the glass bowl AND the compass -
Reference to multiple objects using ALL-word:
> drop ALL
-
Excluding objects using a BUT-word, like:
> wear everything EXCEPT the bowler hat
-
The use of a THEM–word to refer to the multiple objects referenced in the previous command, e.g.:
> remove the hat and the scarf then drop THEM
Referencing multiple instances (objects or actors) in a given command position is only possible if the adventure author has allowed it by using a
multiple *
indicator in the Syntax
definition (see Chapter 15).
All the above variations are built-into the interpreter and automatically handled by the run-time system.
The interpreter also restricts parameter references to things which are reachable according to the semantic rules of each built-in base class (see Chapter 12 for the complete details).
For example, objects can only be referred to if they are present at the current location, except if the Syntax
for the command uses the
omnipotent !
indicator (see Chapter 15 for details).
For some hints on ways to allow the player to refer to objects and actors that are not at the current location, refer to Distant & Imaginary Objects.
If the player uses ALL instead of a reference to an instance in his command, the verb will be applied to all appropriate instances at the current location, except the ones that do not pass all checks for the verb (see Sect. 13.10 for further details on this).
A restriction placed on the player input by the interpreter is that the words the player is allowed to use can only contain alphanumeric characters, underscores and dash.
This must be kept in mind when naming verbs that use the default syntax (an explicit Syntax
statement can always specify other player words to trigger the verb).
35. Player Words
You use Syntax
statements to define the structure of available player commands.
The actual player input consists of “tokens” which are matched to those structures.
The most prominent part of that are the words.
The allowed words comes from
-
the syntax statement — the verb word itself and any “filler” words, in Alan generalized to “prepositions”
-
the special words mentioned above, to allow variations in reference forms
-
synonyms — directly from the
Synonyms
statements -
instance names — adjectives and nouns
All those words are collected by the Alan compiler into a dictionary of known words.
Words in the player’s input are separated into tokens by space or by characters not allowed inside words (see above).
Other characters are considered separate tokens. Some of those tokens are used in some forms of player input, such as comma and period, as seen above. Unrecognized characters will just result in an error.
35.1. Contractions
One special case is the apostrophe (“ ’ ”). It is allowed inside words. But in many languages this is also used for contractions, or elisions, such as (English) “can’t”, or (Italian) “l’acqua” for the contraction of the definite article and the noun, leaving out some other characters and sounds.
In order to support the contraction of multiple words using the apostrophe, Alan does some special handling of word tokens containing an apostrophe. The complete word will first be looked up, but if that is not defined the separate parts will be looked up. E.g.
> prendi l’acqua
In this example the word “l’acqua” will first be tried as a complete word, and if found in the dictionary, the input will be interpreted as using that word (perhaps a noun). If it isn’t found, the command parser will split at the apostrophe, first trying “ l’ ” (the contracted definite article) as a separate word. Then the second part will be tried, in this case “acqua”.
This makes it possible to use natural words as nouns and create “ l’ ” as a synonym for the definite article.
36. Run-Time Contexts
When the player enters a command, the Alan run-time system evaluates the various constructs from the adventure description (source) as described above. Depending on the player’s command evaluation, different parts of the adventure may be triggered. These parts all have different conditions under which they are evaluated and have different contexts. Five different execution contexts can be identified:
-
Execution of verbs — During the execution of a verb (the syntax and verb checks and the verb bodies), which is the result of the player entering a command that was not a directional command, parameters are defined and may be referenced in statements and expressions. In addition, the
Current Actor
is set to the hero andCurrent Location
to the location where the hero is (Here
refers to the location of the hero). -
Execution of descriptions — These are triggered as responses to a directional command, a
Look
orDescribe
statement, or aLocate
statement operating on the hero. During this execution context, no parameters are defined,Current Actor
is set to the hero, andCurrent Location
of course to the location being described. The description clauses for objects and locations, as well as theEntered
clause of locations, are evaluated in this context.Entered
clauses are executed for all actors entering a location withCurrent Actor
set to the moving actor. -
Execution of actors — When an actor performs his script step there are no parameters defined but
Current Actor
is set to the actor currently executing.Current Location
is set to that of the executing actor (Here
refers to where the executing actor is). -
Execution of events — No parameters and no actor are defined. The location is set to where the event was scheduled to execute.
-
Execution of rules — Rules are executed without location, so neither parameters,
Current Location
norCurrent Actor
are defined. Any output statement in this context will be completely useless since the hero can never be at the same location of an executing rule.
So, the execution of various parts of the adventure source can also be said to have a number of different focuses, meaning where the action is considered to take place:
-
The hero — the actions of the player are always focused on the hero, and their execution is always relative to the hero’s location.
-
An actor — steps executed by an actor are always focused where the actor is.
-
An event — code executed in events is focused where the event was specified to take place.
-
A rule — rules are executed after each actor (including the hero) and after each event, with the focus set to the complete game world.
37. Moving Actors
The main way to move the hero around the adventure’s world is through Exit
s (see Sect. 13.12).
They are executed if the player inputs a directional command, i.e. a word defined as the name for an Exit
in any location.
First, the current location is investigated for an Exit
in the indicated direction, if there is none an error message is printed.
Otherwise, that exit is examined for Check
s, which are run according to normal rules (see Sect. 16.3).
If there was no Check
, or if the checks passed, the statements in the body (the Does
-part) is executed.
The hero is then located at the location
indicated in the Exit
header, which will result in the description of the location (by executing the Description
-clause of the location) and any objects or actors present (by executing their Description
s, explicit or implicit).
When any actor (including the hero) gets located at a location
, the Entered
clause of that location is executed as if the actor had actually entered it by movement.
The actor being located will be the Current Actor
even if the movement was not caused by him (but was the result of an event, for example).
Therefore, this is also the last step in the sequence of events caused by locating the hero somewhere.
38. Undoing
A player might occasionally regret a typed command, perhaps realising that it was not the correct one.
The Alan interpreter supports such undoing of commands.
The interpreter stores each game state as soon as it has changed, and an undo
command resets the game state to the last saved one.
The command history is saved automatically, and as many states as memory permits are saved, providing almost unlimited undo
capability.
The player command to restore a previous game state is handled directly by the interpreter.
It must consist of the single word undo
.
39. Scripting and Commenting
Most versions of the Alan interpreter, Arun, supports both the creation of a transcript for the game in progress, as well as playing back a saved transcript as input passed to the interpreter.
These feature are very useful during the development of a game, allowing to play through the game up to a desired point and start from there, or even to automatically test your game.
To make Arun read input from a script file, you can use the special command character ‘@
’, which should be followed by the name of the text file in which your commands are listed.
You can add comments to each line in a script file.
The interpreter will not read beyond a semicolon (‘;
’), so anything after it can be seen as a comment.
Note that this also works for direct player input.
Part V: Hints and Tips
This chapter will give you some ideas about how the various features of Alan may be used to implement common features in an adventure game. These are only suggestions and you are, of course, welcome to invent your own, but these are probably some ideas that can get you started.
Using the Import
mechanism of the Alan language (see Chapter 10) you can reuse snippets that you invent in multiple games or works.
By building such a library you don’t have to reinvent the same thing every time.
A very easy way to get a lot of functionality, and learn about using the language, is to use the Alan Standard Library. You you can download it from the Alan website. The library implements many of the things described below, and loads of other handy things for you to use directly. For details on how to use that library, refer to its documentation.
The following examples, hints and tips do not use any library, only plain vanilla Alan code. |
40. Use of Attributes
Attributes are primarily used for holding status information about the instance to which they belong. This allows, for example, a water bottle to contain three levels of water:
The bottle IsA object
Has level 3.
Verb drink
Does
If level of bottle > 0 then
"You take sip from the bottle."
Decrease level of bottle.
else
"There is no more water in the bottle."
End if.
End verb drink.
End The bottle.
Another example is a breakable mirror:
The mirror IsA object
Is not broken.
Verb break
Does
Make mirror broken.
End verb break.
End the mirror.
The appropriate verbs defined in the instances may then modify the attributes and thus update the status information.
Attributes defined for a whole class of instances also allow an extra dimension of classification of the instances. If the following declaration is made:
Add to every object
Is not takeable.
then all objects receive the attribute takeable
and unless the attribute is specifically redeclared for a particular instance they will not be takeable.
Note however that the semantic meaning of ‘takeable’ (i.e. what actually happens, such as preventing the ‘taking’ action) must be implemented e.g. in the verb ‘take’:
Verb take
Check object is takeable
Else "You can't take the $o."
Does
Locate object in inventory.
End verb take.
Similarly, restrictions concerning what is possible to eat, drink, open, etc. may also be implemented. This use of attributes to classify instances is “action-oriented”, i.e. they imply that a particular action (verb) is applicable to the instance.
An alternative approach is to use attributes to classify instances according to their characteristics. Consider:
Verb take
Check o is not heavy
Else "That is too heavy."
And o is not animal
Else "$+1 moves quickly away, just far enough
for you not to reach it."
Does
Locate o in hero.
"You take" say the o. "."
End verb take.
With this approach you need to keep track of which properties a particular verb will accept or require. This could be extended one step further, having verbs check actual dimensions, such as weight or size, instead.
And while we are talking about classification, the Alan 3 class concept can help. Often a classification can be made, clearly and succinctly, by defining a sub-class, for which every property pertaining to that type of instances can be collected. Often, the need for an attribute disappears.
Further more, you don’t need to define a syntax for a single parameter verb if it only accepts instances from a particular class. Consider the following definitions:
Every vehicle IsA object
End every vehicle.
Every car IsA vehicle
Verb drive
Does "Yoooohooooo!"
End verb.
End every car.
Every bus IsA vehicle
End every bus.
The car1 IsA car at l
End the car1.
The bus1 IsA bus at l
End the bus1.
Without any Syntax
definition whatsoever, Alan will supply a default syntax for the drive
verb which restricts its use to car instances only:
> look
There is a car1 and a bus1 here.
> drive bus1
You can’t do that.
> drive car1
Yoooohooooo!
So the class mechanism not only allows for another way to classify your instances, but also makes it much easier to handle player input correctly.
41. Descriptions
Attributes come in handy when presenting information about instances to the player.
The attributes can be tested in If
statements to modify the Description
s and possibly even the short description in the Mentioned
sections.
For example:
The mirror IsA object
Is not broken.
Description
"On the wall there is a beautiful mirror
with an elaborate golden frame."
If mirror is broken then
"Some moron has broken the glass in it."
End if.
Verb break
Does
Make mirror broken.
End verb break.
End the mirror.
If you also use this feature with the short descriptions will make the adventure feel a bit more consistent.
The bottle IsA object
Has level 3.
Article ""
Mentioned
If level of bottle > 0 then
"a bottle of water"
Else
"an empty bottle"
End if.
End the bottle.
If the bottle had level
0 and was in the hero container, this would result in:
> inventory
You are carrying an empty bottle.
42. Common Verbs
As your adventures library grows, you will find that some verbs are needed often, and always function the same way.
Examples are ‘take’, ‘drop’, ‘inventory’, ‘look’, ‘quit’ and so on.
It is advisable to put them in a file which may then be imported into your games.
See Chapter 10 about the Import
mechanism.
The files may then contain these common verbs as well as their syntax definitions and any synonyms.
Attributes needed for these particular verbs could also be placed in a default attribute declaration in this file.
All your adventures may then import this file (or files), making these features immediately accessible when you start a new adventure. All it takes is some thought on what names to use for the attributes, as discussed in Chapter 40.
And of course there is already an extensive library available from the Alan website, www.alanif.se. It also includes a lot of other features common to most adventure games.
43. Distant Events
An effect of the feature that output is not visible unless the hero is present is that the description of an Event
might not always be presented to the player.
Event explosion
"A gigantic explosion fills the whole room with smoke
and dust. Your ears ring from the loud noise. After
a while cracks start to show in the ceiling,
widening fast, stones and debris falling in
increasing size and numbers until finally the
complete roof falls down from the heavy explosion."
Make location destroyed.
End event.
If the hero isn’t at the location where the event is executed, he will never know anything about what happened.
The solution is to create an Event
that goes off where the hero is.
Event distant_explosion
"Somewhere far away you can hear an explosion."
End Event.
...
If Hero nearby then
Schedule distant_explosion At Hero After 0.
...
44. Doors
A common feature in adventure games is the closed door. Here’s one way implement it:
The treasury_door IsA object at hallway
Name treasury door
Is not open.
Verb open
Does
Make treasury_door open.
Make hallway_door open.
End verb open.
End the treasury_door.
The hallway IsA location
Exit East to treasury
Check treasury_door is open
Else "The door to the treasury is closed."
End exit.
End the hallway.
The hallway_door IsA object at treasury
Name hallway door
Is not open.
Verb open
Does
Make treasury_door open.
Make hallway_door open.
End verb open.
End the hallway_door.
The treasury IsA location
Exit West to hallway
Check hallway_door is open
Else "The door to the hallway is closed."
End exit.
End the treasury.
Note that we need two doors, one at each location, but they are synchronised by always making them both opened or closed at the same time.
The check in the Exit
s makes sure that the hero cannot pass through a closed door.
45. Questions and Answers
Sometimes it may be necessary to ask the player for an answer to some question. One example is if you want to confirm an action. The following example delineates one simple way to do this, which could be adopted for various circumstances.
The hero IsA actor
Is not quitting.
End the hero.
Syntax
'quit' = 'quit'.
yes = yes.
Synonyms
y = yes.
q = 'quit'.
Verb 'quit'
Does "Do you really want to give up?
Type 'yes' to quit, or to carry on just
type your next command."
Make hero quitting.
Schedule unquit after 1.
End verb 'quit'.
Verb yes
Check hero is quitting
Else "That does not seem to answer any question."
Does quit.
End verb yes.
Event unquit
Make hero not quitting.
End event unquit.
46. Actors
Actors are vital components to make a story dynamic.
They move around and act according to their Script
s.
To make the player aware of the actions of other actors they need to be described.
This must be done so that the player always gets the correct perspective on the actors' actions.
A way to ensure this is to rely on the fact that output statements are not shown unless the hero is at the location where the output is taking place. This means that for every actor action, especially movement, you need to first describe the actions, then let the actor perform them and, finally, possibly describe the effects.
An example is the movement of an actor from one location to another. In this case the step could look something like:
"Charlie Chaplin goes down the stairs to the hallway."
Locate charlie_chaplin at hallway.
"Charlie Chaplin comes down the stairs and
leaves the house through the front door."
Locate charlie_chaplin at outside_house.
"Charlie Chaplin comes out from the nearest house."
An actor is described, for example, when a location is entered or as the result of a look command, in the same way as objects are.
This means that a good idea is to include the description of an actor’s activities in its Description
.
One way to do this would be to use attributes to keep track of the actor’s state and test these in the Description
clause.
The george IsA actor
Name George Formby
Is
Not cleaning_windows.
Not tuning.
Description
If george is cleaning_windows then
"George Formby is here cleaning windows."
ElsIf george is tuning then
"George Formby is tuning his ukelele."
Else
"George Formby is here."
End if.
...
Although quite feasible, this is a bit tedious.
Since the actor’s state is partlyt indicated by the script the actor is executing, this could be used to avoid the potentially large If
chain.
The optional descriptions tied to each script will be executed instead of the main description when the actor is following that script.
So this would allow us to simplify to:
The george IsA actor
Name George Formby
Description
"George Formby is here."
Script cleaning.
Description
"George Formby is here cleaning windows."
Step
...
Script tuning.
Description
"George Formby is tuning his ukelele."
Step
...
...
This makes it easier to keep track of what an actor is doing.
Another hint here is to describe the change in an actor’s activities at the same time as executing the Use
statement, like
Event start_cleaning
Use script cleaning for george.
"All of a sudden, George starts to clean the windows."
End event.
This makes the descriptions of changes to be shown when they take place, and the description of the actor is always consistent. You can, of course, still have attributes describing the actor’s state to customize the description of the actor on an even more detailed level, but it generally suffices to describe an actor in terms of what script he is executing.
47. Vehicles
The current version of Alan does not support actors being inside containers or inside other actors, which could be a straight forward way to implement vehicles. However, since the reader/player does not need to know how the output is generated, we can use a location and a chain of events to substitute for the vehicle.
Let’s start with the geography:
The garage IsA location
End the garage.
The parking_lot IsA location Name 'Large Parking Lot'
End the parking_lot.
Then we implement the actual car:
The car IsA object Name little red sporty ferrari Name car
At garage
Is not running.
Has position 0.
Verb enter
Does
Locate hero at inside_car.
End verb enter.
End the car.
We also need a description for the inside of the car. We will use another location for this:
The inside_car IsA location Name 'Inside the Ferrari'
Description
"This sporty little red vehicle
can really take you places..."
Exit out to inside_car
Check car is not running
Else "I think you should stop the car
before getting out..."
Does
Depending on position of car
= 0 then locate hero at garage.
= 1 then locate hero at parking_lot.
-- etc.
End depend.
End exit.
Verb drive
Check car is not running
Else "You are already driving it!"
Does
Make car running.
If car at garage
then schedule drive_to_parking after 0.
else schedule drive_to_garage after 0.
End If.
End verb drive.
Verb park
Check car is running
Else "You are not driving it!"
Does
"You slow to a stop and turn the engine off."
Make car not running.
Cancel drive_to_parking. Cancel drive_to_garage.
End verb park.
End the inside_car.
We must ensure that the player can say just “drive” and “park” by defining the Syntax
for those single word commands:
Syntax drive = drive.
Syntax park = park.
You can also see from the above code that there are (at least) two events that need to be defined too. They handle the movement of the car from one place to another:
Event drive_to_parking
"You drive out from your garage
and approach a large parking lot."
Set position of car to 1.
Locate car at parking_lot.
Schedule drive_to_garage after 1.
End event drive_to_parking.
Event drive_to_garage
"You drive out from the parking lot
and approach your own garage."
Set position of car to 0.
Locate car at garage.
Schedule drive_to_parking after 1.
End event drive_to_garage.
The main idea is that the player/reader is inside the car, and the events are executed at this location, thus emulating movement.
There are a multitude of different solutions to this problem.
One possibility is to exchange the car object for an actor and the events for script steps.
However, in this solution the car object is not where the hero is (inside_car
) so the output from the scripts for the car will not automatically be shown to the player.
There are (at least) two different ways to deal with this (one involving attributes, the other involving an extra object), but the solutions are left as an exercise to the reader!
As Alan allows nesting locations (locating a location at another as if it was an object or actor), yet another solution would be to actually move the car location between the garage and the parking lot.
Sincere thanks go to Walt (sandsquish@aol.com) for inspiring communication that brought this example to life.
48. Floating Objects
Floating objects is a term used for objects that are available everywhere, or at least at many places. Usually they are available wherever the hero is, and we want to avoid creating duplicate objects, so in a way we make them “float” along with the hero, or some other actor, instead.
48.1. Body Parts
One example of floating objects is the various parts of the hero’s body.
To create floating objects you can use a particular feature of entities, namely the fact that they are always located where the hero is. Such an entity can of course have the container property to allow it to contain a number of other instances.
So to have the hero’s body parts available wherever the hero goes you can use:
The body_parts IsA entity
Container
End the body_parts.
The right_arm IsA object Name right arm in body_parts ...
The head IsA object Name head in body_parts ...
Using entity containers is also a simple way to create other compartments on the hero, such as a belt:
The belt IsA entity
Container
Header
If count in hero > 0
then "and"
else "but"
End if.
"in your belt you have"
Else
""
End the belt.
You can combine that with the following definitions of the hero and the inventory verb:
The hero IsA actor
Container
Header "You are carrying"
Else "You are empty-handed"
If count in belt = 0 then "." End if.
End the hero.
Verb inventory
Does
List hero.
List belt.
End verb inventory.
And the following output could result:
> inventory
You are empty-handed but in your belt you have a knife.
The example uses the |
48.2. Outdoors and Indoors
Another example of floating objects are semi-abstract objects like air, ground and walls. Some of these add some extra complexity for they should be available only under certain conditions.
Of course, you would not want outdoor things to be available when you are indoors. To solve this, simply create yet another container object where we can store the outdoor things when they should not be accessible and place it where the hero can never be. Now we only need to make sure that the objects are transferred between the two storages:
The outdoor_things IsA entity
Container
End the outdoor_things.
The outdoor_things_storage IsA object at limbo
Container
End the outdoor_things_storage.
The air IsA object in outdoor_things_storage ...
The sky IsA object in outdoor_things_storage ...
When location of hero is outdoors =>
Empty outdoor_things_storage in outdoor_things.
When location of hero is not outdoors =>
Empty outdoor_things in outdoor_things_storage.
You need to add the boolean attribute outdoors
to every location to the make the rules work, of course.
And, voilà, every time the hero arrives at an outdoor location he will find the air and the sky.
And every time he enters a location that has the attribute outdoors
set to false he will not find them available.
Well, perhaps he would like to have the air available indoors too, but that is left as an exercise for the reader!
An alternative to the location attribute is to use classes.
Define an
|
48.3. Nested Locations as a Solution
Yet another option would be to make use of the nested locations feature.
Put all your outdoor locations in a outdoor location where the outdoor_storage
entity is also present (this is just a hint):
The outdoor_region IsA location
End the outdoor_region.
The park IsA location at outdoor_region
End the park.
Then the outdoor items can stay at this “region” location, no need for rules or extra containers.
49. Darkness and Light Sources
A very common puzzle in old time adventures (so much so that it has possibly been exploited beyond its potential) is the problem of dark locations and finding a source of light.
Darkness and light sources can be implemented in Alan in different ways. Again, we basically have the choice between attributes and classes. The solutions are both general and rather similar, so we will have a look at the solution using attributes and leave the other solution to the reader. (A good exercise to really understand the Alan class concept, so please take a stab at it. If you want to have a look at one solution, you can study the Alan Standard Library, which uses classes to implement light sources.)
First we need an attribute owned by all objects. We know we only need to consider objects because light sources need to be transported by the player, so they can not just be anywhere, like entities.
Add to every object
Is not lightsource.
End add to.
This ensures that all objects have the boolean (true/false valued) attribute lightsource
with the default not being a light source.
Any objects that provide light need to explicitly state that they are instead.
For some instances this attribute might change value dynamically, e.g. when the lamp is lit and extinguished.
Locations then must declare themselves as lit or not:
Add to every location
Is lit.
End add to.
Here we assume most locations are lit, dark locations need to declare themselves Not lit
.
We can now count the number of instances at the current location having the attribute lightsource
set, and if there are one or more there is some light provided.
So, the look verb could be reworked to:
Verb 'look'
Check current location is lit
or count IsA object, is lightsource, here > 0
Else
"You cannot see anything without any light."
Does
Look.
End verb.
The Check
of the 'look'
verb now checks the current locations need for light and then counts instances of object which are both light sources and present, to see if there is light.
Of course, we must also modify the dark locations so that they don’t display their descriptions upon entrance.
This is easy to do using another addition to every location, a description check, similar to the check in the 'look'
verb:
Add to every location
Description
Check current location is lit
or count IsA object, is lightsource, here > 0
Else
"You cannot see anything without any light."
End add to.
50. Distant and Imaginary Objects
Sometimes you need to make it possible for the player to refer to things either far away, that are not really objects or that may be at many places at once. Examples of these are a distant mountain that may be examined through a set of binoculars, the melody in “whistle the melody”, and water or walls. One way of handling this is to use entities, since they are “everywhere”. But sometimes you need better control over when they are available and when not.
50.1. A Mountain
For objects that need to be visible from a distance, the easiest method is to introduce a “shadow object”. This is a second object acting on behalf of, or representing, the distant object at the locations where it should be possible to refer to it. For example:
The hills IsA location
:
End the hills.
The mountain IsA object at hills
:
End the mountain.
The scenic_vista IsA location Name Scenic Vista
End the scenic_vista.
The shadow_mountain IsA object at scenic_vista
Name distant mountain
Description
"Far in the distance you can see the Pebbly
Mountain raising towards the sky."
End the shadow_mountain.
This would allow for example at scenic_vista
:
Scenic Vista.
Far in the distance you can see the Pebbly Mountain raising towards the sky.
> look at mountain through the binoculars
…
If the mountain must be visible and possible to manipulate from a number of locations, you might implement one shadow object for each location, but this might become a bit tedious if they are many. If they are identical you can use a simple rule like the following:
When hero at scenic_vista or hero at hill_road =>
Locate shadow_mountain at hero.
This will ensure that whenever the hero moves to any of the places from where the mountain is visible, the shadow_mountain
will surely follow.
However, as the rules are executed after the hero has already moved, a better strategy might be to make the shadow_mountain
‘silent’, i.e. to have no description.
Instead, its description should be embedded in the description of the adjacent locations.
Yet, another possibility would be to move the pseudo-object around using statements in the Exit
s, like:
The scenic_vista IsA location Name Scenic Vista
Exit East to path
Does
Locate shadow_mountain at path.
End exit East.
End the scenic_vista.
Regardless of which of these strategies you chose, you need to take care that the shadow object is not present when the real object is.
In this particular case, it should not be moved to the hills
.
50.2. The Melody
To allow the player to ‘whistle the melody’ for example, there are two different tactics that can be employed.
One choice is to make the melody an entity
(or some subclass thereof that you have defined), because, as we have seen, those can be manipulated from everywhere:
The melody IsA entity ...
Syntax whistle = whistle (m) ...
The other route would be to make it an actual object
.
In this case the Syntax
for the whistle
verb would need to indicate omnipotence — i.e. that the player can refer in the parameter to instances which are far away, including istances inheriting from object
. (See Sect. 15.1 for more details on the omnipotent indicator.)
The melody IsA object ...
Syntax whistle = whistle (m)! ...
The melody then does not have to be reachable, near or even be at any location at all, for the player to be able to refer to it.
In both cases you would most likely need to restrict the parameters for the syntax so that the player can’t ‘whistle the chair’. Which of the two strategies you would chose depends mainly on things like:
-
are there many things that this applies to (many ‘melodies’, perhaps)?
-
should the player be able to manipulate this instance in other ways?
-
do you need many different entities for various purposes?
52. Structure
A good thing to do when designing an interactive fiction story is to separate the geography from the story.
In Alan, you can use the Import
facility to structure your Alan source.
One approach could be to place the description of each location in a separate file along with any objects that could be considered part of the scenery and other related items.
These files can then all be included in a ‘map’ file, which in turn is included by the top-level file.
The story line can be divided into files too, one for each ‘scene’ — a scene being comments describing the important things that are suppose to happen, any prerequisites and objects, events, rules, etc. which are specific for this part of the story.
This strategy will both give you a better structure of your adventure as well as help you design a better story, much like the storyboarding technique used in making movies or plays.
53. Debugging
Occasionally your Alan code is flawed and you really can’t understand what is actually happening. To aid in discovering which part of your code is run when, the interpreter Arun incorporates some debugging features. There are a few debugging switches available when starting the interpreter from the command line:
-c Log the commands input by the player -l Log a complete transcript of the game -t<n> Enable trace mode (<n> = level 1,2,3 or 4) -d Enter the debugger when the game starts
The |
53.1. Command Logs and Game Transcripts
For various purposes, such as debugging, a log of the game play can be handy.
There are two such options available.
One is a command input log and it is created when the option -c
is given to the interpreter when starting a game.
Such a file is also sometimes called a "solution file" since it can be used to show the solution to a game.
A command input log can sometimes be used as input to the interpreter, and thus automate the execution of the exact player experience.
The other options is to create a complete transcript of the game play and is created when the option -l
is used.
Such a log file contains both player input and the game output.
Both kinds of log files are created in the directory which was current when the interpreter was started, the name of the log file will begin with the game name. They will have the extension .a3s, for "Alan v3 Solution", or .a3t, for "Alan v3 Transcript", respectively.
53.2. Interpreter and Instruction Trace
Trace mode can also be helpful when debugging. Level 1 of tracing will show each section, verb, exit, description etc., the interpreter is invoked on, making it easier to see which parts of the code are executed.
Trace level 2, instruction trace, will in addition also trace the execution of each operation in the generated code. Level 3 shows the execution of all steps, also those only pushing to or popping from stack. Level 4 dumps the content of the stack between each instruction. So higher numbers gives more information but is probably also less and less useful for normal debugging.
53.3. Debug Mode
Finally, and usually most useful, there is the debug mode. If the interpreter is started with this option, it will execute the start up sequence and then prompt for a debug command with:
adbg>
53.4. Using the Debugger
Abug may also be entered during the execution of an adventure. To do this you simply give the following player command (type it at the game prompt):
> debug
The game must have been compiled with the debug option or the command will be sent to the game which probably does not recognize it.
Typing a question mark or help in response to the debug prompt will give a brief listing of the commands available in Abug:
break [[file]:[n]] -- set breakpoint at source line [n] in [file] delete [[file]:[n]] -- delete breakpoint at source line [n] in [file] files -- list source files events -- show events classes -- show class hierarchy instances [n] -- show instance(s) objects [n] -- show instance(s) that are objects actors [n] -- show instance(s) that are actors locations [n] -- show instances that are locations trace ('source'|'section'|'instruction'|'push'|'stack') -- toggle various traces next -- execute to next source line go -- go another player turn exit -- exit debug mode and return to game, enter again using 'debug' as input x -- d:o quit -- quit game
Any command may be abbreviated as long as it is unambiguous. Typing b for break will work, for example. |
The display commands, actors, locations, objects and events, may optionally be followed by a number. Abug will then display detailed information about the entity requested, such as values of attributes, its present location, etc. Currently there is no way to modify anything using Abug.
You can run the adventure to the next source line by using the next command. If the source file is available, the interpreter will also show the source line.
Breakpoints can be set on a source line. Enter the break command followed by the number of the source line. Alan allows the source to be separated into multiple files, so the interpreter always indicate which file the source line is in, e.g. when hitting a breakpoint or stepping to the next source line. When setting a breakpoint, the current file is always assumed. You can currently set a breakpoint in another source file by preceding the line number with the file name delimited by a colon.
Breakpoints can be deleted. The delete command without a line number will remove any breakpoint at the current line. You can specify which breakpoint to delete by giving the line number (and optionally the file name).
The debugger knows on which source lines it is possible to place a breakpoint. If you attempt to put a breakpoint at some line where it is not possible, it will attempt to place one at a line which is numerically higher but as close a possible. This will sometimes cause a breakpoint to be placed in a context that will not be what you expected. |
The trace command and its options correspond to the types of traces described in the section on Command Logs and Game Transcripts above.
Wherever different output styles are available, e.g. in GLK based interpreters like WinArun, the Alan debugger tries to use them to distinguish the debugger output from the output of your game by using the pre-formatted style (see the section on styles in Sect. 23.1).
The following is a short excerpt from a command line debugging session (user input in italics):
<Arun - Adventure Language Interpreter version 3.0beta8 (2021-04-05 21:19:57)> <Version of 'saviour.a3c' is 3.0beta8!> <Hmm, this is a little-endian machine, fixing byte ordering....OK.> <'saviour' contains the following IFIDs: IFID: UUID://c065a752-a476-a252-731f-e9fe96fcdc6d// > adbg> n adbg: Stepping to saviour.alan:1346 <01346>: "$pWelcome to the game of SAVIOUR!$pIn this game your mission adbg> n Welcome to the game of SAVIOUR! <<Game output deleted for breivity>> adbg: Stepping to saviour.alan:1354 <01354>: Show 'logo.png'. adbg> n adbg: Stepping to saviour.alan:1355 <01355>: "$iVisit the Alan Home Pages at:" adbg> break 1357 Line 1357 not available, breakpoint instead set at saviour.alan:1358 <01358>: Visits 2. adbg> g Visit the Alan Home Pages at: http://www.alanif.se adbg: Breakpoint hit at saviour.alan:1358 <01358>: Visits 2. adbg> n Outside The Tall Building adbg: Stepping to saviour.alan:318 <00318>: "To the north is a tall ancient building with a large entrance. adbg> n To the north is a tall ancient building with a large entrance. On the top there is a clock tower. Most of the windows in the building are broken, and a sign with three oval objects are hanging lose from the wall. > north adbg: Stepping to saviour.alan:325 <00325>: Score 5. adbg> ? Alan 3.0beta8 -- Adventure Language System (2021-04-05 21:19) ADBG Commands (can be abbreviated): help -- this help ? -- d:o break [[file:]n] -- set breakpoint at source line [n] (optionally in [file]) delete [[file:]n] -- delete breakpoint at source line [n] (optionally in [file]) files -- list source files events -- list events classes -- list class hierarchy instances [n] -- list instance(s), all, wildcard, number or name objects [n] -- list instance(s) that are objects actors [n] -- list instance(s) that are actors locations [n] -- list instances that are locations trace ('source'|'section'|'instruction'|'push'|'stack') -- toggle various traces next -- continue game and stop at next source line go -- go another player turn exit -- exit to game, enter 'debug' to get back x -- d:o quit -- quit game adbg> trace section Section trace on. adbg> n <EXIT north[1] from Outside The Tall Building[4], Moving:> <ENTERED in class entity[1] is empty> <ENTERED in class location[2] is empty> <ENTERED in instance Hall[5] is empty> Hall adbg: Stepping to saviour.alan:332 <00332>: "Inside the entrance is a hallway full of dust and pieces of adbg> instances Instances: [1] #nowhere ("#nowhere") [2] pseudowords ("pseudowords") (container), at [1] #nowhere ("#nowhere") [3] nowhere ("nowhere") [4] outside ("Outside The Tall Building") [5] Hall ("Hall") [6] door ("door"), at [5] Hall ("Hall") [7] stairs ("Stairs") [8] cellar ("cellar") [9] rats ("rats"), at [8] cellar ("cellar") [10] store ("store") [11] tape ("spool of computer tape"), at [10] store ("store") [12] first_floor ("First Floor") [13] book ("old book"), at [12] first_floor ("First Floor") <<list abbreviated>> adbg> instance 13 The [13] book ("old book") Isa object[4] Location: at [12] first_floor ("First Floor") Attributes: Takeable[2] = 1 Readable[3] = 1 openable[4] = 0 startable[5] = 0 examinable[6] = 1 adbg> g Inside the entrance is a hallway full of dust and pieces of the ceiling has fallen to the floor. At the west end is a staircase, and to the south is the exit. To the east is a folding door. It is closed. > west <EXIT west[3] from Hall[5], Moving:> <ENTERED in class entity[1] is empty> <ENTERED in class location[2] is empty> <ENTERED in instance Stairs[7] is empty> Stairs You are at the landing of an old staircase. It seem steady enough to walk in, but be careful if you are going to use it. There is a passage leading up, and another leading down into a dark cellar. To the east is the hallway. A strange smell emerges from below. > up <EXIT up[5] from Stairs[7], Moving:> <ENTERED in class entity[1] is empty> <ENTERED in class location[2] is empty> <ENTERED in instance First Floor[12] is empty> First Floor The landing on the first floor is as dirty as all the others. Meters and meters of old cables are laying around, leading into a room to the east. The stairs leads up and down. They still seem alright. Through the dirty windows the barren field outside the building can be seen. Almost completely covered by dust, there is an old book laying on the floor here. > take book and read it <VERB 21, in parameter str(#1)=old book[13], inherited from object[4], CHECK:> <VERB 21, in parameter str(#1)=old book[13], inherited from object[4], DOES:> Taken. <VERB 5, in parameter object(#1)=old book[13], inherited from object[4], CHECK:> <VERB 5, in parameter object(#1)=old book[13], DOES:> As you carefully try to open the book it falls apart into dust and falls to the floor through your fingers. > debug adbg> instance 13 The [13] book ("old book") Isa object[4] Location: at [3] nowhere ("nowhere") Attributes: Takeable[2] = 1 Readable[3] = 1 openable[4] = 0 startable[5] = 0 examinable[6] = 1 adbg> trace instruction Instruction trace on. adbg> n > _north ++++++++++++++++++++++++++++++++++++++++++++++++++ 1f85: PRINT 10048, 22 "You can't go that way." 1f86: RETURN -------------------------------------------------- > west ++++++++++++++++++++++++++++++++++++++++++++++++++ 1f85: PRINT 10048, 22 "You can't go that way." 1f86: RETURN -------------------------------------------------- > east <EXIT east[2] from First Floor[12], Moving:> <ENTERED in class entity[1] is empty> <ENTERED in class location[2] is empty> <ENTERED in instance office[14] is empty> ++++++++++++++++++++++++++++++++++++++++++++++++++ fcb: LINE 0, 0 fce: PRINT 3479, 6 "Office" fcf: RETURN -------------------------------------------------- ++++++++++++++++++++++++++++++++++++++++++++++++++ fd2: LINE 0, 598 adbg: Stepping to saviour.alan:598 <00598>: "In front of you is a deserted office area. Desks and chairs adbg> g fd5: PRINT 3485, 404 "In front of you is a deserted office area. Desks and chairs are piled up in one corner. The ventilation system has partly fallen to the floor, tearing part of the ceiling down with it. Under the twisted tubing a couple of old coffee makers are crushed to pieces. One shelf, having some kind of lettering, no longer readable, is thrown to one side, and another is still standing in a corner, full of dust." fd6: RETURN -------------------------------------------------- ++++++++++++++++++++++++++++++++++++++++++++++++++ 100c: LINE 0, 616 100f: ATTRIBUTE 15, 17 =0 1010: NOT FALSE =TRUE 1011: IF TRUE 1014: LINE 0, 617 1017: PRINT 3711, 43 " There is a ladder laying on the floor here." 1018: ELSE : 1029: RETURN -------------------------------------------------- > look <VERB 19, GLOBAL, DOES:> ++++++++++++++++++++++++++++++++++++++++++++++++++ 7dc: LINE 0, 199 7dd: LOOK ++++++++++++++++++++++++++++++++++++++++++++++++++ fcb: LINE 0, 0 fce: PRINT 3479, 6 "Office" fcf: RETURN -------------------------------------------------- ++++++++++++++++++++++++++++++++++++++++++++++++++ fd2: LINE 0, 598 fd5: PRINT 3485, 404 "In front of you is a deserted office area. Desks and chairs are piled up in one corner. The ventilation system has partly fallen to the floor, tearing part of the ceiling down with it. Under the twisted tubing a couple of old coffee makers are crushed to pieces. One shelf, having some kind of lettering, no longer readable, is thrown to one side, and another is still standing in a corner, full of dust." fd6: RETURN -------------------------------------------------- ++++++++++++++++++++++++++++++++++++++++++++++++++ 100c: LINE 0, 616 100f: ATTRIBUTE 15, 17 =0 1010: NOT FALSE =TRUE 1011: IF TRUE 1014: LINE 0, 617 1017: PRINT 3711, 43 " There is a ladder laying on the floor here." 1018: ELSE : 1029: RETURN -------------------------------------------------- 7de: RETURN -------------------------------------------------- > q
In the instruction trace, lines of +
characters indicates the start of interpretation, thus they can be present inside other single step traces (like the Look
in the example above).
Lines of dashes, indicates the return from one such level of interpretation.
Part VI: Adventure Construction
54. Getting an Idea
As with a book, the success or failure depends on how intriguing the story is, how hooked you can get the reader (in our case the player). Therefore, the first step must be to get a good idea. This may be hard or easy but with time, you, like any good author, learn to pick up ideas when you get them in ordinary every-day life, and store them for later use.
A seemingly simple idea might also be developed into a good adventure if it is placed in the correct setting and supplied with additional features, tricks and problems.
When you have a good idea, try to refrain from typing it in directly in a text editor and compile it with Alan. Instead, write the story down as if it were the story line for a book or a movie. Where appropriate, insert hints on various diversions and alternate paths that come to mind, but try to stay mainly with the main story from beginning to the preferred end. Then, let a close friend read it.
55. Elaborating the Story
After having rewritten the story line once or twice, start creating the scenery. If your setting is small, you could draw a map of the locations needed, but a better way is probably to make a list of major locations first (those essential to the story). For each location note what important properties the location must have and which objects are necessary (just as notes, don’t create the Alan declarations yet!). For each object, make a small note on why the object is needed (by the player!).
This may also be done using a scene-by-scene approach. By this, we mean that the story is segmented into scenes (and maybe also acts) like in a play. For each act and scene, you do the above. This makes it easier to get an overview over a larger adventure.
I also suggest that you also create a story on a level above the actual game, at least in your own mind. This story should explain why the game-world exists and thus give a consistency to the text that you will present to the player. Nobody likes an adventure without a cause. This story or world of ideas need not be revealed to the player.
This also applies to the narrator, i.e. the imaginary person or creature that carries out the conversation with the player. Create an image of him or it and stick to it. Receiving comments about your (limited) progress in the game might be funny as long as they are not out of character.
56. Implementing it
At last, it is time to sit down at the terminal. Divide the adventure text into files containing global verbs, the map (possibly divided further according to the scenes), the actors (perhaps one file for each actor) and a main file including the other files. This makes it easy to handle the adventure and you might ask your friend to participate in the development by giving him a few files to work with.
First, just declare the locations and connect them with exits. Do not work on the “purple prose” descriptions yet. The Alan system supplies good defaults for descriptions and so on, so use these while developing the structure of the adventure. Do not bother even with the details of making it impossible to pick up the elephant, etc.
Play the adventure continuously during the development, but do not try the things you plan to make impossible later. Just go through it according to the line you planned the story to follow. A hint here is to use a separate file for the start section. In this file you can easily set up the situation you wish to test while not having to tire yourself by playing the adventure from the start every time.
57. Polishing the Adventure
There, now you have a working adventure, it’s still a bit bare bones, but still the story plays the way you planned. Now it is time to insert all the nice descriptions, the limitations and perhaps the extra things to divert and hinder the hero. Just be careful not to fall into the locked-door-syndrome. Too many adventures have been tedious to play because you need to find-key/get-key/unlock-door- with-key/open-door (anyway, why do people go around locking doors and throwing away the keys). Think big.
Start by fixing the verbs so that they prohibit the impossible. Introduce as many synonyms as you can think of, this makes the adventure so much more playable.
Create the location descriptions. Remember to use the same style in all your descriptions; breaking out of style does not look good in the eyes of the adventurous. The descriptions must give the player the correct image, the brain is still the best graphic interface available, but they should also plant ideas in the player on how to solve the problems you place before him.
Another thing to aim for is the feeling that a player gets when he somehow finds information explaining things he has encountered earlier in the game. Here, as always, it is good advice to ask a friend to read the texts and convey his or her impressions (remember you know it all because you wrote it!).
Lastly, fill in the adjectives for the objects, their descriptions and short descriptions (if needed).
58. Beta Testing
Now you might think that you can start distributing your game. But, wait! As any complex computer program, the game may have various kinds of bugs. Bugs in a work of interactive fiction range from misspellings and grammar errors in your descriptions, logic errors in your implementation of puzzles or events or omissions in the descriptions of surroundings that make the player miss or misunderstand how to act, to inconsistencies in the settings or story, plots that don’t work.
So how do you find these? Your only help are the beta testers. They are the people that you now should consider first a first trial beta release of your game. They should be people who you trust do give their honest opinion and really play it through to find any problems.
The beta testers will probably give you a long list of issues that you have to address before the next release. Some of the issues are simple; others may affect the basis of your story. You should seriously consider (and if possible discuss) such suggestions.
One aid in finding any problems in the playability of the game is to use the log file facility of the interpreter (see the section Command Logs and Game Transcripts) to produce a list of the commands a player have used. This can greatly aid in spotting troublesome areas in your game. A common example is when the player gets stuck and reverts to "guess-the-verb". The log will give you the output of the exact game played.
After having collected all this information, considered which ones to act upon, and implemented these, you should probably do it once again (sigh!).
Now, at long last, your adventure game is ready to meet its audience.
Part VII: Appendices
Appendix A: How to Use the System
How to actually set up and use the complete Alan system depends very much on which platform, OS and in which environment you are going to use it.
If you just want to run a game, there is of course the original command line version distributed from www.alanif.se. To use this, read the relevant sections below to get a feel for how that will work.
But there are also a number of other packages that include an Alan interpreter, Gargoyle and Spatterlight, are two, with Gargoyle at the point of writing, being the one most up to date, and also available on multiple platforms.
If you actually want to write interactive fiction using Alan you also need the compiler. This is distributed from www.alanif.se in what’s called the Development Kit, or the SDK (for Software Development Kit), which includes the compiler, interpreter, a conversion program for v2 games and some examples.
Whatever option you choose, there should be more detailed instructions on how to install included with that package. Below follows some information pertaining to the original versions from the Alan website.
A.1. Compiling
Although there are other options, like the AlanIDE, WinAlan et al., basic use of the Alan Adventure Development System is through a traditional command line batch compiler. This means that the actual development system is a compiler that reads text files created using any standard text editor. To compile an adventure use the following command in a command shell:
alan <adventure>
where <adventure>
is the name of the main file containing your adventure source text.
The compiler will assume an extension of “.alan” if none is supplied.
The option -help
will give a brief help on other options to the compiler.
The primary output from the compiler is an adventure code file adventure.a3c.
An identifying file, adventure.ifid, is also produced. This is a text file containing a unique identification string of your game, for bibliographical purposes; you can open it with a text editor, in case you’re curious. Its content will be compiled into the adventure code file, which makes your game identifiable by electronic means.
As long as this file exists, the same identification (IFID) will be used. If it does not exist, a new one will automatically be generated.
A.2. Compiler Switches
If you run the compiler from a command line you can get information about which switches it supports using the -help
switch:
$ alan -help
Here are some examples of other switches:
-
-version
shows the version of the compiler -
-encoding
assume source files are in this encoding. The option should be followed with one of the keywordsiso
orutf-8
. This is required when your source files written using an encoding which isn’t Alan’s default. Many terminals and editors today use the UTF-8 encoding but historically Alan only supported the ISO-8859-1 encoding for non-ASCII characters. Currently the Alan compiler will default to-encoding iso
but in the near future UTF-8 will be the default encoding for Alan source files. -
-charset
backwards compatible alias for-encoding
-
-verbose
print compiler version and other verbose messages -
-warnings
,-infos
show warning (and/or informational) messages from the compilation process -
-import
add a directory to the search path for imported files (see Chapter 32 for details on theImport
statement). This switch can be used multiple times, each adding a new directory -
-listing
direct compiler output (error messages etc.) to a file with the same base name as the input (source) file, but with the extension .lis -
-full
will produce a complete listing of the source on the screen, or if combined with the-listing
option, in the listing file -
-debug
include debugging information in the produced adventure files (same as using thedebug
option in the Alan source, see Chapter 8) -
-pack
encode and compress the text data (same as using thepack
option in the Alan source, see Chapter 8) -
-summary
produce a summary about number of actors, size of adventure files, timing information, etc. -
-dump
print the internal form (developers use mainly)
Giving an extra hyphen before the option reverses its meaning (where appropriate), e.g. --warnings
means don’t show warnings.
Switches may be abbreviated as long as they are unambiguous.
A.3. Encodings and Character Sets
If you are interested in writing adventures in anything other than English, then this section is important to you. Else you can continue to live in oblivion of the multitude of other languages that need to be supported by computer programs these days, and skip to the next section.
Alan uses a single byte character encoding known as ISO-8859-1, both internally and in game files. This makes game files completely portable across various platforms and still able to support most of the glyphs (“characters”) of most Western languages. Historically though, different operating systems and environments have used different encodings for text files, so when you created Alan source code the files were encoded differently depending on the platform (Mac, DOS, Unix, Windows, …).
There is (still) no standardized way to deduce in which encoding a text file is, so in an attempt to handle “national characters” in the game source (particularly in Swedish), the -charset
option was devised.
The options is now more appropriately called -encoding
.
Using this option an author could indicate to the compiler how to interpret, and possibly convert, the characters in the source file(s) created on another platform.
This has changed and an encoding called UTF-8 is becoming almost the standard encoding for text on many platforms, including Windows.
The Alan compiler is capable of reading and converting source files in UTF-8 (which probably is the default when you create a text file these days).
You can instruct the compiler to assume UTF-8 encoding using the -encoding utf-8
option, but if the text file is encoded using a BOM (a special, invisible marker that may be used at the beginning of UTF-8 encoded files) the Alan compiler will automatically assume that the file is encoded in UTF-8.
If there is no BOM, Alan will assume the default encoding or as instructed by the -encoding
option.
So it is safest to always use the -encoding
option.
This should make the creation and editing of Alan source files a more streamlined experience than before.
A.4. Running the Adventure
To play the generated adventure the Alan interpreter, arun, is executed with the adventure name as a parameter. For example:
$ arun adventure
No extension on the adventure name is allowed, the .a3c and, if applicable, .a3r files are inferred and found automatically from the base name.
On platforms with graphical user interfaces to which arun has been natively ported, you can double click on a game file to start a game, or double click on the interpreter application icon, in which case a dialogue requesting a game file will appear.
If the interpreter program is copied to a different name, it will automatically look for a game file with the same name. Any parameters or switches will then be ignored. For example, by copying the arun program to adventure the interpreter will, when started under the new name, directly look for the file adventure.a3c. The files adventure and adventure.a3c thus make a complete game package, which is easy to start using the single command:
$ adventure
A.5. Interpreter Switches
The interpreter supports the following switches:
-
-h
print some help -
-v
verbose mode -
-u
use UTF-8 encoding for input and output including logs and command scripts -
-l
log complete game transcript to a file (‘.a3t’) -
-c
log all player commands to a file (‘.a3s’ for “solution”) -
-n
no Status Line -
-p
don’t break the output for paging -
-d
print the version of interpreter and enter debug mode -
-t[<n>]
various levels of execution trace, higher <n> gives more details -
-i
ignore CRC and version errors in the adventure files (dangerous) -
-r
make regression test easier (don’t timestamp, page break, randomize…) -
-version
print version of the interpreter, the version and IFID of the game, if given, and then exit
Debugging support is described in Chapter 53.
Logging options
The logging options, -l
and -c
, will each create a new file with a name that is the combination of the game name, a time stamp and the appropriate extension in the current directory, and write the log in it.
The options can be used simultaneously and will then create two files with differing extensions for the two types of logs.
For example
$ arun -c adventure
This will give you a file named adventure<timestamp>.a3s
which will contain all commands the player entered.
It may later be used as the so called “solution file” which can be followed manually to “replay” that session, or even used as a command script that can be feed to the interpreter using
> @adventure<datestamp>.a3s
On the other hand, using
$ arun -l adventure
will log the complete game transcript to a file named adventure<timestamp>.a3t
.
This option can be useful when collecting information from playtesters to analyse their progress, e.g. to improve the game.
When you’re gathering feedback from beta testers, the timestamp will help collecting many such files in the same directory for analysis.
You can avoid the timestamp in filenames via the |
Some interpreters, particularly the Glk-based ones, will have their own file naming conventions, and may therefore not abide to these standardised extensions for command scripts or transcripts. |
Appendix B: A Sample Interaction
In order to give you an idea of what playing an adventure might be like, if you do not have done that, this appendix lists a typical, and authentic, interaction between a player and a game. Player input is preceded by a prompt ('>') and is emphasised for clarity.
In reading this through, you will find that it is possible to use more or less full imperative statements to communicate with the game. However, you may leave out non-important words and the game usually provides a set of common abbreviations, such as ‘w’ for ‘west’, which really means ‘go west’.
It is also common to provide possibility to refer to multiple objects in a command, either by use of ‘all’ or specifically listing the objects.
Your input is in italics.
West of House.
Welcome to Dungeon (ALAN Demo). This version created 29-FEB-92.
You are in an open field west of a big white house with a boarded front door. There is a small mailbox here.
> examine the mailbox
I see nothing special about the mailbox.
> open it
The small mailbox is now open. The small mailbox contains a leaflet.
> take the leaflet and read it
Taken!
'I hope you have noticed that this isn’t your usual Dungeon but rather an ALAN implementation brought to you by
ThoNi & GorFo Adventure Factories
Happy Adventuring!'
> inventory
You are carrying a leaflet.
> go south
South of House.
You are facing the south side of a white house. There is no door here, and all the windows are barred.
> east
Behind House.
You are behind the white house. In one corner of the house there is a window which is slightly ajar.
> open the window
With great effort, you open the window far enough to allow passage.
> go in
Kitchen.
You are in the kitchen of the white house. A table seems to have been used recently for the preparation of food. On the table you can see an elongated brown sack, smelling of hot peppers and a clear glass bottle. A passage leads to the west, and a dark staircase can be seen leading upward. To the east is a small window which is open.
> examine all
You can’t refer to multiple objects with 'examine'.
> take all
(elongated brown sack, smelling of hot peppers) Taken!
(clear glass bottle) Taken!
> up
Upstairs.
You are in the attic. The only exit is stairs that lead down. A large coil of rope is lying in the corner. There is a square brick here which feels like clay. A nasty-looking knife is lying here.
> take all
(rope) Taken!
(square clay brick) Taken!
(nasty knife) Taken!
> examine knife
I see nothing special about the knife.
> down
Kitchen (again)
A table seems to have been used recently for the preparation of food. The table is empty. To the east is a small window which is open.
> w
Living room.
You are in the living room. There is a door to the east. To the west is a wooden door with strange gothic lettering, which appears to be nailed shut. There is a trophy case here. In the center of the room is a large oriental rug.
On hooks above the mantlepiece hangs an elvish sword of great antiquity. A battery-powered brass lantern is on the trophy case. There is an issue of US NEWS & DUNGEON REPORT dated 29-FEB-92 here.
> move rug
With a great effort, the rug is moved to one side of the room. With the rug moved, the dusty cover of a closed trap door appears.
> open door
I don’t know which door you mean.
> open trap door
The door reluctantly opens to reveal a rickety staircase descending into darkness.
> take the sword, the lantern and the issue
(rug) The rug is extremely heavy and cannot be carried.
(elvish sword) Taken!
(battery brass lantern) Taken!
(issue) Taken!
> read issue
US NEWS & DUNGEON REPORT
29-FEB-92…………………………..Late Dungeon Edition
--- LATE NEWS FLASH!! ---
The Dungeon prelude is now available as an ALAN implementation. The Dungeon game was selected as an example that everybody interested in adventure gaming should recognize.
Appendix C: Run-Time Messages
This appendix describes the errors that may occur during the running of the adventure, i.e. during interpretation of the generated Acode. There are two classes of errors, player input response messages and system errors.
Input response errors are not fatal but abort the execution of the current player command and discard the rest of the user input, which is a normal part of the interaction between the player and the Alan run-time system. System errors are fatal and abort the execution of the adventure.
C.1. Input Response Messages
Various messages are printed for the benefit of the player. Most messages probably come from the adventure itself, i.e. they where provided by the adventure author. However, some messages can be given directly by the Arun interpreter. They are presented below using the Alan STRING-format, i.e. containing the special character combinations described in Output Statements. These standard messages exist for all languages and the default value of the texts are selected depending upon the setting of the language option.
The contents of any message may be modified using the Message
statement (see Chapter 20).
The identifier on the first line of a message explanation is the identifier that should be used in the Message
statement to change the contents of that message.
The text after the colon on the first line is the default English message text.
Then follows a short explanation, including possible availability of parameters and their types.
All messages are available in all supported languages but below the English texts are shown.
Although the default values of the messages are static strings, it is possible to create messages that are more dynamic.
The |
UNKNOWN_WORD : "I don't know the word '$1'."
WHAT : "I don't understand that."
The input did not follow any Syntax
the Arun parser knows about.
I.e. the input could not be matched to any of the defined syntaxes.
WHAT_WORD : "I don't understand what you mean by '$1'."
The player input a multiple word, such as “all”, “them” or a pronoun, but the Arun parser could not find any objects or actors that it could refer to.
parameter1 is a string which is the word used by the player.
MULTIPLE : "You can't refer to multiple objects with '$v'."
The Syntax
matched for the indicated Verb
did not allow multiple parameters.
NOUN : "You must supply a noun."
The player started to specify an object or actor but only supplied the adjectives.
AFTER_BUT : "I was expecting at least one object after '$1'."
In a command containing ALL BUT, the player must also give the object or objects excluded.
parameter1 is a string containing the BUT word the player used.
BUT_ALL : "You can only use '$1' after '$2'."
The BUT words may only be used after an ALL word.
parameter1 is a string containing the BUT word used by the player.
parameter2 is a string containing the ALL word used by the player.
NOT_MUCH : "That doesn't leave much to $v!"
The player used an ALL BUT construct, which explicitly excluded everything matched by the ALL.
WHICH_START : "I'm not sure if you mean $+1" WHICH_COMMA : ", $+1" WHICH_OR : "or $+1."
Multiple objects (or actors) matched the words given by the player. More adjectives are necessary to distinguish between them. The three messages are used to list the possibilities. The player can repeat the command with a more precise wording. The first message is used for the first alternative, the last for the last alternative and the middle for all the middle alternatives.
For each message, parameter1 is a reference to the alternative instance.
WHICH_PRONOUN_START : "I'm not sure whether by '$1' you mean" WHICH_PRONOUN_FIRST : "$+1"
When a pronoun given in a command matched multiple parameter in the previous command, these messages are issued to explain this and which the alternatives where.
Note that the message is different from the multiple match above only for the start of the message, the list of alternatives are the same, i.e. WHICH_COMMA
(repeated) and WHICH_OR
(the final).
NO_SUCH : "I can't see any $1 here."
The player referred to an object or actor that was not present.
parameter1 is an instance referring to an instance.
If there did not actually even exist an instance in the game with the combination of the adjectives and nouns that the player used, the interpreter uses any instance matching the noun. This still allows inflecting in accordance with the noun case, which is common in many languages (English being one of few exceptions). |
NO_WAY : "You can't go that way."
A directional word was used but there is no exit in that direction.
CANT0 : "You can't do that."
The interpreter could match the input to some Syntax
, but did not find any Verb
body to execute.
This may be a situation overlooked by the author or the player may be trying to do something that is not possible.
SEE_START : "There is $01" SEE_COMMA : ", $01" SEE_AND : "and $01" SEE_END : "here."
These messages are used to construct the default text for describing thing
s present at the current location
that have no Description
clause.
The message parts are used as in "There is <indefinite form object1>, <indefinite form object2> and <indefinite form object3> here." The parts in bold are the ones in the messages and each object is printed in its indefinite form as appropriate.
CONTAINS : "$+1 contains" CARRIES : "$+1 carries"
The messages above are used to construct the default headers for listing container
s.
The CARRIES
message is used if the container
instance is an actor
.
CONTAINS_COMMA : "$01," CONTAINS_AND : "$01 and" CONTAINS_END : "$01."
The messages above are used to construct the contents listing of a container
in much the same way as for the object listing above.
The messages are used according to the pattern "<header for container> contains <indefinite form contents1>, <indefinite form contents2> and <indefinite form contents3>."
You can modify these messages to change the formatting of listings — e.g. to one element per line.
CAN_NOT_CONTAIN : "$+1 can not contain $+2."
If an attempt to put something in a container
that does not meet the class restrictions for the container
, this message will be delivered.
IS_EMPTY : "$+1 is empty."
The default messages for empty container
s.
EMPTY_HANDED : "$+1 is not carrying anything."
The default messages for empty container
s that are actor
s.
HAVE_SCORED : "You have scored $1 points out of $2 in $3 moves."
This is the default message for presenting scores, if you use the Score
statement.
parameter1 is an integer containing the current score.
parameter2 is an integer containing the maximum score possible.
parameter3 is an integer containing the elapsed turns since the game started.
MORE : "<More>"
The classic message when the screen is full. The player should press RETURN to proceed.
AGAIN : "(again)"
When the description of a location
is suppressed due to Visits
having a value other than zero (aka “brief mode”), this message is presented immediately after the name of locations which have been already visited by the player.
In some games this might be a desirable feature, to remind the player that the location was already visited, and that its description is currently being omitted as a consequence.
If you wish to disable this message, set it to an empty string.
See the Visits
statement for more details on how this message is related to and affected by the value of Visits
.
SAVE_WHERE : "Enter file name to save in"
When executing a Save
the player can enter the name of the file to save in.
The name used in the previous Save
is used as a default.
SAVE_OVERWRITE : "A file with that name exists, overwrite (RETURN confirms) ? "
If the save file already exists, the player must confirm overwriting.
SAVE_FAILED : "Sorry, save failed."
When executing a Save
, the file system indicated some error, usually a write protected directory or full disks.
RESTORE_FROM : "Enter file name to restore from"
A Restore
statement can restore from any named file.
The previously used file name is used as the default.
SAVE_MISSING : "Sorry, could not open that save file."
When executing a Restore
, Arun could not find, or open, a save file with the name entered.
NOT_A_SAVEFILE :"That file does not seem to be an Alan game save file."
The save file found by the Restore
statement was not Alan game save file.
SAVE_VERSION : "Sorry, the save file was created by a different version."
The save file found by the Restore
statement was created by a different version of the Alan interpreter or the game.
SAVE_NAME : "Sorry, the save file did not contain a save for this adventure."
The indicated save file did not contain a save of this adventure.
REALLY : "Are you sure (RETURN confirms) ? "
This is the confirmation prompt, e.g. before overwriting an already existing save file.
QUIT_ACTION : "Do you want to UNDO, RESTART, RESTORE or QUIT ? "
The Quit
statement requests an action from the player.
The possible answers are currently hard-wired into the interpreter, so changing |
UNDONE : "'$1' undone."
When an action is undone, this message is presented to confirm the player action.
parameter1 is a string containing the player command that was undone. Note that since only commands that change any state in the game world are logged, the command might very well not be the last command.
NO_UNDO : "Nothing to undo."
If the player tries to undo an action and no further actions where recorded (because of lack of memory, undone to beginning of session, etc.) this message is used to inform the player of that fact.
IMPOSSIBLE_WITH : "That's impossible with $+1."
If a player action is impossible with a particular parameter combination, but might be possible otherwise, this message is shown to indicate that it is the action with the parameter that is impossible.
CONTAINMENT_LOOP : "Putting $+1 in itself is impossible."
The interpreter detected an attempt to locate an instance inside (contained) itself. This message reliefs the author from the responsiblility to check for every possible circumstance where this might happen.
CONTAINMENT_LOOP2 : "It is impossible to put $+1 in $+2 because $+2 is already inside $+1."
Same as above but in this case the containment was transitive, i.e. it would create a containment loop with more that one instance involved.
C.2. System Errors
System errors are errors caused by internal malfunctions. Mainly these are implementation errors (aka. bugs!), but may (in some manner) also result from user errors. The system error messages also have a purple prose style to fit in with your game, e.g.:
As you enter the twilight zone of Adventures, you stumble and fall to your knees. In front of you, you can vaguely see the outlines of an Adventure that never was. SYSTEM ERROR: Can't open adventure code file.
C.3. Player Errors
These errors are usually caused by incorrect arguments or file names entered by the player.
Can't open adventure code file.
The player attempted to run an adventure for which there were no code file available, probably a misspelling.
Could not read all A3C code. Checksum error in Acode (.A3C) file (%1 instead of %2).
These two messages indicate problems in the adventure file. Possibly caused by transfer problems of the .a3c file.
C.4. Author Errors
The following system errors are in some sense caused by the Adventure author (you).
Out of memory.
The adventure was so large that the interpreter could not allocate enough dynamic memory for it. Try to finish other running applications (does not work or is not possible on all systems), get more real memory, or complain to the Alan implementors. This might also be caused by reading incomplete or corrupted game files.
Incompatible version of ACODE program.
The version of the interpreter you are using is different than the Alan compiler used to compile the adventure. Use a different Arun or recompile the adventure with the matching compiler.
The Arun switch |
Index not in container in 'containerMember()'
This is most likely caused by doing Random In
on an empty container
.
Recursive LOOK.
This message is shown when a Look
statement is executed as a result of a Look
!
The Look
statement should only be used in Verb
bodies.
It should not be used in descriptions of instances because there is a definite risk that it will be executed as the effect of a Look
, either explicit or implicit (by the hero entering a location
which would trigger a Look
in itself thus starting the recursion!).
Locating a location that would create a recursive loop of locations containing each other.
This means that an attempt to locate a location
inside itself has been made.
Probably in an attempt to dynamically manipulate the location
structure (the map).
Non-existing parameter referenced.
A parameter that wasn’t available was referenced.
This is probably due to using a parameter shorthand such as $2
inside a string in a context where the Syntax
was restricted to only one parameter.
This may avoided by using the Say
statement instead of the embedded string parameter references, which would allow compile time checking, thus avoiding the risk of having this happen to the player.
Parameter references embedded in strings are currently not checked during compile time. |
Interpreter recursion.
The interpreter keeps track of its execution so that it can never enter an endless loop.
There are a few situations where this can occur.
One example is if the Description
of an instance in some way, directly or indirectly, executes Describe This
.
As the interpreter is already executing a Description
of the current instance the invocation of the second will create a loop that never terminates.
C.5. Implementor Errors
Any other text in a system error message is really a SYSTEM ERROR. Scribble down the text and contact the implementors. If possible, supply the source for your adventure, a trace of the few last player commands (if possible with single step and trace turned on, see Debugging).
Appendix D: Language Grammar
D.1. Description
In this manual, the Alan language is defined using a BNF-form, which you can see in most descriptions. The grammar is a set of rules defining what constructs are legal in the source for an Alan program. Below follows a brief explanation on how to interpret these rules by using some short examples. For details on the actual rules, refer to the content of Part II.
The BNF form divides the rules for structure of the input source by describing it in smaller parts, which may in turn be defined by other rules. For example, a rule might say that an ADVENTURE (in this case an Alan program) consists of options, declarations and a start section. This grammar rule would look like:
adventure = [options] {declaration} start_section
Each item that is an identifier (options
, declaration
, etc.), is a construct that in turn is defined by other rules, possibly elsewhere in the manual.
The equal sign (=
) may be read as “consists of” or “is defined as”.
Optional parts are surrounded by square brackets ([
and ]
).
Parts that may be repeated are enclosed in curly braces ({
and }
).
= |
→ ‘is defined as’ |
[ ] |
→ ‘optional’ |
{ } |
→ ‘zero or more times’ |
So the rule might be read as “an adventure consists of options which are optional, zero or more declarations and a start_section”.
If the item to the left of the equal sign may be defined in multiple ways, the alternatives are divided by a vertical bar (|
).
For example:
declaration = messages
| class
| instance
| verb
| rule
| synonyms
| syntax
| verb
| event
| addition
This definition says that a declaration might be messages, a class definition, an instance declaration, etc.
The basic components of the language are reserved words and symbols. These are represented in the rules by quoted strings of characters. These are not defined elsewhere, but should instead be written as indicated. Character case is not significant.
random_expression = 'RANDOM' 'IN' expression
The reserved words Random
and In
must be followed by an expression (which, to make sense, must refer to a container
instance) to form a random_expression (which in itself is an expression).
D.2. Keywords
The following is a complete list of all keywords in the Alan language. Note that they can still be used as identifiers in a source file if the rules described in Chapter 29 are followed. Basically this means that if you surround them by single quotes they can be used as identifiers in your source code anyway. This might be especially important if you want to use any of these words as words the player might want to input, such as part of a name for an item.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Although the Alan language is case-insensitive, the keywords in the above list are cased according to the styling conventions adopted in this document, for the sake of consistency and to provide an intuitive association between keywords and their role in the language. |
Appendix E: Predefined Player Words
Alan defines a set of words for the player to use, which are required for the syntax variations described in Player Input. These words are available even without any declarations at all in the game source. Some of these might conflict with, or complement, words defined in the source. The lists below contain those player words for the currently defined languages.
Appendix F: Compiler Messages
F.1. Format of Messages
This appendix describes the error messages generated by the Alan compiler. The compiler presents the messages in the order of occurrence in the file. The offending source line is always shown together with the message. The following example illustrates a typical compiler output.
ZILexample.alan (1) 23. If barfoo Is foobared Then (2) =====> 1 *1* 310 E : Identifier 'barfoo' not defined. 27. Exit north To Rumble. =====> 1 (3) *1* 310 E : Identifier 'Rumble' not defined. (4) 28. Exit west To Tumble. =====> 1 *1* 310 E : Identifier 'Tumble' not defined. 46. =====> 1 *1* 101 E : Syntax error. Inserting "start here ." before this token. *1* 211 E : Adventure must start at an instance inheriting from 'location'. 5 error(s). (5) No detected warnings. 1 informational message(s).
The following information is available in the compiler listing, framed for visibility:
1 | File name |
2 | Line number and source text of a line |
3 | Message indicator |
4 | Message pointer, message number and text |
5 | Message summary (three lines) |
For information on how to select which levels of messages to show and where output is directed, refer to the options and their descriptions in section Compiler Switches.
F.2. Message Explanations
For each message, a short description of the error, its possible causes, etc., are given.
Each reported message also indicates the severity of that error.
The message is supplemented with an indication of its severity.
An informational message (indicated by the letter I
) simply gives some information to the user, a warning message (W
) indicates an error but the compilation still generates a valid output (although not always what the user intended).
Error messages (E
) indicate errors that have made it impossible to generate any output, but the compiler will continue to process all input.
Fatal (F
) and system (S
) messages always terminate the compilation process immediately.
The message descriptions below may also contain the special insertion markers %n
(where n
is a digit), which indicate that text will be inserted at that point in the message during compile time, e.g. the offending identifier or a file name.
100 Parsing resumed here.
A severe syntax error was discovered. Some input was skipped. This error message marks the place where the parsing was restarted.
101 %1 inserted.
A syntax error was discovered and one or more symbols inserted in the input in an attempt to recover.
102 %1 deleted.
A syntax error was discovered and one or more symbols were skipped from the input in an attempt to recover.
103 %1 replaced by %2.
A syntax error was discovered and one or more symbols were replaced by one or more other symbols in an attempt to recover.
104 Severe syntax error, construct ignored.
An intricate syntax error was discovered. A complete construct was skipped in an attempt to recover.
105 Syntax error, couldn't recover. 106 Parse stack overflow. 107 Parse table error. 108 Parsing terminated.
Alan compiler implementation errors. Should not occur!
150 Unterminated STRING.
An opening double quote was not terminated by a closing quote before end of file. Error message points to the opening quote. Remember STRINGs may cover several lines.
151 File name missing for $INCLUDE directive.
An include directive was given but no file was indicated. The complete file name must be given according to the rules in section Chapter 32.
198 Could not open output file '%1' for writing.
The indicated output file could not be opened, probably because the directory did not exist or the file or directory was write-protected.
199 Adventure source file (%1) not found.
The source file given on the command line did not exist. The Alan compiler adds the “.alan” extension to the file name given, if it did not include a period.
201 Mismatched block identifier, '%1' assumed.
The identifier following a terminating End
did not match the one given at the beginning of the construct.
This indicates an illegal nesting or a missed End If
.
The identifier indicates to which block the End
is assumed to belong.
202 Multiple usage of direction '%1' in this Exit. 203 Multiple definition of Exit '%1' in this location.
The directional word indicated was used more than once, either in the same, or different Exit
declaration from the location
.
This is contradictory and not legal.
204 Multiple definition of %1 DEFAULTS. Ignored.
Only one declaration of default attributes per type is allowed. The second declaration is ignored.
205 Multiple usage of '%1' in this VERB definition.
When specifying actions for multiple Verb
s in the same declaration, the indicated word occurred twice.
206 Multiple definition of SYNTAX for %1.
More than one Syntax
definition for the same Verb
was found.
This is an error.
You should remove the offending one.
207 VERB '%1' is not defined.
A Syntax
construct defined the syntax for a verb that was never defined.
208 '%1' is not a VERB.
The identifier on the left hand side of a Syntax
definition was defined as something that was not a Verb
.
210 Action qualification not allowed here.
The Before
, After
and Only
qualifiers may not be used in a Does
clause in this context.
211 Adventure must start at a Location.
You specified a WHERE expression in the Start
section that did not specify an explicit location
.
The Start
section specifies where the hero starts and must be a location
.
212 Syntax parameter '%1' overrides symbol.
The Syntax
definition valid in this context defined a symbol that is the same as an entity (class or instance).
The syntax parameter will take precedence.
213 Verb alternatives not allowed here.
You may only specify different Verb
body alternatives within objects.
The global Verb
body and the verb body in a location
may not have alternatives.
214 Parameter not defined in syntax for '%1'.
The identifier given as the selector in a Verb
body alternative was not defined in the Syntax
for that verb.
215 Syntax not compatible with syntax for '%1'.
To be able to use the same body for different verbs by supplying them in a comma-separated list in the Verb
header they must all be compatible.
This means that they have the same number of parameters and the parameters have the same names.
Otherwise conflicts will arise when figuring out which parameter to use.
216 Parameter '%1' multiply defined in this SYNTAX.
The parameter was defined more than once in the same Syntax
definition.
217 Only one multiple parameter allowed for each syntax. This one ignored.
To be able to use multiple parameters in a player command only one parameter may be marked as referring to multiple objects or actors using ALL or conjugations.
This is a warning, the Syntax
will be as if the first multiple marker was the only one.
218 Multiple definition of attribute '%1'.
The indicated attribute name was defined more than once in the same context (default attribute list or within the same entity). Remove one definition.
220 Multiple definition of '%1'.
The indicated word has multiple, and possibly different, definitions.
221 Multiple class restriction for parameter '%1'.
The same parameter occurred more than once in the list of class restriction in the same Syntax
definition.
222 Identifier '%1' in class definition is not a parameter.
Only the parameters in the Syntax
may be referenced in the class-restricting clause of a Syntax
definition.
230 No syntax defined for this verb, assumed '%1 (object)'.
This message is a warning to indicate that the default syntax handling has been used.
310 Identifier '%1' not defined.
The indicated word was never defined.
It must be declared either as a location
, an object
, a Container
, an actor
or an Event
.
311 Must refer to %1.
The construct indicated does not refer to the correct kind of item, the message indicates which kind of item was expected.
312 Parameter not uniquely defined as %1, which is required.
In certain contexts it is necessary to refer to a particular type of entity, e.g. the In
expression must refer to a Container
or an object
with the Container
property.
If the reference (the WHAT part) is a parameter identifier, this parameter must be restricted to be of the required type by use of parameter restrictions (such as Where c IsA Container
).
315 Attribute not defined for '%1'.
The indicated attribute is not defined for the particular object
, location
or actor
.
It must either be a default attribute or be locally declared.
318 Entity '%1' is not a Container.
The referenced entity (object
or actor
) was not declared to have the Container
property, although the context required a container.
320 Word '%1' belongs to multiple word classes (%2 and %3).
A word was declared as to belong to different word classes such as noun, verb, adjective etc. Only multiple declarations that may lead to unexpected behaviour are reported, usually because of limitations in the current implementation. Generally it is allowed to declare a word e.g. as both an adjective and a noun.
321 Synonym target word '%1' not defined.
To define a synonym its target word (the word on the left side of the equal sign) must be defined as a proper word elsewhere in the source.
322 Word '%1' already defined as a synonym.
A word may not be declared as a synonym for different target words.
330 Wrong types of expression. Must be of %1 type.
In an expression, a value or an expression was used that had a type that was not allowed. The message indicates the correct type.
331 Incompatible types in %1.
The two values in an expression with a binary operator did not have compatible types, or the value used in a Set
statement was not type compatible with the referenced attribute.
332 Type of local attribute must match default attribute.
An attribute declared locally (within an object
, actor
or location
) that has the same name as a default attribute, has to have the same type (Boolean, integer or string).
333 The word '%1' is defined as a synonym as well as of another word class.
Synonyms must be words not defined elsewhere.
400 Script not defined for Actor '%1'.
No Script
with the indicated identity was defined for the actor
.
401 Actor reference required outside Actor specification.
Inside an actor
specification it is permissible to leave out the actor reference in a Use
statement in which case the surrounding actor is assumed.
Outside actor specifications, the actor reference must always be supplied.
402 An Actor can't be inside a Container.
The Locate
statement tried to locate an actor
inside a Container
.
This is not allowed.
403 Script number multiply defined for Actor '%1'.
The indicated number was used for more than one Script
for the same actor
.
404 Attribute to %1 must be a default attribute.
To reference attributes for object
, location
and actor
the attribute used must be a default attribute, as all objects, locations or actors must have it.
405 The class of a parameter used in %1 must be uniquely defined.
In some statements the class of the identifier must be determined during compile time.
This is, for example, the case in Make
and Set
statements.
406 A parameter defined as Container have no default attributes.
A parameter that was restricted to containers does not have any default attributes.
Actors, objects and locations have separate sets of default attributes.
In order to refer to an attribute on a parameter it must be restricted to one of these classes.
If the parameter also requires the container property, use Container actor
or Container object
.
407 Attribute in LIMITS must be a default attribute.
Every object must possess the attribute which the Limits
clause needs to test.
408 Attributes in %1 must be of Boolean type.
The attribute referenced in the indicated context must be a Boolean attribute.
409 No parameter defined in this context.
No parameter is defined in the context where a reference to object
was made.
Parameters are only defined within Check
s and bodies of Verb
s, so the use of object
(an obsolete construct, use the parameter identifier instead) is also restricted to those contexts.
See Run-Time Contexts.
410 A parameter may not be used in %1.
In certain statements a parameter may not be used at all.
411 %1 ignored for Actor 'hero'.
It is allowed to redefine the predefined actor hero
(the player).
This makes it possible to define local attributes and descriptions for the hero.
However any definitions of scripts or initial location are ignored (the script is supplied by the player in his input and the initial location is defined in the Start
section).
412 'ACTOR' is not allowed inside events.
In Event
s no actor is active.
This means that no reference to the active actor
can be made.
See Run-Time Contexts.
413 Expression in %1 must be of integer type.
The context required a numeric expression.
414 Invalid initial location for %1.
The initial location
specified was not valid.
415 Invalid Where specification in %1 statement.
The statement indicated does not allow the WHERE specification used.
416 Interval of size 1 in RANDOM expression.
This message informs that the interval in a Random
statement was just one single value, resulting in always returning the same value, not very random.
417 Comparing two constant entities will always yield the same result.
The expression compared two identifiers none of which was a parameter. This will always give the same result. This is probably an error, but the message is still a warning as it gives a perfectly running adventure (but, perhaps not what you intended?).
418 Aggregate is only allowed on integer type attributes.
The aggregates Max
and Sum
can only perform their calculation on integers.
419 Expression in %1 must be of integer or string type.
In the indicated context only integer and string type expressions may be used.
501 LOCATION '%1' has no Exits.
In case the hero is located at the indicated location
he may not be able to escape from that location.
This may be intentional (as for a limbo location or a location with magic words to use as an escape) but the warning is presented as a reminder.
600 Multiple use of option '%1', ignored.
The indicated Option
was used more than once, this occurrence is ignored and the previous setting used.
601 Unknown option, '%1'.
A word was given in the Option
section that was not the name of an option.
602 Illegal value for option '%1'.
The indicated Option
does not allow the value used.
997 SYSTEM ERROR: %1
A severe implementation dependent error has occurred (a bug!). Please report.
998 Feature not implemented in %1.
The combination of some syntactically correct but semantically tricky constructs is not yet implemented. Please report.
999 No Adventure generated.
When an error is detected this informational message is given to indicate that no executable adventure was output.
Appendix G: Localization
When it comes to writing text adventures in languages other than English, the Alan IF system is a great choice. Since Alan doesn’t enforce on authors a fixed world model nor a predefined set of verbs, its open-ended philosophy simplifies adapting Alan to other languages.
To create adventures in languages other than English, there are a few issues that need to be taken into account first:
-
Special characters support (letters with accents, diacritics, etc.).
-
Handling of grammatical number, gender person and case.
-
Defining
Synonym
s for variations of the basic articles and prepositions. -
Provide some initialization mechanism on all game instances to enforce grammatical consistency via custom attributes.
Alan was designed around English as the default language, but implementing support for other languages should be a fairly easy task, thanks to Alan’s flexible design and syntax. The best approach is to create a dedicated “grammar module” to supported your desired language, which can then be reused to create any adventure in that language. Alan has already been successfully localized to Italian, so you could look at the Italian grammar module for inspiration.
The sections below provide some guidelines and insight on establishing if Alan is able to support a given language, and which steps are required to implement a core grammar module to support that language. Because every language is different, it’s impossible to provide a set of fixed guidelines; instead, we can provide practical examples of how specific features of other languages where (or could be) implemented in Alan.
Before embarking on localizing Alan to a specific target language, you’re advised to:
-
Study how other languages where implemented in Alan.
-
Study how the target language was implemented in other IF systems (if any).
-
Read Chapter V: Natural Language of The Inform Designer’s Manual (DM4), by Graham Nelson, which provides precious insights on how IF systems can handle different languages.
Looking at how others have accomplished similar tasks is always a great source of inspiration.
G.1. Character/Glyph Availability
Many languages rely on characters (or glyphs) which are not found in the English alphabet, like ö, Ñ or æ. Alan uses a character encoding called ISO-8859-1 (aka Latin1) which covers most characters (but not all) used in European and Western countries.
To use these characters you just need to ensure that the adventure source files adopt this character encoding. Most editors allow specifying a specific encoding, either when you save a file the first time, or through a dedicated menu entry, the settings panel or by associating a file extension with a specific encoding. Some operating systems might allow you to define the default encoding.
If the Alan source files are encoded using ISO-8859-1 they will be presented correctly when the game is run in graphical interpreters like WinArun and Gargoyle. If you run the game in console mode, you must ensure that both your console program and your operating system are set (and able) to show characters using the ISO-8859-1 encoding — often just setting the correct encoding is not enough, because some console fonts might not contain all the special characters glyphs required, and will be unable to represent them. Make sure that you also install the right console font, and to set it as your shell/CMD default font.
For languages with non-Latin alphabets, currently the only viable solution is to fallback on some romanization convention. In the early years of the Internet, when Ascii was the standard charset in communications, many creative solutions were devised to allow communicating in non-Latin languages over BBSs, chats and (later on) via sms using only Ascii characters. Many of these “chat alphabets” are still popular today, and you can find many tools to convert to and from them. For example, there is the Arabish chat alphabet for Arabic, Volapuk for Russian, and Shlyokavitsa (or Maimunitsa) for Bulgarian — just to mention a few.
Beware though that in Alan you won’t be able to freely mix letters, numbers and symbols as these alphabets do, for there are some strict rules on what is considered a valid player input token. You’ll need to be creative, and find ways to adapt your needs to the restrictions of the Alan language.
G.2. Standard Messages
There are two main types of messages that are output by the interpreter to the player. They are initiated by various parts of the Alan system. The built-in messages are hardcoded into the compiler (see Sect. C.1) but can be changed to messages in the available languages. If you are using any of the available standard libraries, that library also issues messages, often overriding the built-in messages.
The only way to translate messages in the library is to translate the library. This is probably what you want to do anyway, since most of the verbs would be in English.
Messages built into the compiler are generated automatically into the game file, e.g. error messages like “You can’t do that.”
These can either be changed by the Language
option (if the language of your choice is already supported), or translated using the Message
statement.
Appendix C lists all such messages and their content.
There are a few special words that are currently not author translatable, as described in Appendix E.
G.3. Player Words
When creating adventures in a language not natively supported by Alan, one of the first things you’ll need to look into are the predefined player words (see Appendix E for more details).
For example, noise words are useful for defining words which should be ignored by the parser. Articles are usually defined as noise since they are irrelevant in adventure games input, and it’s convenient that when the player types:
> take the apple
the parser will only have to process “take apple” (the article “the” having been discarded as noise). If this wasn’t the case, then authors would have to define multiple syntaxes for every verb in order to account for the presence of the article — which, in terms of understanding player commands, doesn’t provide any meaningful information. Some languages have many different articles forms, one for each number and gender combination, and unless these are defined as noise words they would lead to numerous alternative syntaxes in the adventure source.
If you’re adventure uses English as the base language, then you can define your noise words by making them synonyms of a known English noise word (e.g. “the” or “go”). For example, with the Italian language we would define all possible articles as noise this way:
Synonyms il, lo, la, 'l''', i, gli, le = 'the'.
This line of code ensures that all Italian definite articles will be ignored by the parser (stripped away from the player input).
In this regard, it’s important that you study carefully the list of all the predefined player words in the supported languages, as found in Appendix E, to check that none of them conflicts with a needed word of your language. Since these player words are hardcoded into Alan, you can’t change them nor undefine them. This is the criteria for choosing the base language for your adventure.
Ultimately, whichever base language you choose is irrelevant, for you’ll be translating all the Alan messages, so the base language is going to be hidden away in the background of your adventure.
If you can use English without conflicts, then go for it since it’s the default language.
To use another natively supported language (i.e. German or Swedish), just enable it via the Language
option.
E.g.:
Language German.
G.4. Word Variations
TBD. Text about using multiple names for the same objects & synonyms, and how the interpreter chooses which word to use when. |
G.5. Word Order
A language may have different word ordering than English. Usually this is one or both of the following
-
verb/noun - does the verb preceed the noun in imperative sentences?
-
adjective/noun - does the adjectives preceed the noun
For example, in German the command for “take the book” would have the noun preceed the verb and be:
(literally, “the book take [it]”) where the parameter occurs in first position, requiring the following Syntax
definition:
Syntax take = (o) nehmen.
TBD. Description on how to handle noun/adjectives ordering. |
G.6. Useful Links
Here are some links to useful resources for localizing Alan to other languages.
-
Alan Italian — Alan 3 support for Italian via a core grammar module, and an Italian port of the Alan Standard Library.
-
The Inform Designer’s Manual (4th Ed.) by Graham Nelson, 2001:
Appendix H: Portability of Games
The adventure files produced by the Alan compiler is compatible across all supported platforms. This means that by copying the binary “.a3c” file (and any “.a3r” file, if available) to another machine it should be possible to run the game on that new machine without any changes. Note however that the files must be transferred in binary mode (where applicable). All characters are automatically converted to the native set allowing multi-national characters to be presented correctly even on machines that do not support the ISO-8859-1 standard. This is of course restricted to characters having a representation in the current native character set.
It is a strong goal to achieve complete portability of the games to be able to provide games for all supported platforms without re-compilation. Game authors are encouraged to seriously consider this when designing games and not use any system specific characters, character combinations or special commands that may be available only on some systems.
Portability between different versions of the system is not guaranteed. Again, backward compatibility of compiled adventure files is a main goal, at least between different builds within the same major version. This means that the most recent v3 interpreter should run games from all previous v3 compilers.
Changes in the game file format may occur between versions, which may or may not be compatible. Conversion tools may be available, older interpreter versions can be requested.
Appendix I: Copying Conditions
The Alan System is distributed under the Artistic License 2.0 for which the full text follows. The intent of this licensing is that the Copyright Holder retain some control over the development of the Alan System, while still keeping it available as open source and free software.
In practical terms this means that the licensing is chosen so that it should be possible to:
-
freely distribute games produced with the Alan system, including for profit
-
re-distribute compiled versions of the Alan system, including together with a game which is not open source or free, provided there is no charge for the Alan system
-
redistribute compiled and/or source versions of the original Alan system (the Standard Version)
-
acquire the source code for the Standard Version
-
modify the source code for private use
-
re-distribute compiled and/or source of a Modified Version provided they are done so under a compatible license with appropriate attribution and that the modification is described and made available, preferably by returning it to the Copyright Holder so that it can be merged into the Standard Version
Artistic License 2.0
Preamble
This license establishes the terms under which a given free software Package may be copied, modified, distributed, and/or redistributed. The intent is that the Copyright Holder maintains some artistic control over the development of that Package while still keeping the Package available as open source and free software.
You are always permitted to make arrangements wholly outside of this license directly with the Copyright Holder of a given Package. If the terms of this license do not permit the full use that you propose to make of the Package, you should contact the Copyright Holder and seek a different licensing arrangement.
Definitions
"Copyright Holder" means the individual(s) or organization(s) named in the copyright notice for the entire Package.
"Contributor" means any party that has contributed code or other material to the Package, in accordance with the Copyright Holder’s procedures.
"You" and "your" means any person who would like to copy, distribute, or modify the Package.
"Package" means the collection of files distributed by the Copyright Holder, and derivatives of that collection and/or of those files. A given Package may consist of either the Standard Version, or a Modified Version.
"Distribute" means providing a copy of the Package or making it accessible to anyone else, or in the case of a company or organization, to others outside of your company or organization.
"Distributor Fee" means any fee that you charge for Distributing this Package or providing support for this Package to another party. It does not mean licensing fees.
"Standard Version" refers to the Package if it has not been modified, or has been modified only in ways explicitly requested by the Copyright Holder.
"Modified Version" means the Package, if it has been changed, and such changes were not explicitly requested by the Copyright Holder.
"Original License" means this Artistic License as Distributed with the Standard Version of the Package, in its current version or as it may be modified by The Copyright Holder in the future.
"Source" form means the source code, documentation source, and configuration files for the Package.
"Compiled" form means the compiled byte code, object code, binary, or any other form resulting from mechanical transformation or translation of the Source form.
Permission for Use and Modification Without Distribution
-
You are permitted to use the Standard Version and create and use Modified Versions for any purpose without restriction, provided that you do not Distribute the Modified Version.
Permissions for Redistribution of the Standard Version
-
You may Distribute verbatim copies of the Source form of the Standard Version of this Package in any medium without restriction, either gratis or for a Distributor Fee, provided that you duplicate all of the original copyright notices and associated disclaimers. At your discretion, such verbatim copies may or may not include a Compiled form of the Package.
-
You may apply any bug fixes, portability changes, and other modifications made available from the Copyright Holder. The resulting Package will still be considered the Standard Version, and as such will be subject to the Original License.
Distribution of Modified Versions of the Package as Source
-
You may Distribute your Modified Version as Source (either gratis or for a Distributor Fee, and with or without a Compiled form of the Modified Version) provided that you clearly document how it differs from the Standard Version, including, but not limited to, documenting any non-standard features, executables, or modules, and provided that you do at least ONE of the following:
-
make the Modified Version available to the Copyright Holder of the Standard Version, under the Original License, so that the Copyright Holder may include your modifications in the Standard Version.
-
ensure that installation of your Modified Version does not prevent the user installing or running the Standard Version. In addition, the Modified Version must bear a name that is different from the name of the Standard Version.
-
allow anyone who receives a copy of the Modified Version to make the Source form of the Modified Version available to others under
-
the Original License or
-
a license that permits the licensee to freely copy, modify and redistribute the Modified Version using the same licensing terms that apply to the copy that the licensee received, and requires that the Source form of the Modified Version, and of any works derived from it, be made freely available in that license fees are prohibited but Distributor Fees are allowed.
-
-
Distribution of Compiled Forms of the Standard Version or Modified Versions without the Source
-
You may Distribute Compiled forms of the Standard Version without the Source, provided that you include complete instructions on how to get the Source of the Standard Version. Such instructions must be valid at the time of your distribution. If these instructions, at any time while you are carrying out such distribution, become invalid, you must provide new instructions on demand or cease further distribution. If you provide valid instructions or cease distribution within thirty days after you become aware that the instructions are invalid, then you do not forfeit any of your rights under this license.
-
You may Distribute a Modified Version in Compiled form without the Source, provided that you comply with Section 4 with respect to the Source of the Modified Version.
Aggregating or Linking the Package
-
You may aggregate the Package (either the Standard Version or Modified Version) with other packages and Distribute the resulting aggregation provided that you do not charge a licensing fee for the Package. Distributor Fees are permitted, and licensing fees for other components in the aggregation are permitted. The terms of this license apply to the use and Distribution of the Standard or Modified Versions as included in the aggregation.
-
You are permitted to link Modified and Standard Versions with other works, to embed the Package in a larger work of your own, or to build stand-alone binary or byte code versions of applications that include the Package, and Distribute the result without restriction, provided the result does not expose a direct interface to the Package.
Items That are Not Considered Part of a Modified Version
-
Works (including, but not limited to, modules and scripts) that merely extend or make use of the Package, do not, by themselves, cause the Package to be a Modified Version. In addition, such works are not considered parts of the Package itself, and are not subject to the terms of this license.
General Provisions
-
Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License. By using, modifying or distributing the Package, you accept this license. Do not use, modify, or distribute the Package, if you do not accept this license.
-
If your Modified Version has been derived from a Modified Version made by someone other than you, you are nevertheless required to ensure that your Modified Version complies with the requirements of this license.
-
This license does not grant you the right to use any trademark, service mark, trade name, or logo of the Copyright Holder.
-
This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, use, offer to sell, sell, import and otherwise transfer the Package with respect to any patent claims licensable by the Copyright Holder that are necessarily infringed by the Package. If you institute patent litigation (including a cross-claim or counter-claim) against any party alleging that the Package constitutes direct or contributory patent infringement, then this Artistic License to you shall terminate on the date that such litigation is filed.
-
Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTORWILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Executive Summary
So, in short, the interpreter Arun and any game produced using the Alan System is yours. You may sell or copy it as you like, and as you need the interpreter to run the game, it may be copied freely too. The Arun interpreter may also be uploaded on BBS’es or FTP-sites to allow players to download an interpreter for his platform and use that to run your game.
The documentation and examples are free to copy or place on any BBS’es or FTP-sites if their contents are not changed.
If you create a game using the Alan System, we’d very much like to see it. Send us a copy (preferably in source) and any documentation or a description of the game and its novel features. We will of course honour any copy-restrictions that you might want to place on it.
Short games or samples of Alan source are also most welcome as examples that we might use and distribute to other users. Sending an example means that you waive all rights to it. If you also supply a walkthrough to your example it will be added to the growing suite of test data and thus help us further improve the quality of the Alan system.
Glossary
Here follow some definitions of technical terms encountered in this manual, to bridge the gap between authorship as a writer and a programmer. Although Alan is designed to be usable by non-programmers, knowing the terms of this glossary might help authors to better grasp some aspects of writing adventures in Alan, by providing an outlook into the wider context of computer languages and programming, and by providing the technical jargon required to discuss technical issues and further research some topics.
- stropping
-
In the realm of computer programming, stropping is a technique to enable end users to create identifiers that contain reserved words of the language, by means of a special notation that allows the compiler (or interpreter) to distinguish between a user-defined identifier and a kewyord of the language, thus avoiding clashes that would prevent compilation/execution of the code. In the Alan language, identifiers containing one or more reserved keywords can be stropped by enclosing the whole identifier within single straight quotes (
'
). Example:The 'The Empty Room' IsA location.
(keywordsTHE
andEMPTY
stropped). For a detailed explanation and practical examples, see Sect. 29.2. For more info on stropping, see Wikipedia.
A word not in the dictionary was used by the player.
parameter1 is a string containing the word used.