chapter 26 "Sentients"
	       Building Sentient Non-Player Characters
		   The Nightmare IV Object Library
		 written by Descartes of Borg 951127

One thing most everyone wants to see are monsters that react more
intelligently to user input.  The fact is, however, that most monsters
in the game only need a small, basic behaviour set.  Nevertheless, in
order to make an area interesting, there should be some monsters which
stand out as unique and purposeful.  The problem about building such
monsters is that they use a lot of processing time.

In order to make sure most monsters which do not need such
intelligence do not waste processing time on such activities, the
Nightmare Object Library separates non-player characters into two
classes: dumb monsters, which are basic mindless automata and
sentients, monsters which react more intelligently to their
environment.

This document describes sentients.  Before looking at this document,
it is highly recommended that you be familiar with the document
/doc/build/NPC which details non-player characters.  Sentients are
non-player characters, so everthing which applies to non-player
characters also applies to sentients.

				*****

Currently, a few basic behaviours distinguish sentients from normal
npcs.  Those behaviours are the ability to intelligently move about
the mud and to react to user speech.  Nightmare thus provides the
following functions to allow you to easily have an sentient enact
those behaviours:

	mapping SetTalkResponses(mapping mp);
	mixed AddTalkResponse(string str, mixed val);
	int RemoveTalkResponse(string str);

	mapping SetCommandResponses(mapping mp);
	mixed AddCommandResponse(string str, mixed val);
	int RemoveCommandResponse(string str);

	varargs int SetWander(int speed, string *path, int recurse);
	string *SetWanderPath(string *path);

	int SetWanderRecurse(int x);

	int SetWanderSpeed(int x);

				*****

Making NPCs react to user speech

You may want to have NPCs react to things players say.  To that end,
the following functions exist:

	mapping SetTalkResponses(mapping mp);
	mixed AddTalkResponse(string str, mixed val);
	int RemoveTalkResponse(string str);

Function: mapping SetTalkResponses(mapping mp)
Example: SetTalkResponses( ([ "square" : "The square is east of here.",
            "house" : "Isn't that an ugly house?" ]) );

This function allows you to set a list of responses to given phrases.
For example, if you put this code in a sentient and a player said
"Where is the square?" or "Your frog is certainly square.", your NPC
would have said "The square is east of here.".  Note therefore that
the NPC is only looking for the keys you place in there.  You could
have restricted it to "where is the square" instead of "square", but
then someone asking "Where's the square" would be missed.  

Also note that phrases should be in lower case.  It will match to
upper case words automatically.

Finally, you can either give a string or a function as the match to a
phrase.  If the match is a string, the NPC simply says the string in
the NPC's native tongue.  If, however, the match is a function, that
function will get called.

*****

Function: mixed AddTalkResponse(string str, mixed val);
Example: AddTalkResponse("in the house", (: HouseFunc :));

Matches an individual phrase to a string or function.  As with
SetTalkResponses(), if the match is a string, the NPC simply says the
string in response to the phrase.  If it is a function, that function
gets called.

*****

Function: int RemoveTalkResponse(string str);
Example: RemoveTalkResponse("house");

Removes the previous set or added talk response from the NPC.

				*****

Making NPCs react to user directives

Nightmare supports a special command, the "ask" command.  A player may
use the ask command to ask an NPC to perform a certain task.  For
example, "ask the healer to mend my right leg".  There is a special
event in NPC's which responds to this called eventAsk().  In order to
make responding to this easier, however, Nightmare has the
CommandResponse functions.  The command response functions allow NPC's
to respond based on commands, like "mend".  

*****

Function: mapping SetCommandResponses(mapping mp);
Example: SetCommandResponses( ([ "heal", "I cannot heal people" ]) );

Allows you to match commands to either strings or functions.  Matched
functions get called with the command as the first argument, and
command arguments as the second argument.  For example, if you had:
	SetCommandResponses("give", (: give :));
Your give() function would get called with "give" as the first
argument and "me the sword" as the second argument in response to a
player issuing the command "ask the monster to give me the sword".

