MoveClass.h - a brief manual
                          by Neil James Brown
                          neil@highmount.demon.co.uk
                          (correct as of 24 January 98)

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

and also at the URL:
  http://www.highmount.demon.co.uk/moveclas.h

Follower.h can be found at the above two locations, with the filename
'follower.h'.
___________________________________________________________________________
| 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. Variables, routines and properties - a full list of what's provided
| 8. Troubleshooting - what to do if there are problems
| 9. 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' refer to the person
wishing to use the library file.


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. Moveclass supports this type of movement - see
section 5 for more details.

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. Moveclass is capable of calculating a
pathway between any two given locations, provided that a pathway exists
within the maximum depth search, and can either use the first discovered
pathway (for speed) or the shortest possible path.


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 (or 'include follower' if you intend to use the follower
module), and ensure that moveclas.h is in the standard library directory. The
search algorithm requires each room to have the class Room, which is defined
in the library. The next step is to give the NPC the moveclass. Eg:

...
Include "VerbLib";
Include "MoveClass"; ! may be "moveclas", depending on name of file

Object A_Room "A Room"
 class Room    ! - Important for the search algorithm. Defined by MoveClass.
  with description "A boring room.";

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

The library supports the use of 'followclass' by Andrew Clover and Gareth
Rees. 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 calculated path is being followed *
2 - no movement (the NPC is standing still)
3 - a specific pre-defined pathway is being followed *

* NOTE: values 1 and 3 are used by the library. Authors should not set
move_type to these values unless they are sure of what they are doing (eg
starting up a half-followed pathway after a pause of a couple of turns).

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 automatically inserted (by moveclass) at the beginning
and "to the <direction>." automatically 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. ** See Section 7
for details of GiveDir.

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>

The subject of NPCs opening and passing through doors is covered in section
6; however, care must be taken with the initial creation of these doors.
Particularly, in door_dir and door_to properties, it is important not to
check the value of 'location' (which is the player's current location) -
'parent(self)' should be used, otherwise NPCs will be unable to open any
doors that the player is not next to. In addition, a door should be *within*
a location. This may sound obvious, but it is easy to define a door with the
keyword 'object' (meaning that it has no parent) and rely on the library to
move the door around as appropriate, using information in the found_in
property. While this works okay for the player, it doesn't for NPCs, and
with Inform 6.05 and library 6/2 (and perhaps earlier versions too) the game
will become trapped in an endless loop the moment an NPC attempts to pass
through a door. With Inform 6.13 and library 6/7 (and probably other fairly
recent versions) this doesn't occur, but it is best to be on the safe side.
A typical door definition, then, would look something like the following:

Nearby my_door "my door"
  with name "my" "door"
       found in Room_A Room_B,
       door_dir
        [; if (parent(self)==RoomA) return e_to;
           return w_to;
        ],
       door_to
        [; if (parent(self)==RoomA) return RoomB;
           return RoomA;
        ],
       with_key my_key,
  has  door static openable;

Finally, in order to use the functions of MoveClass, the author will need to
start the NPC's daemon. It is recommended that this is done only when the
NPC is needed - so if the NPC doesn't appear until halfway through the game,
then StartDaemon(NPC) only then. If the NPC needs to be started up at the
beginning, include a StartDaemon(NPC) statement within the code's Initialise
property, eg:

[ Initialise;
  StartDaemon(Fred);
  StartDaemon(Dobbin);
  "^^Welcome to this game...^";
]; 


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,best)

To explain the parameters:

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

target_room - 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. (A
value of 1 is not yet catered for - this will eventually allow the NPC to
wait around for the pathway to become unblocked again.)

best - the value of this determines whether the best possible (ie shortest)
path is found or merely the first one the search algorithm comes across.
Searching for a pathway will produce a short delay during a game - the size
of the delay depends on the computer and interpreter, but generally no more
than a couple of seconds. Searching for and using the first available
pathway takes less processing time than searching for the best, and
therefore produces less of a delay during the game.

If a solution is found, 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
(especially in games with large numbers of locations), 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)' 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) and no_of_entries is the number of
entries in the array (above, it would be 7). The path size can be as big as
Inform will allow with regard to array sizes.

In addition to the main directions, the number 0 in a prepath array
instructs the NPC to stand still for one turn. Note that the property
after_action is NOT called in this instance, as the NPC hasn't moved.


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...>

A door can also be used as a barrier, invisible to the player, to prevent
NPCs (or certain NPCs) travelling in certain directions. For example, if the
player and all NPCs except for a very superstitious person are happy to
travel into a witch's cave, it might be worthwhile to create a door which the
player isn't aware of, that prevents the superstitious person from passing
through by putting in an npc_open property to that effect.


7. Variables, routines and properties
-------------------------------------

In summary, a brief list of the variables and properties that moveclass
defines, and the routines it provides:

Global variables:

- path_size_limit - a limit to the size of the depth search employed by the
routine NPC_path(..). This can be raised and lowered throughout the game to
suit, and only affects the initial search, not the actual movement by the
NPC. Initially set to 10, it can have any value between 2 and 32.

Attributes:

- explored }  Used by the search algorithm to keep track of the rooms it has
- blocked  }  already explored and found blocked.

Properties:

- npc_open - defined in anticipation of use by authors to allow their NPCs
access through doors. Must be a property of the door, and must return true
if the NPC is allowed to pass through (eg, if it is open, or they have the
correct security access), or false otherwise. Should also display a suitable
"The NPC opens the door" message where appropriate.

