MoveClass.h - a brief manual
                          by Neil James Brown
                          neil@highmount.demon.co.uk (correct as of 26/7/97)

MoveClass.h can be found at the ftp site ftp.gmd.de in the location:
  if-archive/infocom/compilers/inform6/library/contributions/moveclass.h

and also from the world-wide web page:
  www.highmount.demon.co.uk/ifpage.htm

___________________________________________________________________________
| INDEX:
|
| 1. Introdction - what MoveClass is all about.
| 2. NPC movement - a brief explanation of NPC movement in games
| 3. Setting everything up - using MoveClass in your game
| 4. The NPC_Path routine - finding a path from one location to another
| 5. The NPCPrePath routine - using a pre-determined pathway
| 6. NPCs opening doors - how to get NPCs to deal with closed/locked doors
| 7. Things still to do - future plans for the library file


1. Introduction
---------------

MoveClass is a library file designed to assist with the creation of
relatively sophisticated non-player characters (NPCs) in Inform-compiled
Interactive Fiction (IF) games. It provides a class and a main routine for
advanced NPC movement, allowing such things as random movement, specific
movement from one location to another, standing still, and switching between
these three states of movement with ease.

The aim of this library file is to shield the programmer from the complex
coding involved with NPC movement, and to improve efficiency by avoiding
duplication of movement code. A basic level of competence with Inform
programming is assumed, as the library file provides several access points.
The programmer may also need to provide a number of game-specific routines
for the library to work with, though the library will not assume
automatically that these have been provided, and will not crash any compiled
programs if they haven't.

For the rest of this manual, 'author' and 'programmer' refers to the person
wishing to use the library file (ie, you).


2. NPC movement
---------------

Non-random NPC movement in IF is usually achieved by a programmer by storing
the directions an NPC needs to take within an array, and then reading the
array one entry at a time, moving the NPC in the given direction. This
technique is fast and effective (see the movement of the various characters
in Christminster), but restricts the author to pre-determined pathways. That
is, the author has to decide where the NPC is going and which way it takes
while writing the game.

On occasion, the author may require a more dynamic approach, where the
pathway is decided during the game. For instance, there may be a dog running
around, which the player can summon at any time by blowing a dog whistle. Or
perhaps the crewmember of a space ship is performing random checks on the
hull integrity when the captain calls her back to the bridge based on
information you have just given the captain. Defining every possible way of
getting from any location to any location is impractical unless there are
very few locations in the game.


3. Setting everything up
------------------------

This library provides the class 'moveclass'; to enable an NPC to use the
functionality provided, include the library file after 'include verblib' in
your listing, and ensure that the file is in the standard library
directory. Then, give the NPC the class, eg:

...
Include "VerbLib";
Include "MoveClass";

Object A_Room "A Room"
  with description "A boring room.",
  has  light;

Nearby Christine "Christine"
  class moveclass
  with description ... <etc> ...

The library supports the use of 'followclass' by Andrew Clover and Gareth
Rees. At the time of writing, only the Inform 5.5 version exists on the ftp
site given above, but an updated version that works with Inform 6.13 is
available on the web page. This class, as the name suggests, allows the
player to follow any NPC. It must be included in the listing *before*
MoveClass, as MoveClass checks to see if FollowClass has been seen by the
compiler and acts accordingly.

The type of movement currently being used by the NPC is given by the
property 'move_type'; this is not normally altered by the author, and has
three values:

0 - random movement
1 - a specific path is being followed (NOTE: an NPC's move_type property
                                       must *not* be set to this value by
                                       the author - it is for library use
                                       only)
2 - no movement (the NPC is standing still)

The default given by the library is 0 - random movement - but the author
may wish to overwrite this by defining move_type as 2, to ensure that the
NPC does not start moving at first.

Other properties the author may wish to overwrite are:

