Roger Firth's IF pages

Home

Inform 6: Frequently Asked Questions

Email
Back up

Into
the Intro

Setting
the scene

Preparing
to program

Learning
the lingo

Dabbling
in data

Operating
on objects

Verbal
versatility

Bothered
by bugs

History and
hereafter

Worldly
woes

Inside
information

Tips and
techniques

These advanced topics related to Inform's model world:

How can I get rid of those damn walls?
    How to LOOK [TO THE] NORTH
How can I embed object details in a room's description?
Is it possible to disable TAKE ALL?
Can I avoid printing "(which is empty)" after a container?
Can I avoid printing "(the objectname)" after certain commands?
How does the list-maker work?
    Inventory style examples
    Overriding the prevailing style of listing
Could you explain what "in scope" means?
    Extending the standard scope
What's the easiest way to shine light everywhere?
Why is water so difficult to model?
    Example: Water and Bucket classes, FILL, POUR and EMPTY
How does everybody know where the north is?
    An alternative approach: GO AHEAD, TURN RIGHT, etc

How can I get rid of those damn walls?

THIS TOPIC HAS BEEN SUPERSEDED BY LIBRARY 6/11

There are two problems in this area: EXAMINE NORTH replies "You see nothing special about the north wall." even for external locations, and EXAMINE WALL prompts "Which wall do you mean, the north wall, the south wall ... ?". This code, though slightly messy to implement, fixes those problems and, as a bonus, also supports LOOK [TO THE] NORTH using David Cornelson's CompassLook enhancement.

  1. Unavoidably, you need to edit English.h. Right at the start of that file, move the line IFNDEF WITHOUT_DIRECTIONS; up slightly, and its matching ENDIF; down slightly, so that they enclose the entire definition and use of CompassDirection (this will have no effect on the vast majority of games which don't define WITHOUT_DIRECTIONS):
  2.   IFNDEF WITHOUT_DIRECTIONS;
      Class  CompassDirection
        with article "the", number 0
        has  scenery;
      Object Compass "compass" has concealed;
    
      CompassDirection -> n_obj "north wall"
                          with name 'n//' 'north' 'wall',    door_dir n_to;
      ...
      CompassDirection -> in_obj "inside"
                          with                               door_dir in_to;
      ENDIF;
  3. At the start of your game, define WITHOUT_DIRECTIONS:
  4.   Constant Story "MYGAME1";
      Constant Headline "^My first Inform game.^";
      Constant WITHOUT_DIRECTIONS;
  5. Between the Includes of Parser.h and VerbLib.h, place these lines:
  6.   Include "Parser";
    
      !------------------------------------------------------------------------------!
      Class   CompassDirection
        with  number 0,
              description [;
                  if (location provides compasslook && location.compasslook(self)) rtrue;
                  print "You see nothing special ";
                  if (self ~= u_obj or d_obj) print "to the ";
                  print_ret (name) self, ".";
                  ],
              compasslook 0,  ! use it somewhere just to satisfy the compiler
        has   scenery;
    
      Object  Compass "compass" has concealed;
    
      CompassDirection -> n_obj "north"      with door_dir n_to,
                          name 'n//' 'north';
      CompassDirection -> s_obj "south"      with door_dir s_to,
                          name 's//' 'south';
      CompassDirection -> e_obj "east"       with door_dir e_to,
                          name 'e//' 'east';
      CompassDirection -> w_obj "west"       with door_dir w_to,
                          name 'w//' 'west';
      CompassDirection -> ne_obj "northeast" with door_dir ne_to,
                          name 'ne' 'northeast';
      CompassDirection -> nw_obj "northwest" with door_dir nw_to,
                          name 'nw' 'northwest';
      CompassDirection -> se_obj "southeast" with door_dir se_to,
                          name 'se' 'southeast';
      CompassDirection -> sw_obj "southwest" with door_dir sw_to,
                          name 'sw' 'southwest';
      CompassDirection -> u_obj "above"      with door_dir u_to,
                          name 'u//' 'up' 'above' 'ceiling' 'roof' 'sky';
      CompassDirection -> d_obj "below"      with door_dir d_to,
                          name 'd//' 'down' 'below' 'floor' 'ground';
      CompassDirection -> out_obj "outside"  with door_dir out_to;
      CompassDirection -> in_obj "inside"    with door_dir in_to;
      !------------------------------------------------------------------------------!
    
      Include "VerbLib";
  7. After the Include of Grammar.h, place these lines:
  8.   Include "Grammar";
    
      Extend 'look'
              * noun=ADirection      -> Examine
              * 'to' noun=ADirection -> Examine;
  9. In any Room With A View, optionally include a compasslook property:
  10.   Room    study "Your study"
        with  description "There is a doorway to the east of this austere room.",
              compasslook [ obj;
                  if (obj == u_obj or d_obj) rfalse;
                  if (obj == e_obj) "You see a doorway.";
                  "You see the wall.";
                  ],
              e_to hallway;

How can I embed object details in a room's description?

Normally, Inform prints a room's description, and then lists the objects currently in that room:

  The farmyard
  A nondescript area between the house to the west and
  a gaping wooden barn to the east.

  You can see a turkey here.

  >

It isn't really surprising to find a turkey in a farmyard, so having it listed in a separate paragraph like this has the effect of unnaturally drawing the player's attention to it -- "Aha! If a turkey is called out, it must be important!". Now perhaps the turkey is important, but you'd prefer the player to work that out for herself. What you'd like to present is something like this, where the turkey is only mentioned casually as part of the general scene:

  The farmyard
  A nondescript area between the house to the west and
  a gaping wooden barn to the east. A turkey scratches
  in the dust.

  >

More information in the DM:
§14 §26

One way of achieving this is to use the describe property. Here's a fragment of the game:

  Room    farmyard "The farmyard"
    with  description [;
              print "A nondescript area between the house to the west
                  and a gaping wooden barn to the east.";
              if (turkey in self) print " A turkey scratches in the dust.";
              new_line;
              ],
          e_to barn,
          w_to farmers_house;

  Room    barn "Wooden barn"
    with  description [;
              print "The farmyard opens to the west of this cluttered old building.";
              if (turkey in self) print " A turkey pecks at scattered straw.";
              new_line;
              ],
          w_to farmyard;

  Room    farmers_house "Farmer's house"
    with  description "You're in a house.",
          e_to farmyard;

  Object  turkey "turkey" farmyard
    with  name 'turkey',
          description "Nice and plump -- Thanksgiving must be near.",
          describe [;
              if (self in farmyard or barn) rtrue;
              "^A turkey hops around.";
              ];

In rooms where you want the turkey to blend in (such as the Farmyard and Barn), do two things:

  1. append an appropriate throwaway turkey reference to the room's description if the turkey is actually present (don't forget the leading space).
  2. include that room in the turkey's describe property in the list of locations which simply rtrue (which causes the turkey not to be listed separately).