- before_action - to be used by authors in NPC objects in place of the
daemon property (which is unavailable, since it is used by moveclass). This
property, if it exists, is executed before movement (if any) takes place,
and is called each turn, even if the NPC does not move.

- after_action - if this property exists within an NPC object, then it is
called only after an NPC has moved successfully from one location to another,
and is useful for occasions where the NPC has to react instantly to objects,
events or other NPCs within the new room.

- walkoff - the message that will be displayed if an NPC moves out of the
location the player is currently at. The default setting is "walks off" - it
can be defined as a string (in which case moveclass automatically places the
name of the NPC at the front and "to the <direction>" at the end - see
details of GiveDir below) or a routine. If defined as a routine, moveclass
passes it one parameter - the direction the NPC has moved in. The author
should print an appropriate message, with one linespace before and one after
(eg "^The NPC walks off.", or print "^The NPC walks off.^").

- walkon - as walkoff, but this time it is the message when an NPC walks
into the player's current location. Can be defined as a string in the form
"walks into the room" (moveclass adds the NPC name at the beginning and a
full stop at the end) or a routine, which moveclass passes one parameter,
the direction the NPC has moved in.

- npc_dirs - a property array used by moveclass to store directions.

- npc_target - used by moveclass to store the target location for an NPC.

- npc_ifblocked - stores the action the author requested when calling
NPC_path, if the NPC finds that its pathway is blocked. Normally shouldn't
be altered by the author.

- npc_backtrack - used by the search algorithm to keep track of where the
search has got to, and to backtrack if a pathway is found to be blocked.

- npc_blocked - a routine that is called when the NPC finds its path
blocked. It shouldn't normally be overwritten by the author, unless a
special response is desired that isn't provided by the current routine.

- npc_arrived - called when an NPC arrives at its destination after
following either a pre-determined or a specially-calculated pathway. It is
recommended that the author overwrites the default routine (which merely
switches the NPC to random movement mode) - but the routine MUST either
change <npc>.move_type to values 0 (random) or 2 (stand still), or start off
the npc on another pathway. This routine must not allow the NPC to have a
move_type value of 1 or 3 if it isn't being started off on another path - if
this happens, it may cause a crash.

- npc_stage - keeps track of how far along a pathway the NPC is currently
at. Must not be altered by the author in any circumstances.

- move_type - determines how the NPC is to move, if at all. The author can
set this to either 0 - for random movement - or 2 - for no movement at all.
Values 1 and 3 are given by moveclass, and represent calculated and
pre-determined pathways respectively. The author may change move_type to 0
or 2 at any time (even part-way through a pathway) but must not set it to 1
or 3 - this may cause a crash.

- follow_action } Included with Moveclass in order to avoid a problem when
- follow_object } using Follower.h as well.

Routines

NPC_path(..) - see section 4 for details.

NPCPrePath(..) - see section 5 for details.

CleanRooms() - used by NPC_path to strip all rooms of the explored and
blocked attributes at the end of the search (whatever the outcome - success
or failure).

GiveDir(dir) - used by moveclass to insert "to the <dir>" (or a more
appropriate message for going in, out, up and down) at the end of the
walkoff string. May be of use to authors, if walkoff is being defined as a
routine.

Leadsto(dir, room) - used by the NPC_path routine to discover where a
pathway running off from a room leads to.

MoveNPC(npc,destination,-,-) - only defined if follower.h isn't being used.
Moveclass has been written to be aware of follower, and uses the MoveNPC
call when moving an NPC from one location to another. If follower hasn't
been included, then MoveNPC won't have been defined, and moveclass provides
a simpler version of the routine itself.


8. Troubleshooting
------------------

MoveClass has been tested with numerous example files and appears to be
sturdy. However, due to the complexity of the code, and the generally
random nature of NPC actions, it is virtually impossible to prove that it
will function in all circumstances.

To take this into account, MoveClass will produce debugging information of
its own if the DEBUG compiler directive is set and the trace level is set
to two or greater. This, in addition to the other debugging messages, may
help you to pinpoint the source of the problem. If it still appears to be
MoveClass that is at fault, then please contact me at the above email
address. If possible, include a transcript of the debugging information.

It should be noted that some older ports of the Zip interpreter (notably
!Zip for the Acorn range and WinZip for Windows 3.1x) contain errors that
cause problems with games compiled under Inform 6.1x and library 6/5+. In
particular, spurious crashes can occur when making extensive use of the
NPC_Path routine. If you are using an old Zip interpreter, then it is
recommended that you upgrade to a more up-to-date one (Frotz ports are
usually safe bets), or restrict yourself to Inform 6.05 and library 6/2.


9. 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. (This has been waiting for a long time to be coded up
  now. It is not a high priority, as you may have gathered. :)
* A new function to allow the player to make use of the search algorithm
  (eg "GO TO BANK" would find the bank location, plot a course and move the
  player there one room at a time).
* A simple step-by-step guide for using MoveClass, unlike this manual which
  apparently is difficult to understand. No, I don't understand it either,
  and I wrote it...
_____________________________________________________________________________

(Thanks to Martin Braun and Dave Gatewood for comments and suggestions
regarding this manual.)
 ___________________________________________________________________________
/                                                                           \
| 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.      |
\___________________________________________________________________________/