walkon - default value is "walks into the room". This message is displayed
whenever an NPC arrives in a room the player is currently in. All the author
needs to know is that the NPC's name is automatically inserted at the
beginning and a full stop is inserted at the end, so possible alternatives
include "enters", "hops into the room excitedly" and "is suddenly here, to
your surprise". If, however, the author wishes to define several types of
these messages for different situations in the game, walkon can also be
defined as a routine, and is passed one parameter - the direction the NPC
moved to get to its current location. It may be an idea to create a routine
that returns the opposite direction to this parameter, to be used in the
message. There should be a new line before the message, and a new line
afterwards (a message in double quotes, which automatically produces a new
line at the end, will suffice).

walkoff - as above, but this time the message is given when the NPC departs
a room that the player is currently in. The default value is "walks off",
with the NPC's name inserted at the beginning and "to the <direction>."
inserted at the end. Alternatives may include "sulks off", "charges" and
"drifts away". As walkon above, this property can be defined as a routine,
and is passed one parameter, the direction the NPC is walking off to. It is
up to the author whether or not to use it. Similar conditions about printed
messages apply.

npc_arrived - this is a routine that is called when the NPC arrives at its
target location after following a specific pathway. The default routine sets
the move_type to 0 - in other words, as soon as the NPC arrives at the
location, it switches to random movement - but it is recommended that the
author overwrites this property within the game source code with something
more appropriate. Maybe it could increment the value of a number property
that represents what the NPC is currently doing, or what state it is in.

action_before - this routine is called every turn, no matter what the NPC is
doing at the time. Normally, the 'daemon' property is available for authors
to use when they wish a routine to be called within an object every turn,
but it isn't when the object in question has been given the class moveclass.
The property 'action_before' can be used as an alternative, and is called
before any movement. (Eg, just before an NPC leaves the room.)

action_after - this routine is only called (if it exists) after an NPC has
moved location. It may be useful for providing the NPC with a reaction to
the presence of another NPC, object or event the instant it arrives.