In other rooms (such as the Farmer's house) do nothing; if the turkey should stray there, it will be listed separately as usual.

Is it possible to disable TAKE ALL?

Many authors dislike the Library's built-in support for TAKE ALL, for a couple of reasons: it generates foolish messages by attempting to take scenery and static objects, and it reveals objects whose presence the author may not wish to make readily apparent.

Processing of ALL is handled by the grammar tokens multi, multiheld, multiexcept and multiinside. One way of disabling ALL is to re-write the relevant routines in Parser.h, but this would be a mammoth task, far far too complex to describe here. Another way is to change the Library's grammar definitions -- in Grammar.h or by using Extend...replace directives -- to change the multi tokens to the simpler noun and held. However, this is not a good idea, because it would remove also the ability to list nouns explicitly: TAKE BELL, BOOK AND CANDLE would have to become TAKE BELL. TAKE BOOK. TAKE CANDLE.

So here's a third way. The ChooseObjects() entry point allows you to influence which objects are included by an ALL. It's easy enough to specify that for the action ##Take (triggered by, for example: GET ALL, PICK UP ALL, REMOVE ALL, TAKE ALL), the ALL simply doesn't match anything, though for other actions it works as before. Here's the code:

More information in the DM:
§33

For a more sophisticated solution, look at takeall.inf from Kory Heath and Lucian Smith

  [ ChooseObjects obj code
          retval;
          obj = obj;          ! Avoid a compiler warning
          switch (code) {
              0:              ! Parser is excluding obj from ALL
                  ;           ! ... accept parser's decision
              1:              ! Parser is including obj in ALL
                  if (action_to_be == ##Take) retval = 2;
                              ! ... force exclusion if TAKE; accept otherwise
              2:              ! Parser is asking for 'appropriateness' hint
                  ;           ! ... but we don't provide one
              }
          return retval;
          ];

This works fine, though the fact that TAKE ALL now generates the standard message "There are none at all available!" irrespective of how many objects are in scope is awkward and misleading. So let's fix that, by defining a LibraryMessages object:

More information in the DM:
§25

  Include "Parser";

  Object  LibraryMessages
    with  before [;
              Miscellany:
                  if (lm_n == 44 && action_to_be == ##Take)
                      "You can't use ALL in this context.";
              ];

  Include "VerbLib";

where we intercept Miscellany message 44 -- "There are none at all available!" -- and, for a ##Take action, substitute a more appropriate replacement.

Can I avoid printing "(which is empty)" after a container?

THIS TOPIC HAS BEEN SUPERSEDED BY LIBRARY 6/11

When Inform is listing the objects in a room -- "You can see a rusty axe and some blood here." -- it provides additional information about containers and supporters. For example, you might encounter "You can see a fish tank (in which is a brick (on which is a bottle (which is empty))) here.", not perhaps the ideal form of presentation.

It turns out that none of the standard object properties will change those parenthetical clauses. invent ought to -- "This routine is for changing an object's inventory listing" -- and it does indeed provide exactly the desired control when displaying the player's possessions, but it has no effect when listing the objects in a room. While this is not necessarily a bug, you may well wish to alter the default behaviour; here's one way.

Unfortunately, the fix requires a change to the standard list-maker WriteListFrom(); this routine creates all object lists printed by the library, and its behaviour is controlled by a series of 'style' bits. During an player inventory, the library calls WriteListFrom() with the FULLINV_BIT style setting, while the call to list a room's contents includes the PARTINV_BIT style setting. The problem is that the code which deals with FULLINV_BIT takes note of an object's invent property (if it has one), while the code for PARTINV_BIT does not.

To make the PARTINV_BIT code recognise an optional invent property, small modifications are needed to two library routines: WriteBeforeEntry() and WriteAfterEntry(). Rather than edit verblibm.h directly -- messing with the library is always a somewhat traumatic activity -- you can use my complete ready-to-run replacements for the two routines. Simply download Roger Firth's WriteList.h from the Archive, and #Include it in your game between Parser.h and VerbLib.h. That's all.

If an object has an invent property, it will be invoked in the usual way (with inventory_stage first set to 1, and then set to 2) both when mentioning that object in a room description, and when listing it in the player's inventory. By default you'll get the same output each time. If you need to distinguish between the two occasions, you can test (c_style&PARTINV_BIT) -- true during a room description -- or (c_style&FULLINV_BIT) -- true during an inventory. Here's an example:

More information in the DM:
§26 §27

  Include "Parser";
  Include "WriteList";
  Include "VerbLib";

  Object  -> "sack"
    with  name 'sack',
          invent [;
              ! When listing objects in the player's inventory
              if (c_style&FULLINV_BIT) rfalse;

              ! When listing objects at the end of a room description
              if (inventory_stage == 1) switch (children(self)) {
                  0: print "an empty sack";
                  1: print "a sack containing ", (a) child(self);
                  default: print "an assortment of objects in a sack";
                  }
              rtrue;
              ],
    has   container open;

Can I avoid printing "(the objectname)" after certain commands?

One feature of the Inform parser is its ability -- in the right circumstances -- to infer which object the player intended. For example:

  >INV
  You are carrying:
    a red ball
    a blue ball

  >DROP
  What do you want to drop?

  >BALL
  Which do you mean, the red ball or the blue ball?

  >RED
  Dropped.

  >DROP
  (the blue ball)
  Dropped.

In first part of this example, the parser handles the ambiguity of which ball to drop. In the second part, when there is no longer any ambiguity, the parser makes an inference -- that the blue ball must be the intended object -- and drops it without further prompting. But not without telling you what it's doing; hence you see "(the blue ball)" displayed.

Mostly, this is helpful, or at worst harmless. Just occasionally, it seems unnecessary and intrusive (for instance, in the discussion on water below -- on such occasions, you might well wish to turn the feature off):

  >POUR WATER INTO BARREL
  (the water into the barrel)
  You pour the water into the barrel.

Sadly, there's no easy way. The heart of the parser, defined in parserm.h, is an 800-line routine called Parser__parse(). Three-quarters of the way through, this is the code responsible for the inference message:

  if (inferfrom ~= 0) {
      print "("; PrintCommand(inferfrom); print ")^";
  }

where inferfrom is a global variable set (mainly) when another library routine -- NounDomain() -- has determined (a) that the player hasn't specified an object unambiguously, and (b) that this doesn't matter, because it can infer what must have been intended.

So, your options are to edit parserm.h and comment-out those three lines, to Replace Parser__parse() with your own variant which omits the lines... or to live with the situation. I recommend this last course of action.

How does the list-maker work?

More information in the DM:
§27

The Library includes a routine for displaying lists of objects, used primarily by LOOK (to present the visible objects in the current room) and by INVENTORY (to present the player's possessions). The routine is described in the DM4, but the information on how best to employ it is frustratingly brief: "the best way is to experiment" is Graham's none-too-helpful advice. The routine is this:

  WriteListFrom(object, style, depth);

where object is the first object to be listed, usually child(location) or child(player), style is a magic number defining the form of the required listing, and depth is an undocumented value, usually 0 but sometimes 1 (or higher?). It's the business of getting the style number right -- by combining control bits -- which is the tricky part. There are thirteen defined bits, capable of being combined in over 8000 ways; fortunately, the vast majority of the combinations don't do anything distinctive, so that we can reduce the problem to something manageable. I can identify only eleven basic styles, arbitrarily labelled A-K, which we'll explain by example. Here's an 'interesting' collection of objects:

  Object  "tray" selfobj          has supporter;
    Object  -> -> "bottle"        has container;
      Object  -> -> -> "wine"     with article "some";
    Object  -> -> "glass"         has container open;
      Object  -> -> -> "drop of water";
    Object  -> -> "dish"          has supporter;
      Object  -> -> -> "tablet";

  Object  "box" selfobj           has container openable open;
    Object  -> -> "bell";
    Object  -> -> "book"          has container openable lockable locked;
    Object  -> -> "candle"        has light;

and here's how WriteListFrom() describes them:

Style

style bits

Result

A

ENGLISH_BIT

a tray and a box

B

ENGLISH_BIT+RECURSE_BIT

a tray, on top of which are a bottle, a glass, inside which is a drop of water and a dish, on top of which is a tablet and a box, inside which are a bell, a book and a candle

C

ENGLISH_BIT+RECURSE_BIT+
PARTINV_BIT

a tray, on top of which are a bottle (which is closed), a glass, inside which is a drop of water and a dish, on top of which is a tablet and a box, inside which are a bell, a book (which is closed) and a candle

D

ENGLISH_BIT+RECURSE_BIT+
PARTINV_BIT+TERSE_BIT

a tray (on which are a bottle (which is closed), a glass (in which is a drop of water) and a dish (on which is a tablet)) and a box (in which are a bell, a book (which is closed) and a candle)

E

ENGLISH_BIT+RECURSE_BIT+
FULLINV_BIT

a tray, on top of which are a bottle, a glass, inside which is a drop of water and a dish, on top of which is a tablet and a box (which is open), inside which are a bell, a book (which is closed and locked) and a candle (providing light)

F

ENGLISH_BIT+RECURSE_BIT+
FULLINV_BIT+TERSE_BIT

a tray (on which are a bottle, a glass (in which is a drop of water) and a dish (on which is a tablet)) and a box (which is open) (in which are a bell, a book (which is closed and locked) and a candle (providing light))

G

NEWLINE_BIT

a tray
a box

H

NEWLINE_BIT+RECURSE_BIT

a tray
a bottle
a glass
a drop of water
a dish
a tablet
a box
a bell
a book
a candle

I

NEWLINE_BIT+RECURSE_BIT+
INDENT_BIT

  a tray
    a bottle
    a glass
      a drop of water
    a dish
      a tablet
  a box
    a bell
    a book
    a candle

J

NEWLINE_BIT+RECURSE_BIT+
INDENT_BIT+PARTINV_BIT

  a tray
    a bottle (which is closed)
    a glass
      a drop of water
    a dish
      a tablet
  a box
    a bell
    a book (which is closed)
    a candle

K

NEWLINE_BIT+RECURSE_BIT+
INDENT_BIT+FULLINV_BIT

  a tray
    a bottle
    a glass
      a drop of water
    a dish
      a tablet
  a box (which is open)
    a bell
    a book (which is closed and locked)
    a candle (providing light)

Notes

Occasionally you may wish to override the prevailing style of listing, for example by having a 'tall' inventory display the contents of one item on a single line. You can create your own list-writer to do this, by using an object's invent property. However, you can only reliably use WriteListFrom() within an invent routine if you obtain the specially-modified version of WriteListFrom() which is included in WriteList.h (described in the previous topic).

For example, to simplify the presentation of the box -- by mentioning only the immediate child objects inside it while ignoring any lower-level children, and also by omitting descriptive comments like "(providing light)" -- you could try this:

  Object  "box" selfobj
    with  invent [;
              if (inventory_stage == 2 && child(self)) {
                  print " (in which";
                  WriteListFrom(child(self), ENGLISH_BIT+ISARE_BIT);
                  print ")";
                  rtrue;
              }
          ];
    has   container openable open;

This enhancement takes effect in any style which uses PARTINV_BIT or FULLINV_BIT (and so affects room descriptions as well as inventories); for example style C now produces "a tray, on top of which are a bottle (which is closed), a glass, inside which is a drop of water and a dish, on top of which is a tablet and a box (in which are a bell, a book and a candle)" and style K gives:

  a tray
    a bottle
    a glass
      a drop of water
    a dish
      a tablet
  a box (in which are a bell, a book and a candle)

Incidentally, here's a tiny routine which walks the object tree, starting from a given object, in exactly the same way as WriteListFrom(). For each object obj that RunRoutineFrom() finds, it calls R(obj):

  [ RunRoutineFrom o R
      obj;
      objectloop (obj from o) {
          R(obj);
          if (child(obj)) RunRoutineFrom(child(obj), R);
      }
  ];

These two code fragments produce the same output:

  [ PrintIt obj; print_ret (a) obj; ];
  RunRoutineFrom(myObj, PrintIt);

  WriteListFrom(myObj, NEWLINE_BIT+ALWAYS_BIT);

Could you explain what "in scope" means?

Scope is one of those topics which can be as easy or as complex as you care to make it. Let's try to make it easy.

More information in the DM:
§32

Put simply, what's "in scope" -- roughly, the things you can EXAMINE -- is represented by the contents of the room you're currently in: yourself, your possessions, and any other objects in that particular room. By convention, even in an external location the surrounging walls are opaque, preventing you from seeing objects in the adjacent rooms.

Almost all of the time, Inform takes care of scope by itself. Providing that you've set your object attributes correctly, you'll see fixed objects and portable objects, supporters and any objects on them, containers and any objects in them (but only if the container is open), cats and dogs and NPCs. Further, you can usually TOUCH, TASTE, TAKE any object (unless it's locked inside a glass case), and you need only some very simple before properties to deal with the obvious exceptions: objects which are in the distance, repulsive, ludicrously heavy, and so on. The point is: the library does an excellent job of managing scope, so that you usually get the desired effect with very little effort.

Just occasionally, though, you need to extend the standard scope, by enabling the player to mention an object which isn't clearly lying around in the room. There are two main techniques available: you can hide the requisite object inside the room by some means, or you can tell the interpreter to look for it somewhere else entirely.

Smuggling extra objects into the room. We're now assuming that you want the player to be able to refer to objects even though they're not listed as part of the room's contents. Your options include:

Looking for objects elsewhere. The second possibility is rather less common than the first, but it's the best approach to one specific problem. The grammars for most verbs which handle objects are similar to this example:

More information in the DM:
§31 §32

  Verb 'open' 'unwrap' 'uncover' 'undo'
      * noun              -> Open
      * noun 'with' held  -> Unlock;

The noun token matches any object in scope (usually, in the room); the held token matches any object carried by the player. That makes sense: you can't expect to OPEN THE COFFIN unless it's there in front of you. However, what if you want to ASK UNDERTAKER ABOUT COFFIN? It seems a reasonable question, and one that the undertaker should be able to answer, even though the object itself isn't currently visible. The standard grammar here is:

  Verb 'ask'
    * creature 'about' topic    -> Ask;

but the topic token is matched by any text at all, leaving you with the messy task of parsing it for meaningful content. An better approach is to let the Inform parser do the hard work for you. You need four items:

  1. A set of 'objects', not physically present in the game, whose only role is to define the words you can ask about in their name properties:
  2.   Object  AskTopics "topics for ASK";
      Object  -> t_coffin "coffin" with name 'coffin' 'box' 'casket';
      Object  -> t_grave  "grave" with name 'grave' 'hole';
      Object  -> t_stone  "gravestone" with name 'gravestone' 'stone' monument';
      etc...
     
  3. A routine to look for those words:
  4.   [ AskTopicsScope;
          switch (scope_stage) {
           1: rfalse;
           2: ScopeWithin(AskTopics); rtrue;
           3: "At the moment, even the simplest questions are confusing.";
          }
      ];
     
  5. A new grammar which calls that routine:
  6.   Extend 'ask' replace
          * creature 'about' scope=AskTopicsScope   -> Ask;
     
  7. And lastly, an NPC life property to supply the answer which matches the 'object' parsed into the second variable:
  8.   Object  undertaker "undertaker"
        with  ...
              life [;
                Ask: switch (second) {
                  t_coffin: "~Best Brazilian mahogany -- it'll last a lifetime.~";
                  t_grave:  "~Six feet deep, and not an inch less.~";
                  t_stone:  "~We can offer you granite, slate or marble.~";
                  ...
                  default:  "~I'm so sorry; I can't help you with that.~";
                }
              ],
        has   animate;

For more information on conversation with NPCs, see Roger Firth's InFact pages

The reason we're presenting this here is because of that scope= token, which uses a routine -- written by you, but almost always of the form shown -- to temporarily redefine the current scope. ScopeWithin() is a library routine which places the contents of the specified object in scope, and here it's used to bring the conversational topic objects into scope, and so enable the conversation to refer to as many subjects as you care to define.

Checking on scope. An object sometimes needs to check for itself whether it's in scope. Typically this is within a daemon or timer; for example, if the player removes a supporting column, he may have two or three turns to leave the room before the ceiling falls in and -- if he's still there -- kills him. Here are three simple tests:

Remembering a lost object. Finally, an exploration of techniques for dealing with an out-of-scope object whose previous presence the player might well recollect. Consider this example:

  > DRINK WATER
  You drink the water, and it's all gone.

  > DRINK WATER
  You can't see any such thing.

True enough, and easy to program. But what if you're after this effect?

  > DRINK WATER
  You drink the water, and it's all gone.

  > DRINK WATER
  What water?

Here are some different ways of achieving this.

  Object  -> water "water"
    with  name 'water' 'liquid',
          article "some",
          description "Very cold and very wet ",
          before [;
            Drink:
              move fake_water to parent(self); remove self;
              "You drink the water, and it's all gone.";
          ];

  Object  fake_water "water"
    with  name 'water' 'liquid',
          article "some",
          react_before [;
              if (self == noun or second) "What water?";
          ],
    has   scenery;

These first four solutions all use some slight trickery in order to keep a fake object with the name 'water' in scope, even after the real water has been consumed. That way, the command DRINK WATER will continue to be accepted by the parser; it's the fake water's job to react to all commands with the "What water?" message.

In this example, the first DRINK WATER command removes the real water object and replaces it with a fake equivalent. The fake then rejects all subsequent commands to DRINK, EXAMINE, or do anything at all with the missing water.

  Object  -> water "water"
    with  name 'water' 'liquid',
          article "some",
          description "Very cold and very wet ",
          before [;
            Drink:
              give self scenery;
              "You drink the water, and it's all gone.";
          ],
          react_before [;
              if (self has scenery && self == noun or second)
                  "What water?";
          ],
    has   ~scenery;

This example is very similar, but rather than replacing one object by another, it uses the scenery attribute to transform the real water into fake.

One advantage of this approach is that pronouns still work properly; for example, you can use these three commands successfully, because IT still refers to the water after it's been consumed:

  EXAMINE WATER
  DRINK IT
  DRINK IT

  Object  -> water "water"
    with  name 'water' 'liquid',
          article "some",
          description "Very cold and very wet ",
          before [;
            Drink:
              remove self; MoveFloatingObjects();
              "You drink the water, and it's all gone.";
          ];

  Object  fake_water "water"
    with  name 'water' 'liquid',
          article "some",
          react_before [;
              if (self == noun or second) "What water?";
          ],
          found_in [;
              if (TestScope(water) == false) rtrue;
          ],
    has   scenery;

The previous two solutions work only in a single location; move to another room, and your quest for water reverts to the standard "You can't see any such thing.". Here's a way of using the found_in property so that you'll obtain the specialized "What water?" response anywhere.

  Object  -> water "water"
    with  name 'water' 'liquid',
          article "some",
          description "Very cold and very wet ",
          before [;
            Drink:
              remove self;
              "You drink the water, and it's all gone.";
          ];

  Object  fake_water "water"
    with  name 'water' 'liquid',
          article "some",
          react_before [;
              if (self == noun or second) "What water?";
          ],
    has   scenery;

  Object  mySelfobj "(self object)"
    with  short_name [; return L__M(##Miscellany, 18); ],
          description [; return L__M(##Miscellany, 19); ],
          before NULL, after NULL, life NULL, each_turn NULL,
          time_out NULL, describe NULL,
          capacity 100, parse_name 0,
          orders 0, number 0,
          add_to_scope [;
              if (parent(water) == nothing)
                  AddToScope(fake_water);
          ],
    has   concealed animate proper transparent;

This example achieves the same thing by a different technique; it uses an add_to_scope property on the player object, so that the fake water is brought into scope as the player moves around.

Unfortunately, you can't just assign a new add_to_scope property value to selfobj, because the standard Library object doesn't provide an initial value for you to overwrite. Instead, you must define your own version of selfobj -- based on the Library object -- with the addition of an appropriate add_to_scope property. Also, you must add this line to your Initialise() routine:

  player = mySelfobj;

  Object  -> water "water"
    with  name 'water' 'liquid',
          article "some",
          description "Very cold and very wet ",
          before [;
            Drink:
              remove self;
              "You drink the water, and it's all gone.";
          ];

  [ ParseWater n;
      while (NextWord() == 'water' or 'liquid') n++;
      if (n == 0) return GPR_FAIL;
      wn--;
      return GPR_PREPOSITION;
  ];

  [ WhatWaterSub; "What water?"; ];

  Extend 'drink'
      * ParseWater -> WhatWater;

To finish with, here are two quite different lines of attack. In this example we define a 'general parsing routine' (DM4 §31) specifically to look for 'water' and 'liquid', and we also append a line to the DRINK verb's grammar. The effect is that, if DRINK WATER isn't matched by a real water object, it'll still match this new grammar line, and thus trigger the WhatWater action to produce the required message.

  Object  -> water "water"
    with  name 'water' 'liquid',
          article "some",
          description "Very cold and very wet ",
          before [;
            Drink:
              remove self;
              "You drink the water, and it's all gone.";
          ];

  [ ParserError error_type
      x;
      for (wn=1,x=0 : wn<=parse->1 : wn++)
          if (WordInProperty(parse-->(2*wn-1), water, name)) x++;
      if ((error_type == CANTSEE_PE && x) ||
          (error_type == ITGONE_PE  && PronounValue('it') == water))
          "What water?";
      rfalse;
  ];

This example steps in at the last possible moment, just as the parser is about to report "You can't see any such thing." and, if the parse error relates to (what is probably) the missing water, displays our customized response instead.

What's the easiest way to shine light everywhere?

More information in the DM:
§19

One of the first things you learn, when starting out to design an Inform game, is that your rooms will be pitch dark unless a source of light is available. That's what the light attribute does for you, and it's most commonly applied to each room:

  Object  hallway "Dingy hall"
    with  description "Steps lead down into darkness.",
          e_to front_door,
          w_to kitchen,
          d_to cellar,
    has   light;

  Object  kitchen "Small kitchen"
          ...
    has   light;

However, (with luck) you'll quickly realize that a Room class simplifies things a little:

More information in the FAQ topic:
What does class inheritance do for me?

  Class    Room
    with  description "A bare and featureless room.",
    has   light;

  Room    hallway "Dingy hall"
    with  description "Steps lead down into darkness.",
          e_to front_door,
          w_to kitchen,
          d_to cellar;

  Room    kitchen "Small kitchen"
          ...;

That approach is both easy and flexible: your rooms will now be illuminated unless you say otherwise. If you don't need that flexibility -- if darkness isn't a factor anywhere in your game -- then you can make things even simpler. Here are three ways:

Why is water so difficult to model?

More information in the DM:
§50

Given how commonly they occur in real life, the implementation in a game of water-like liquids is a remarkable challenge. TAKE is difficult (you need a suitable container, and what happens to any existing contents?), as is DROP (what happens to the spillage?). Worse, a liquid can be divided and combined indefinitely, and mixed with other liquids. Or solids. If there's a bit of it, you could DRINK it; if there's a lot of it, SWIM becomes a possibility; and so on.

See Jim Fisher's ORliquid.h and Emily Short's amazingly comprehensive (and very large) WaterElement.h in the Archive

There are a couple of excellent library extensions in the Archive to deal with liquids, but for a game where a bucket of water has only an insignificant walk-on role, you might fancy something rather simpler. So here are a couple of object classes to act as a starting point. You get support for FILL and EMPTY, and for pouring the contents of one container into another. You don't get support for specific quantities of liquid -- a container is either empty or full -- nor for mixing liquids, nor for anything else beyond the basics. And even so, there's quite a lot of code.

More information in the DM:
§3.11

First, the Water class can be used for the liquid object itself. It enables you to create and delete instances of the class at run-time, redirects several possible actions to the liquid's container, and includes an infinite property to distinguish between finite (the contents of a bottle or glass) and infinite (a lake, a fountain) amounts:

  Class   Water(2)
    with  name 'water',
          short_name "water",
          article "some",
          infinite false,     ! is this an inexhaustible source of liquid?
          before [;
            Drink:
              "A small sip refreshes you.";
            Take:
              "[Use FILL and the name of a suitable container.]";
            Insert,Transfer:
              if (parent(self) ofclass Bucket) <<EmptyT (parent(self)) second>>;
              if (second ofclass Bucket)       <<Fill second>>;
              "I'm not sure that's a suitable container.";
            Drop,Empty,EmptyT:
              if (parent(self) ofclass Bucket) <<(action) (parent(self)) second>>;
          ],
          react_before [;
            Fill:
              if (noun ofclass Bucket) rfalse;
              "I'm not sure that's a suitable container.";
          ];

The Water class is heavily inter-dependent on the Bucket class, which defines an object capable of containing the liquid; it doesn't have to be a bucket -- a glass, jar, bottle or barrel would all be appropriate. Most of the work is handling the actions of Fill, Empty and EmptyT (EMPTY x INTO y) for Water objects, ensuring that Water isn't mixed with other objects:

  Class   Bucket
    with  before [ x y; x = child(self);
            Empty:
              if (~~x ofclass Water) rfalse;      ! follow standard action
              Water.destroy(x);
              print_ret (The) x, " pours onto the ground and disappears.";
            EmptyT:
              if (~~x ofclass Water) rfalse;      ! follow standard action
              if (second ofclass Bucket) {
                  y = child(second);
                  if (~~y) {                      ! second is empty
                      move x to second;
                      "You pour ", (the) x, " into ", (the) second, ".";
                  }
                  if (y ofclass Water)
                      print_ret (The) second, " is already full of ", (name) y, ".";
                  else
                      print_ret (The) second, " has other stuff in it.";
              }
              else {
                  if (second == d_obj) <<Empty self>>;
                  "I'm not sure that's a suitable container.";
              }
            Fill:
              if (~~x) {                          ! is there a water source in scope?
                  objectloop (x ofclass Water && x.infinite && TestScope(x)) {
                      x = Water.create(); move x to self;
                      "You fill ", (the) self, " with ", (name) x, ".";
                  }
                  rfalse;                         ! no water available
              }
              if (x ofclass Water)
                  print_ret (The) self, " is already full of ", (name) x, ".";
              else
                  print_ret (The) self, " has other stuff in it.";
            Receive:
              if (x ofclass Water)
                  print_ret (The) self, " is already full of ", (name) x, ".";
          ],
    has   container open;

More information in the DM:
§33

If you FILL a Bucket from a source of Water, there are two Water objects in the room. A reference to 'WATER' seems to favour the infinite source rather than the finite amount in the Bucket, which feels the wrong way round. So we can define a ChooseObjects() routine to rectify this:

  [ ChooseObjects obj code;
      if (code == 2 && obj ofclass Water)
          if (obj.infinite) return 1; else return 2;
  ];

Finally, here are a couple of useful extensions to the standard grammar:

  Verb 'pour' = 'empty';

  [ Wet; if (noun ofclass Water) rtrue; rfalse; ];

  Extend 'fill'
      * noun 'with' noun=Wet      -> Fill
      * noun 'at'/'from' noun     -> EmptyT reverse;

The nice thing about defining object classes, however complex, is that the actual objects themselves are pretty simple. Here's a bubbling fountain, from which you can fill a jar with water:

  Object  -> fountain "fountain"
    with  name 'little' 'stone' 'fountain' 'pool',
          description
              "Water gushes from the little stone fountain
               and collects in the pool beneath.",
          before [;
            EmptyT:
              if (second ofclass Bucket) <<Fill second>>;
              "I'm not sure that's a suitable container.";
          ],
    has   static transparent;

  Water   -> -> "water"
    with  infinite true;

  Bucket  -> "jar"
    with  name 'jar' 'pot';

  Bucket  -> "barrel"
    with  name 'barrel' 'cask';

And this is what you get for your money:

  >LOOK

  A valley in the mountains
  The Alpine vista is glorious.

  You can see a fountain and a barrel (which is empty) here.

  >EXAMINE FOUNTAIN
  Water gushes from the little stone fountain and collects in the pool beneath.

  >FILL JAR
  You fill the jar with water.

  >INV
  You are carrying:
    a jar
      some water

  >POUR WATER INTO BARREL
  (the water into the barrel)
  You pour the water into the barrel.

  >LOOK

  A valley in the mountains
  The Alpine vista is glorious.

  You can see a fountain and a barrel (in which is some water) here.

  >EMPTY BARREL
  The water pours onto the ground and disappears.

How does everybody know where the north is?

More information in the DM:
§9

Once in a while there comes a player or designer complaining that the use of cardinal directions in a game seems awkward and absurd. Awkward, because it doesn't read very elegantly in room descriptions to have a list of available exits which mention "north, south and southwest" -- it may break the mood of the narration, especially if you are indoors. Absurd, because games assume that players have an innate and accurate sense of direction, a compass inside their brains, which lets them assess that, for example, the banana lies to the northeast, even when they are prisoners in an underground cave. Wouldn't it be more logical to use relative directions -- "to your left", "ahead of you", and so on -- and be done with?

Many discussions arise from the "Compass versus Relative directions" debate. Not so long ago, Mike Roberts, designer of the excellent Text Adventure Development System, decided to make a TADS test game, Rat In Control, which allowed players to switch between both systems; the idea was to see if the conventional Compass Directions system was in fact clearer and easier to use, and if players would form confused perceptions of the map when using relative directions. You may check the announcement and further discussion of the experiment here.

Disregarding the question of use from a player's perspective, there are some design issues which makes the implementation of relative directions a difficult task. Let's try to define the problem and its ramifications.

Imagine our player standing in a square room, looking to the north. We need to describe the new directions, so a start would be "ahead of you", "to your right", "to your left" and "behind you". However, things begin to sound a bit more clumsy with diagonal directions: "diagonally ahead of you and to your right", or "slightly to your left", or "over your right shoulder" or (heaven forbid) "round to your left and a bit more". You can even complicate things further by adding the third dimension: "above, diagonally ahead of you and to your right" (or perhaps just "two o'clock high"), but let's presume for the sake of simplicity that your game doesn't need those.

So our player is in a square room facing north, which, for the time being, has become "ahead of you". When we use compass directions, our movement verbs echo the direction we want to go: if we type EAST, the player moves in that direction and arrives at a new location; end of the problem. With relative directions, however, you have to take in consideration which way the player is facing, because if you type AHEAD or some such, it will get you to a different place if you're facing to the north or if you're facing to the south. Two problems arise from this:

And then, of course, you still have the little problem of room descriptions. Consider the following paragraph:

  The Library
  Bookshelves cover all the walls in this magnificent room. There's
  an arch to your left leading into the living room, and double oak
  doors ahead of you connect with your private office.

So far, so good. But now, suppose that we enter our private office, which proves to be a dead end, and then return to the library. Unless we always force the player to face to the north (which is really a bit silly), we now find that the arch is "to the right" and the double oak doors "behind you". So your descriptions need to implement some way of catering for these variations.

The following code is a (simplified and crude) example which implements just four relative directions (ahead, right, behind and left), without removing the existing compass directions. You could easily extend it to cover diagonal directions; the system wouldn't change, but you'd be acquiring the malady known as "combinatorial explosion".

  ! Four common properties to add to the new CompassDirection objects:

  Property ahead_to;
  Property right_to;
  Property back_to;
  Property left_to;

  Global position; ! in which direction is the PC looking?
                   ! 0 = north; 1 = east; 2 = south; 3 = west;
                   ! There are no diagonal directions in this example.

  Global op;       ! Absolute position of a room or a fixed object
                   ! in relation to the (adjacent) location where the PC is.
                   ! It doesn't matter how the PC moves, the "office" (e.g.)
                   ! is always to the north of the "library".

  ! We now need some new Compass Direction objects:

  CompassDirection ahead_obj "ahead" Compass
                   with door_dir ahead_to, name 'straight' 'ahead' 'forward' 'f//' 'a//';

  CompassDirection right_obj "right" Compass
                   with door_dir right_to, name 'right' 'r//';

  CompassDirection back_obj "behind" Compass
                   with door_dir back_to, name 'back' 'backwards' 'behind' 'b//';

  CompassDirection left_obj "left" Compass
                   with door_dir left_to, name 'left' 'le' 'lf' 'lft';

  ! The Room class implements the relative directions system.
  ! Caveat: you cannot use the s_to "can't go" syntax.
  ! You must use a 'before' property and trap Go actions.

  Array cdir --> n_obj e_obj s_obj w_obj;

  Class   Room
    with  description "Under Construction.",
          before [;
            Go:
              if (self provides n_to && noun == n_obj) position = 0;
              if (self provides e_to && noun == e_obj) position = 1;
              if (self provides s_to && noun == s_obj) position = 2;
              if (self provides w_to && noun == w_obj) position = 3;
          ],
          ahead_to [; <<Go (cdir-->(position))>>; ],
          right_to [; <<Go (cdir-->((position+1)%4))>>; ],
          back_to  [; <<Go (cdir-->((position+2)%4))>>; ],
          left_to  [; <<Go (cdir-->((position+3)%4))>>; ],
    has   light;

  ! Define a printing rule for our changing descriptions:

  Array rdir --> "ahead of you" "to your right" "behind you" "to your left";

  [ dir obj;
      if (location provides n_to && obj == location.n_to) op = 0;
      if (location provides e_to && obj == location.e_to) op = 1;
      if (location provides s_to && obj == location.s_to) op = 2;
      if (location provides w_to && obj == location.w_to) op = 3;

      if (position == 0 or 2) print (string) rdir-->((position+op)%4);
      if (position == 1 or 3) print (string) rdir-->((position+op+2)%4);
  ];

  ! In Initialise, we must define a starting value for the position variable:

  [ Initialise;
      location = entrance;
      position = 0;
      ...
  ];

  ! And now some grammar:

  Array cdir --> "north" "east" "south" "west";

  [ TurnRightSub;
      print "You make a quarter turn right. You are now facing to the ";
      position = (position + 1)%4;
      print_ret (string) cdir-->position, ".";
  ];

  [ TurnAroundSub;
      print "You turn around. You are now facing to the ";
      position = (position + 2)%4;
      print_ret (string) cdir-->position, ".";
  ];

  [ TurnLeftSub;
      print "You make a quarter turn left. You are now facing to the ";
      position = (position + 3)%4;
      print_ret (string) cdir-->position, ".";
  ];

  Extend 'go'
      * 'to' noun=ADirection  -> Go;

  Extend 'turn' first
      * 'right'               -> TurnRight
      * 'around'              -> TurnAround
      * 'left'                -> TurnLeft;

Now, every time you write a room description, all you need to do is use the new printing rule when you describe the positions of adjacent rooms. Supposing there are two rooms named living_room and office respectively, you could write:

  Room    library "The Library"
    with  description
              "Bookshelves cover all the walls in this magnificent room.
               There's an arch ", (dir) living_room,
              " leading into the living room, and double oak doors ", (dir) office,
              " connect with your private office.",
          ...

As you see, the implementation of relative directions is no trivial task, and it's probable that players will find it a bit confusing at run-time. So our advice is that you stick with the conventional Compass directions, which are clean and straightforward, even if they seem a bit awkward and absurd at times.

If you don't want to use the words "north", "south", etc, in your room descriptions, you may add a little compass object to your game's UI which informs the player of every available exit from the current room. This way, you won't need to clutter your descriptions with mood-breaking lines explaining where the player can go to in practical terms, since there's a fixed spot on the screen where this information can be found.

Into
the Intro

Setting
the scene

Preparing
to program

Learning
the lingo

Dabbling
in data

Operating
on objects

Verbal
versatility

Bothered
by bugs

History and
hereafter

Worldly
woes

Inside
information

Tips and
techniques