Execution of an Adventure | Table of Contents | Adventure Construction |
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.
Attributes are primarily used for holding status information about the object, actor or location to which it belongs. This allows, for example, the water bottle to contain three levels of water.
OBJECT bottle HAS level 3. VERB drink DOES IF level OF bottle > 0 THEN DECREASE level OF bottle. ELSE "There is no more water in the bottle." END IF. END VERB drink. END OBJECT bottle.
Another example is the broken mirror.
OBJECT mirror IS NOT broken. VERB break DOES MAKE mirror broken. END VERB break. END OBJECT mirror.
The appropriate verbs defined in the objects may then modify the attributes and thus update the status information.
But attributes defined for all objects also allows a kind of classification of the objects (or locations or actors as appropriate). If the following declaration is made
OBJECT ATTRIBUTES NOT takeable.
all objects receive the attribute "takeable" and if the attribute is not specifically redeclared for an object it will not be takeable. Note however that the semantic meaning of "takeable" 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.
In the same way restrictions concerning what is possible to eat, drink, open etc. may be implemented. This use of attributes to classify objects is "action-oriented", i.e. they imply that a particular action (verb) is applicable to the object.
An alternate approach is to classify objects after their characteristics. Consider:
VERB take CHECK OBJECT IS NOT heavy ELSE "That is much too heavy." AND OBJECT IS NOT animal ELSE "The $o moves quickly away, just far enough for you not to reach it." DOES LOCATE OBJECT IN inventory. END VERB take.
This approach is more "class-oriented" as the objects are classified and a verb is possible to apply to certain classes of objects and not to others. This approach is more elegant but is harder to keep track of as you introduce new objects (which class or even classes does a new object belong to?).
The attributes are also used when presenting information about status to the player. The attributes are tested in IF-statements to modify the DESCRIPTIONs and possibly even the short description in the MENTIONED sections. For example:
OBJECT mirror 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 OBJECT mirror.
To use this feature with the short descriptions makes the adventure feel a bit more consistent.
OBJECT bottle HAS level 3. ARTICLE "" MENTIONED IF level OF bottle > 0 THEN "a bottle of water" ELSE "an empty bottle" END IF. END OBJECT bottle.
> inventory You are carrying an empty bottle
As your library of adventures grow you will find that some verbs are always needed, and always function the same way. Examples are "take", "drop", "invent", "look", "quit" and so on. It is advised to use an include file (see Files ) containing these 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 include this file, making these features immediately accessible when you start a new adventure. All that this takes is some thought as to what names to use for the attributes as discussed in Use of Attributes on page 63 .
Another common feature is the closed door. Here's how to implement it.
OBJECT treasury_door AT hallway VERB open DOES MAKE treasury_door open. MAKE hallway_door open. END VERB open. END OBJECT treasury_door. LOCATION hallway EXIT east TO treasury CHECK treasury_door IS open ELSE "The door to the treasury is closed." END EXIT. END LOCATION hallway. OBJECT hallway_door AT treasury VERB open DOES MAKE treasury_door open. MAKE hallway_door open. END VERB open. END OBJECT treasury_door. LOCATION treasury EXIT west TO hallway CHECK hallway_door IS open ELSE "The door to the hallway is closed." END EXIT. END LOCATION treasury.
Note that we need two doors, one at each location, but they are synchronised by always making them both open or closed at the same time. The check in the EXITs makes sure that the hero can not pass through a closed door.
Containers are either pure containers or objects or actors with the container property. A pure container is always considered to be where the hero is. This means that the inventory (what the hero is carrying), his clothes etc. are suitable to be pure containers.
For a container to be directly manipulable by the player it must be an object (or actor). This means that it always is located at a particular location in the same way as other objects. A container (in the following the term container is used to refer to objects with the container property) is always open, i.e the objects it contain are always accessible.
To be able to "close" a container, i.e. to make it impossible for the hero to take or see things inside a container, the following technique may be used (other techniques may be possible and even better!). Create an extra object with the container property, this container is used as a temporary storage for objects in the first container (the one the player is seeing). Place this at a location not accessible to the player (the limbo location Nowhere always comes in handy!).
The verbs "open" and "close" then get the following definition within the object:
OBJECT chest AT treasury CONTAINER IS NOT open. VERB close DOES MAKE chest NOT open. EMPTY chest IN chest_contents. END VERB close. VERB open DOES MAKE chest open. EMPTY chest_contents IN chest. "Opening the chest reveals its contents." LIST chest. END VERB open. END OBJECT chest.
The trick used here is to make all the things in the container disappear when it is closed. To do this, the extra container chest_contents is used as a temporary holding place for the things inside the chest. Note that we need to make chest_contents an actual object since pure containers are always accessible (they are where the hero is!). When the chest is opened again we simply empty the contents of the chest_contents container into the chest, and Voila !
Actors are a vital component to make a story dynamic. They move around and act according to their scripts. To make the player aware of the other actor's actions they need to be described. This must be done so that the player always get the correct perspective on the actions of the actors.
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 in the same way as objects are. This means that a good idea is to include the description of an actor's activities in the description of him. One way to do this would be to use attributes to keep track of the actors state and test these in the description clause.
ACTOR george 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. As, at least a part of, the state is 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
ACTOR george NAME George Formby DESCRIPTION "George Formby is here." SCRIPT 1. DESCRIPTION "George Formby is here cleaning windows." STEP ... SCRIPT 2. 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 1 FOR george. "All of a sudden, George starts to clean the windows." END EVENT.
This makes the descriptions of changes to be shown when it takes 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.
A slight problem with the feature that output is not visible unless the hero is present, is that a 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 has happened. The solution is to create an event that goes of 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. ...
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, as the reader/player does not need to know how the output is generated we can use a location and a row of events to substitute for the vehicle. Try the following complete example:
SYNONYMS car = ferrari. SYNTAX drive = drive. park = park. SYNTAX l = l. VERB l DOES LOOK. END VERB. LOCATION garage END LOCATION. LOCATION parking_lot NAME 'Large Parking Lot' END LOCATION. OBJECT car NAME little red sporty ferrari AT garage IS NOT running. HAS position 0. VERB enter DOES LOCATE hero AT inside_car. END VERB enter. END OBJECT car. LOCATION inside_car NAME 'Inside the Ferrari' DESCRIPTION "This sporty little red vehicle can really take you places..." EXIT out TO inside_car -- just a dummy, since we are -- going to change it below CHECK car IS NOT running ELSE "I think you should stop the car before getting out..." DOES IF position OF car = 0 THEN LOCATE hero AT garage. ELSIF position OF car = 1 THEN LOCATE hero AT parking_lot. --- Etc. END IF. END EXIT. VERB drive CHECK car IS NOT running ELSE "You are already driving it!" DOES "You start the car and drive off." MAKE car running. SCHEDULE drive1 AFTER 1. 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 drive1. CANCEL drive2. --- Etc. END VERB park. END LOCATION inside_car. EVENT drive1 "You drive out from your garage and approach a large parking lot." SET position OF car TO 1. LOCATE car AT parking_lot. SCHEDULE drive2 after 1. END EVENT drive1. EVENT drive2 "You drive out from the parking lot and approach your own garage." SET position OF car TO 0. LOCATE car AT garage. SCHEDULE drive1 after 1. END EVENT drive2. START AT garage.
The main idea is that the player/reader is inside the car, and the events are executed at this location thus emulating movement. It is possible to exchange the events for script steps and the car object for an actor. However as the car object is not where the hero is ('inside_car') the output from the scripts will not be shown. 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!
Sincere thanks go to Walt (sandsquish@aol.com) for inspiring communication that brought this example to life.
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.
ACTOR hero IS NOT quitting. END ACTOR 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 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.
Thanks to Tony O'Hagan (aoh@maths.nott.ac.uk) for this excellent idea.
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.
Examples of floating objects are the air, the ground and such semi-abstract objects. But sometimes you also need to make actual objects be floating objects such as parts of the heroes body.
To create floating objects you can use a particular feature of containers, namely the fact that they are always located where the hero is.
So to have the hero's body parts and the air and the sky to be available wherever the hero goes you can use:
CONTAINER body_parts END CONTAINER body_parts. CONTAINER outdoor_things END CONTAINER outdoor_things. OBJECT right_arm NAME right arm IN bocy_parts ... OBJECT head NAME head IN body_parts ... OBJECT sky IN outdoor_things ... OBJECT air IN outdoor_things ...
Of course you would not want the outdoor things to be available when you are indoors, but this can be fixed in a way similar to the container contents trick shown in Containers and Their Contents on page 66 . Simply create a container object and place it where the hero can never be:
OBJECT outdoor_things_storage AT limbo CONTAINER END OBJECT outdoor_things_storage. WHEN location IS outdoors => EMPTY outdoor_things_storage IN outdoor_things. WHEN location IS NOT outdoors => EMPTY outdoor_things IN outdoor_things_storage.
And Voila', 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...
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.
This puzzle can be implemented in Alan in a rather general way by using a default object attribute, a default location attribute and a few additions to the descriptions of the dark locations and the look verb.
Object Attributes lightsource 0. Location Attributes lit.
This will give all objects the value of 0 of the attribute lightsource . Any object that provide light should set this to something larger than zero. The attribute might of course change value dynamically, e.g. when the lamp is lit and extinguished. We can thus sum all the values of the attribute lightsource at a location and if the sum is above zero there is some light provided. So the look verb could be reworked to:
Verb 'look' Does If Sum Of lightsource Here = 0 And Location Is Not lit Then "You cannot see anything without any light." Else Look. End If. End Verb 'look'.
Of course we must also modify the dark locations:
Location indoors Is Not lit. Description If Sum Of lightsource Here > 0 Then "This is usually a very dark room. But in this light you can see..." Else "You can not see anything in the dark." End If. Exit out To outdoors. End Location. Location outdoors Description "Out here in the sun you can see everything." Exit 'in' To indoors. End Location.
So for every location which should be dark we must add the above test to the description clause.
There is however still a small problem with this solution. Objects available at the location are visible (described) as you enter the location. This must be taken care of, e.g. by moving all objects present to a limbo location (analogous to the container contents trick described in Containers and Their Contents ) in the dark part of the IF statement, and back in the ELSE clause.
Thanks goes to Thomas Ally (Thomas_Ally(at)freenet.richland) for prompting this solution.
A feature introduced in v2.7 made the following section almost obsolete. The new feature is the ability to refer to distant objects and actors (see Syntax Definitions on page 27 for a discussion on the omnipotent '!' indicator). I.e. the previous restriction that the player could only refer to objects and actors at the same location was removed. However there are instances where it may still be required to separate the handling of an object when it is present and when it is not, therefore this section gives a few examples of what can be done using some trickery with the mechanisms of Alan.
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.
For objects that should 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:
LOCATION hills : END LOCATION hills. OBJECT mountain AT hills : END OBJECT mountain. LOCATION scenic_vista NAME Scenic Vista END LOCATION scenic_vista. OBJECT shadow_mountain NAME distant mountain AT scenic_vista DESCRIPTION "Far in the distance you can see the Pebbly Mountain raising towards the sky." END OBJECT shadow_moutain.
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 ...
which would otherwise be impossible. If the mountain should be visible and manipulable from a number of locations, you might implement one shadow object for each location but this is a bit tedious if they are identical. One trick here is to use something like the following rule:
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 is sure to follow. However, as the rules are executed after the hero has moved, a better strategy might be to make the shadow_mountain `silent', i.e. to have no description. Instead the description of it 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 exits, like
LOCATION scenic_vista NAME Scenic Vista EXIT east TO hills DOES LOCATE shadow_mountain AT hills. END EXIT east. END LOCATION scenic_vista.
Objects that are always present, such as the air or the parts of the hero's body, may be treated like normal objects. I.e. they are defined as the objects they represent. They are then placed in a container that is not an object, which makes the objects always accessible, since containers (that are not objects) are considered to be where the hero is (cf. the inventory). This is also a simple way to create other compartments on the hero, such as a belt.
CONTAINER belt LIMIT count 2 ELSE "You can't fit more in your belt." END CONTAINER belt. VERB invent DOES LIST inventory. LIST belt. END VERB invent. CONTAINER pseudo END CONTAINER pseudo. OBJECT air IN pseudo VERB breathe : END VERB breathe. END OBJECT air.
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 include facility to structure your Alan source. One approach could be to place the description of each location in a separate file together with any objects that could be considered part of the scenery or at least is not only a tool in a puzzle. These files can then 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.
To simplify the development of adventures written in the Alan language, the interpreter Arun incorporates some features for debugging. There are a few debugging switches available when starting the interpreter:
-l Create a log of the player commands
-t Enable trace mode
-s Enable single instruction trace
-d Enable debug mode
For various purposes, such as debugging, an actual log of the player commands can be handy. Such a log is created if the option -l is given to the interpreter when starting a game. The log file is created in the directory which is current when the interpreter is started, the name of the log file will be the same as the game with the extension .log .
Trace mode can also act as an aid in debugging. It will print information about each invocation of the instruction interpreter, making it easier to see which parts of the code are being executed.
Single instruction trace will, in addition to the trace mode information, also trace every single Acode instruction.
Finally, debug mode will execute the start up sequence and then prompt for a debug command with
ABUG>
Abug may also be entered by typing the single command
> debug
during the execution of an Adventure that was compiled with the debug option.
A question mark or an `h' will give a brief listing of the commands available in Abug:
a Display a list of all actors. c Display a list of all containers. e Display a list of all events and their status. g Go on. I.e. proceed by executing the next turn. Abug will stop and prompt for a new command again before the player is next in turn. l Display a list of all locations. o Display a list of all objects. q Quit the adventure (and Abug). s Toggle single instruction trace. t Toggle trace mode (off and on). x Exit Abug, i.e. proceed without stopping.
The commands A , C , L and O 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.
The following is a short excerpt from a debugging session (user input in bold face):
<Arun, Adventure Interpreter version 2.6 alpha> <Version of 'saviour' is 2.6(0)a> Welcome to the game of SAVIOUR! [introductory text deleted for brevity] ABUG> s Step on. ABUG> t Trace on. ABUG> g > n <EXIT 1 (n) from 22 (Outside The Tall Building),Executing:> ++++++++++++++++++++++++++++++++++++++++++++++++++ dd9: PUSH 1 dda: SCORE 1 (5) ddb: RETURN -------------------------------------------------- <EXIT 1 (n) from 22 (Outside The Tall Building), Moving:> ++++++++++++++++++++++++++++++++++++++++++++++++++ de4: PUSH 4 de5: PUSH 6229 de6: PRINT 6229, 4 "Hall" de7: RETURN -------------------------------------------------- . ++++++++++++++++++++++++++++++++++++++++++++++++++ de8: PUSH 158 de9: PUSH 6235 dea: PRINT 6235, 158 "Inside the entrance is a hallway full of dust and pieces of the ceiling have fallen to the floor. At the west end is a staircase, and to the south is the exit." deb: PUSH 1 dec: DESCRIBE 1 ++++++++++++++++++++++++++++++++++++++++++++++++++ 620: PUSH 30 621: PUSH 1428 622: PRINT 1428, 30 " To the east is a folding door." 623: PUSH 6 624: PUSH 1 625: ATTRIBUTE 1, 6 (1) 626: IF TRUE 627: PUSH 13 628: PUSH 1446 629: PRINT 1446, 13 " It is closed." 62a: ELSE 62f: RETURN -------------------------------------------------- ded: RETURN -------------------------------------------------- ABUG> a ACTORS: 17: Hero ABUG> a 17 ACTOR 17 : Hero Location = 23 Hall Script = 0 Step = 0 Attributes = ABUG> o OBJECTS: 1: door 2: rats 3: spool of computer tape 4: old book 5: 3 metre long ladder 6: rather heavy computer terminal 7: small coin 8: birds nest 9: set of rusty keys 10: clock 11: drawer 12: desk 13: dirty manual 14: computer 15: vending machine 16: old mouldy candy bar ABUG> o 6 OBJECT 6 : rather heavy computer terminal Location = 30 Terminal Room Attributes = 1: 1 (takeable) 2: 1 (readable) 3: 0 (openable) 4: 0 (startable) 5: 1 (examinable) 6: 0 (connected) 7: 0 (showing_msg1) 8: 0 (showing_msg2) ABUG> q
Lines of '+' characters indicates the start of interpretation, thus they can be present inside other single step traces (like the DESCRIBE in the example above). Likewise lines of '-' indicates the return from one such level of interpretation.
Execution of an Adventure | Table of Contents | Adventure Construction |