*****

Function: mixed AddCommandResponse(string str, mixed val);
Example: AddCommandResponse("give", (: give :));

This allows you to add to the list of commands to which the NPC
responds.  The NPC responds to those commands as outlined for
SetCommandResponses().

*****

Function: int RemoveCommandResponse(string str);
Example: RemoveCommandResponse("give")

Removes a previously set command response.

				*****

Making NPCs move about the game intelligently

A sticky subject on most muds is that of wandering monsters.  When
done poorly, they can waste resources to a great degree.  Nightmare,
however, works to avoid wasting resources while getting the most out
of allowing monsters to move about.

Nightmare supports two types of wandering monsters: those which have
pre-determined paths and others which are true wanderers.  True
wanderers, those who simply randomly choose paths are subject to the
following restrictions:
	They may not move into rooms not yet loaded in memory.
	They will not try to open closed doors.
The first restriction is the most important to note.  This means that
the NPC will not wander into rooms that have not been recently visited
by some player.  This avoids the problem NPCs cause on many muds of
uselessly loading rooms that only the monster will ever see.

Monsters given specific paths to wander are not subject to the above
restrictions.  Of course, they cannot wander through closed doors.
But you can make part of their path to open a closed door.  In
addition, since such monsters have very specific sets of rooms into
which they can travel, they are not in danger of needlessly loading a
zillion rooms.

*****

Function: varargs int SetWander(int speed, string *path, int recurse);
Examples:
	SetWander(5);
	SetWander(5, ({ "go north", "open door", "enter hut", "go west" }));
	SetWander(5, ({ "go north", "open door", "enter hut", "go west",
                       "go south" }), 1);

This is the function you will almost always use in create() to make a
sentient wander.  Only one of the three possible arguments is
mandatory, that being the speed.  The speed is simply the number of
heart beats between attempts to move.  Thus, the higher the number,
the slower the movement of the monster.

The second argument, if given, is a list of commands which will be
executed in order by the monster.  If it is not given, the monster
will be assumed to be a true wanderer.  In other words, the first time
the monster tries to wander, the monster will "go north".  The second
time, he will "open door".  The third, he will "enter hut", etc.  

The third argument is either 1 or 0.  If 1, that means once the
monster has completed the path, it will use the first command in the
list the next time it tries to wander.  If 0, it will cease to issue
commands once it has cycled through the list.

You might note that between the time the above monster opens the door
and enters the hut, somebody could come along and shut the door.  How
can you deal with that?  You could do:
	SetWander(5, ({ "go north", ({ "open door", "enter hut" }) }));
You will notice here that the second member of the command array is
itself an array instead of a string.  In that case, all members of
that array get executed as part of that wander.  In this case it helps
make sure no one closes the door between when the monster tries to
open it and when it tries to pass through the door.

For even more flexibility, you can make elements of the array into
functions.  Instead of executing a command in a wander turn, the
function you provide instead gets called.  For example:
	SetWander(5, ({ "go north", (: kill_anyone :), "go south" }), 1);
Where the function kill_anyone() has the monster kill any players in
that room.  Thus, this monster sits in its room and occasionally pops
its head one room to the north to kill anyone sitting there.

*****

Function: string *SetWanderPath(string *path);
Example: SetWanderPath(({ "go north", "go south" }))

Allows you to set the monster's wander path independent of other
settings.  The wander path will never get executed, however, unless
the monster's wander speed is greater than 0.

*****

Function: int SetWanderRecurse(int x);
Example: SetWanderRecurse(1);

Allows you to make the monster's wander path recurse independent of
other settings.  This is meaningless, however, unless the monster's
wander speed is greater than 0 and a wander path is set for it.

*****

Function: int SetWanderSpeed(int x);
Example: SetWanderSpeed(5);

Allows you to set the monster's wander speed independent of other
settings.  This is NOT the same as SetWander(5).  SetWander() will
clear out any previous wander path and wander recurse settings.  This
function has no effect on the monster's wander path or wander recurse.