** IMPORTANT NOTE: MoveClass reads the <direction>_to properties (eg n_to,
s_to) of a room to see which directions are available and where they lead
to. It will normally expect to find the name of a room. If such a property
is defined as a routine, then MoveClass will run it and expect it either to
return the name of a location, or false (if it doesn't lead anywhere). This
might be necessary (for example) if you are coding a lift where going east
could lead to several different locations, or no location at all, depending
on what status the lift has. In this case, the routine should only contain
the conditional statements, and no other actions or print statements, eg:

  n_to
   [; if (self.number==1) return Top_Room;
      return Bottom_Room;
   ],
    <etc>

These <direction>_to properties should not contain strings or any other type
of routine. If you wish to do this, then capture the 'go' command in the
room's 'before' property and act on the direction. Eg, if you wish to have
something like:

  e_to "Rocks block your way.",
  w_to [; if (self hasnt general)
          { give self general;
            move footprint to location;
          }
          return West_Location;
       ],
   <etc>

... then implement this in the following way:

  before
   [; Go: if (noun==e_obj) "Rocks block your way.";
          if (noun==w_obj)
          { if (self hasnt general)
            { give self general;
              move footprint to location;
            }
          }
   ],
  w_to West_Location,
   <etc>


4. The NPC_path routine
-----------------------

Once everything has been set up, it should now be possible to get an NPC to
travel to a specific target location (within limits). In order to do this,
use the call:

  NPC_path(npc_in_question,target_room,what_to_do_if_blocked)

To explain the parameters:

npc_in_question - the NPC you wish to send to the new location.

target_toom - the target room.

what_to_do_if_blocked - currently this can only be set to either 0 or 2, and
indicates what action the code should take if the NPC should find the path
blocked for some reason along the way (eg a door that was unlocked when it
calculated the path is now suddenly locked). A value of 0 means that the NPC
should just give up and start wandering around randomly, while a value of 2
will prompt the code to search for an alternative route to the target. If no
alternative route exists, the NPC will start wandering around randomly.

The routine NPC_path may find a solution very quickly or may take a little
time as it searches the locations. The time taken depends a little on the
speed of the interpreter, but games using the library were tested on various
interpreters and took no more than a second on any of them.

If a solution is found at all, the routine returns true, otherwise it
returns false. It is recommended that the game code reads the value returned
and acts appropriately on it.

Limiting the search depth
- - - - - - - - - - - - -

By default, the algorithm will only search to a depth of ten rooms. If the
target room is not found at maximum stretch, the algorithm will pull back
and try an alternative route (if there is one). So, if the shortest path to
a target room is greater than ten moves in length, it won't be found. Ten
rooms may not seem large but it can result in a lot of processing, and a
short but noticable delay. It works better where there is a high level of
interconnection between rooms.

This limit can be raised or lowered, if desired, by setting the global
variable 'path_size_limit' to any value between 2 and 32. (It is preferable
to set this value in the 'initial' routine of your code.) Please do not
attempt to set it to any other value, as this may cause the compiled game to
crash. A value of 32 may well result in an extremely noticable delay, and is
likely to find an inefficient solution. Experimentation is the best way of
finding a good value.


5. The NPCPrePath routine
-------------------------

To set an NPC off on a pre-determined pathway, first of all you need to
define that pathway using a global array. In the array, you must use the
names of the compass directions as defined in the 'english.h' library file
(eg n_obj, se_obj, u_obj, out_obj). For example:

Array MovingMyNPC -> n_obj n_obj w_obj u_obj in_obj se_obj d_obj;

The above array gives the pathway as north, north, west, up, in, southeast
and down.

When you wish to set the NPC in question off on that pathway, use the call
'NPCPrePath(NPC_name,name_of_array,no_of_entries,target_room)' where
NPC_name is the name of the NPC, name_of_array is the name of the array (in
the above example, this would be MovingMyNPC), no_of_entries is the number
of entries in the array (above, it would be 7), and target_room is the room
the NPC should end up in. The size of this path should not exceed the value
of path_size_limit, otherwise the routine will generate an error.


6. NPCs opening doors
---------------------

This library file takes doors into account, and can be easily programmed to
deal with closed or locked doors. Open doors are not a problem, but when an
NPC comes across a closed or locked door, it checks to see if the door
provides a 'npc_open' property, and if so, calls it, passing it the NPC
identifier.

The author, if s/he wishes to make it possible for an NPC to open a door,
should provide that door with this 'npc_open' property, and should accept
the single parameter. The aim of this routine is to either ensure that the
door is open and return true, or return false to signify that the door
cannot be opened in these circumstances. The library provides messages for
movement, but 'npc_open' routines should provide messages saying that the
door has been unlocked and/or opened. Remember that linespacing should be
consistent with the rest of the game when providing the messages. A simple
npc_open routine may look like the following:

<...Definition of door object omitted...>
     npc_open
      [ npc;  ! - parameter passed from library
        give the_door open;
        if (npc in location) print_ret (The) npc, " opens the old oak \
         door."; ! - prints a message if the player is there to see action
        rtrue; ! - returns true otherwise.
      ],
<...rest of code...>

A slightly more complex npc_open, taken from an actual game, is given below:

<...initial door code...>
       npc_open
        [ i; 
           if (self has open) rtrue;
           give self open;
           StartTimer(self,1); ! (Timer closes door again after one turn)
           if (i in location) "^", (The) i, " presses the door release, \
            and ", (the) self, " slides open.";
           if (self in location) "^", (The) self, " slides open.";
           rtrue;
        ],
<...more door code...>


7. Things still to do
---------------------

The library file is still in development and it is hoped that the following
functions will be written for the next release:

* Implementation of the action 'wait until door is opened' in response to a
  'blocked' event.
_____________________________________________________________________________

Did you find this manual easy to understand or was it too complicated? Has
it omitted anything important? Was there something you wanted to know about
MoveClass that this manual doesn't cover? Please contact me at the e-mail
address at the top of this document if you have any comments.

                                                        Neil Brown
                                                        26 July 1997